Sashimi開発記録 - キャッシュ

キャッシュの導入について考えたこと、試したことなどメモ。

キャッシュ機構の導入

CMSの場合、主なI/Oは

  • RDBMSへのアクセス
  • 画像やjs/cssなどアセットの取得

の二つ。後者はnginxなどのフロントエンドサーバで対処すべきことと思われるのでSashimiでは扱わない。

RDMBSへのアクセスでは、ブログという特性上

  • 少数の人気記事にアクセスが集中する(Zipf's law)
  • データサイズは小さい(テキストデータ)

という感じだと思われる。

サイズが小さいのでRedisなどの外部キャッシュサービス(KVS)を使用するメリットはあまり無いと思われる。RDBMSにはそもそもキャッシュ機構が備わっている(クエリキャッシュとか)ので、Redisを使用した場合とRDBMS任せにした場合とで、性能に大差は無いだろうと予測。

というわけで試してみた。今回テストしたハードウェアはノートパソコン(Lenovo E450)なので、以前示したデータほどのパフォーマンスは出ていない。また、計測ツールにwrkを使用していたが、今回の環境上だとなぜかwrkが終了しないのでabコマンドでの測定に切り替えている。

Maria クエリキャッシュ

パラメータは一切チューニングしていない。デフォルトのまま。結果は5196.66 reqs/sec

Server Software:        sashimi
Server Hostname:        localhost
Server Port:            9001

Document Path:          /about
Document Length:        6662 bytes

Concurrency Level:      12
Time taken for tests:   1.924 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      67790000 bytes
HTML transferred:       66620000 bytes
Requests per second:    5196.66 [#/sec] (mean)
Time per request:       2.309 [ms] (mean)
Time per request:       0.192 [ms] (mean, across all concurrent requests)
Transfer rate:          34402.48 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       4
Processing:     0    2   1.1      2      11
Waiting:        0    2   1.0      2      10
Total:          0    2   1.1      2      11

Percentage of the requests served within a certain time (ms)
  50%      2
  66%      2
  75%      3
  80%      3
  90%      3
  95%      4
  98%      6
  99%      7
 100%     11 (longest request)

Redis

同一マシン上でRedisサーバを1台立ててキャッシュした場合。4257 reqs/sec. スコアは悪化している。

Server Software:        sashimi
Server Hostname:        localhost
Server Port:            9001

Document Path:          /about
Document Length:        6662 bytes

Concurrency Level:      12
Time taken for tests:   2.349 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      67790000 bytes
HTML transferred:       66620000 bytes
Requests per second:    4257.39 [#/sec] (mean)
Time per request:       2.819 [ms] (mean)
Time per request:       0.235 [ms] (mean, across all concurrent requests)
Transfer rate:          28184.43 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       5
Processing:     0    3   1.5      2      19
Waiting:        0    2   1.2      2      17
Total:          0    3   1.5      3      19

Percentage of the requests served within a certain time (ms)
  50%      3
  66%      3
  75%      3
  80%      4
  90%      4
  95%      5
  98%      7
  99%      9
 100%     19 (longest request)

さらなる高速化

この性能でもおそらく殆どのブログ運用で十分な性能であると思われるが、sashimiの開発方針は最速のCMSなのでもうちょっと頑張ってみる。

MySQLであれRedisであれ、ネットワークI/Oは発生するのであとはアプリ内で何とかするしかない。とりあえず一番シンプルにJavaのConcurrentHashMapを使用してみた。ScalaにはTrieMapというスレッドセーフなMapがあるらしいが、パフォーマンスはConcurrentHashMapのほうが優れているみたいだ。TrieMapの利点は任意の時点で正確なイテレータを取得することができることらしい。

結果は8929 reqs/secと大幅に高速化することができた。

Server Software:        sashimi
Server Hostname:        localhost
Server Port:            9001

Document Path:          /about
Document Length:        6662 bytes

Concurrency Level:      12
Time taken for tests:   1.120 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      67790000 bytes
HTML transferred:       66620000 bytes
Requests per second:    8929.07 [#/sec] (mean)
Time per request:       1.344 [ms] (mean)
Time per request:       0.112 [ms] (mean, across all concurrent requests)
Transfer rate:          59111.51 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       2
Processing:     0    1   0.8      1      13
Waiting:        0    1   0.8      1      12
Total:          0    1   0.8      1      13

Percentage of the requests served within a certain time (ms)
  50%      1
  66%      2
  75%      2
  80%      2
  90%      2
  95%      3
  98%      4
  99%      5
 100%     13 (longest request)

ということで何か問題が出るまではシンプルにConcurrentHashMapで行こうと思う。

今回検証したコードを示すのはちょっとだるいので、気になる人はGitHubでコミット履歴等を見て下さい(なげやり)。