🏀
DBのパフォーマンスを最大限に出すシンプルな方法(コネクション数とレイテンシー)
この記事は個人の検証ベースとなりますので参考程度にしていただければと思います。
環境: 16コア、Go 1.23, PostgreSQL 16
きっかけは以下のスライドです。面白いのでぜひ読んでみてください。
都市伝説バスターズ「WebアプリのボトルネックはDBだから言語の性能は関係ない」 - Kaigi on Rails 2024
その記事の性能測定の例
まずは自分が利用する環境のデータベースのレーテンシーを調べます。
例えば以下のクエリです。元のスライドのページのスクショ
curl とかで叩いて何回かログに出して見ます。Go では time.Since などで測定可能です。
{"queryTime":"126.421µs","queryResultTime":"137.252µs","":"User1"}
{"queryTime":"140.378µs","queryResultTime":"145.919µs","":"User1"}
{"queryTime":"127.253µs","queryResultTime":"131.421µs","":"User1"}
{"queryTime":"150.497µs","queryResultTime":"158.522µs","":"User1"}
{"queryTime":"172.66µs","queryResultTime":"181.667µs","":"User1"}
{"queryTime":"100.281µs","queryResultTime":"103.798µs","":"User1"}
{"queryTime":"129.316µs","queryResultTime":"133.013µs","":"User1"}
{"queryTime":"104.98µs","queryResultTime":"108.267µs","":"User1"}
{"queryTime":"130.429µs","queryResultTime":"133.886µs","":"User1"}
{"queryTime":"129.407µs","queryResultTime":"133.495µs","":"User1"}
{"queryTime":"125.439µs","queryResultTime":"129.357µs","":"User1"}
一番早いクエリは約 100µs ですね。100µs は 0.1 ms ですので RPS でいうと1 つのコネクションで秒間約1万(10000 RPS) になります。 コネクション 10 で 10 万, 20 で 20万 RPS ということとなります。実際どこまで出るかは CPU 次第です。自分の環境ではコネクション20で最大 260K RPS でした。
8 threads and 30000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.15ms 837.24us 51.16ms 80.01%
Req/Sec 88.26k 10.65k 111.54k 75.84%
Latency Distribution
50% 819.00us
75% 1.31ms
90% 2.38ms
99% 2.77ms
3926018 requests in 15.09s, 198.44MB read
Requests/sec: 260096.55
Transfer/sec: 13.15MB
今度は 50ms かかるクエリはどうでしょう?
こちらも元の記事の検証コードを参考にします。0.05s ⇒ 50ms
PostgreSQL では sleep ではなく、pg_sleep
になるのでそちらを使います。
50ms だと1コネクションで秒間 20 RPS しかでませんね。
>>> 1000 / 50
20.0
コネクション 20 で 20x20 = 400 です。実際もそのような数字ですね。
8 threads and 30000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 997.95ms 563.57ms 1.95s 58.97%
Req/Sec 97.01 51.76 400.00 88.52%
Latency Distribution
50% 1.00s
75% 1.50s
90% 1.80s
99% 1.95s
5927 requests in 15.09s, 306.77KB read
Socket errors: connect 0, read 0, write 0, timeout 5147
Requests/sec: 392.66
Transfer/sec: 20.32KB
10万 RPS 出したい場合はどうしましょう(CPUが耐えられる場合)
>>> 100000 / 20
5000.0
コネクション約 5000 必要です。
測定の結果約 94000 RPS です。
8 threads and 30000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 51.28ms 12.83ms 449.39ms 98.98%
Req/Sec 13.73k 4.48k 17.89k 84.41%
Latency Distribution
50% 50.22ms
75% 50.32ms
90% 50.84ms
99% 64.26ms
1419640 requests in 15.06s, 71.76MB read
Requests/sec: 94247.31
Transfer/sec: 4.76MB
CPUを見るとまだ余裕があります。
コネクション数を 6000 に上げます。今度はCPUを最大限利用してくれました。
10万 RPS達成です。
8 threads and 30000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 58.05ms 6.03ms 120.35ms 56.26%
Req/Sec 12.95k 5.86k 23.45k 58.61%
Latency Distribution
50% 59.52ms
75% 62.38ms
90% 64.68ms
99% 71.03ms
1534466 requests in 15.04s, 77.56MB read
Requests/sec: 102000.61
Transfer/sec: 5.16MB
実際 50ms は遅いので 5ms の場合はどうでしょう。
1コネクションで 1000/5 = 200 RPS、20万 RPS目指してみます。
200*1000 = 20万、コネクション 1000必要となります。
8 threads and 30000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 6.84ms 1.84ms 112.65ms 93.51%
Req/Sec 32.09k 11.88k 50.32k 58.72%
Latency Distribution
50% 6.56ms
75% 7.32ms
90% 8.23ms
99% 11.02ms
1903478 requests in 15.08s, 96.21MB read
Requests/sec: 126265.77
Transfer/sec: 6.38MB
126K RPS。20万 RPS はでませんでしたね。CPU は最大に使っているのでこれ以上できることはあまりありません。
以上、意見やコメント大歓迎です。
Discussion