に
プライマリキーにUUIDを採用した場合のINSERT時間のペナルティ
っていう話があったので、へーって思って見てみた。対象の MySQL はただ docker run mysql:8.0
したヤツです。
string が主キーで、PK があるものと無いもの、UUID v4 と v1 とで比べれば良かろう。(v1 は値が偏るので)
create table bar(id varchar(255) not null, primary key(id)); create table baz(id varchar(255) not null, primary key(id)); create table qux(id varchar(255) not null);
INSERT_SQL = <<~SQL INSERT INTO %s(id) values (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?) SQL COUNT_NUM = 50_000; # 回 *100 行の INSERT def execute(table_name, &block) COUNT_NUM.times do |i| puts "#{table_name}\t#{i}\t#{Time.now.to_i}" if i % 1000 == 0 # 10万行 INSERT ごとに時間を出力 db.xquery(INSERT_SQL % table_name, *100.times.map{ block.call }) end end uuid = UUID.new execute("bar") { SecureRandom.uuid } # UUID v4 execute("baz") { uuid.generate } # UUID v1 execute("qux") { SecureRandom.uuid } # UUID v4 PK なし
100 行ずつ 5 万回 INSERT、1000 INSERT (=10 万行)ごとに時間を取得しました。
結果をグラフにするとこう。横軸が総 INSERT 行数、縦軸は 10 万行の INSERT にかかった秒数。
へー。たしかに 100 万レコードを過ぎた辺りから徐々に遅くなってるのが分かる。 もうちょっと右側 (数千万レコード級) でどうなるかも見てみたい……。
UUID 文字列を使うのが間違い (例えば UUID_TO_BIN()
使えよとか) という話もなくはないし、1 行あたりだと 1ms 切るので数千万程度なら大して気にはしないと思うけど、とりあえず。
自分では snowflake 系に慣れきっちゃってるので、コレを見てもなんともだなぁ。
おもしろかった。