なんとなく気になって・・。
調べたかったユースケースとしては、
- バイナリを保存したい
- それを一意なキーで取得し、そのままレスポンスしたい
- 整合性はどっちでもよい
KVでもR2でもそれができるけど、どっちでやるか?っていう。 頻度としてはKVが良さそうと直感的に思うけど、R2のほうが安いからな〜っていう下心を添えて。
対戦者の紹介
まずはKV。
- KVS
- 結果整合性(最大60s待ち)
- 最大25MiBまで
await KV.get(key, "stream")
でバイナリを返せる- R2よりコストが高い
つぎにR2。
https://developers.cloudflare.com/r2/api/workers/workers-api-reference/
- S3互換BLOBストレージ
- 強整合性
- 最大5TiBまで
(await R2.get(key)).body
でバイナリを返せる- KVよりコストが安い
どちらも.get(key)
からバイナリが取得できる。
ファイルサイズが25MiBを超えるならR2しか選択肢はないけど、そうではなく、コストも問題にならないなら、KVでもよい。
コスト優先でR2を選ぶ場合、そのパフォーマンスがどれほどKVと差が出るのかが気になるところ。その特性からして、KVより遅いとは想定できるけど、それが数値としてどれほどか?っていうのを調べたい。
コード
事前の条件として、KV、R2ともに1件だけバイナリファイルをアップロード済とする。
let now;
let resp;
now = performance.now();
const kvList = await env.TEST_KV.list();
console.log("kvList: %s ms", performance.now() - now);
now = performance.now();
const r2List = await env.TEST_R2.list();
console.log("r2List: %s ms", performance.now() - now);
now = performance.now();
const kvFile = await env.TEST_KV.get("foo", "stream");
console.log("kvGet: %s ms", performance.now() - now);
resp = new Response(kvFile);
now = performance.now();
const r2File = await env.TEST_R2.get("foo");
console.log("r2Get: %s ms", performance.now() - now);
resp = new Response(r2File.body);
このコードをwrangler dev --remote
して、それぞれログに出た値を眺めてみる。
結果
雑に10回ほどcurl http://localhost:8787
してみた結果。
連打してみたり、ちょっと放置してから叩いてみたり。
kvList: 191 ms
r2List: 113 ms
kvGet: 879 ms
r2Get: 151 ms
kvList: 12 ms
r2List: 103 ms
kvGet: 14 ms
r2Get: 148 ms
kvList: 12 ms
r2List: 117 ms
kvGet: 13 ms
r2Get: 127 ms
kvList: 16 ms
r2List: 105 ms
kvGet: 133 ms
r2Get: 129 ms
kvList: 15 ms
r2List: 105 ms
kvGet: 21 ms
r2Get: 153 ms
kvList: 14 ms
r2List: 106 ms
kvGet: 17 ms
r2Get: 123 ms
kvList: 12 ms
r2List: 113 ms
kvGet: 12 ms
r2Get: 289 ms
kvList: 14 ms
r2List: 105 ms
kvGet: 16 ms
r2Get: 121 ms
kvList: 186 ms
r2List: 110 ms
kvGet: 888 ms
r2Get: 137 ms
kvList: 15 ms
r2List: 99 ms
kvGet: 17 ms
r2Get: 123 ms
という感じだった。
- 初回、しばらく放置した後は、コールドスタートみたいな挙動がある
- 以降は安定する(そうしてもらわんと困る)
- 基本的には、KVのほうが圧倒的に速い
というわけで、R2はその中身を最速で取得する方法ですらこのパフォーマンスなので、await (await R2.get(key)).json()
みたくR2ObjectBody
のメソッドを使うとなると、もっと遅くなるはず。
この数値をどう捉えるかはその人次第ではあるが、コスト優先でR2をKVSとして使うのは、パフォーマンスの劣化を覚悟の上でという感想。