3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Redisで100万件のデータを0.3秒で読み込む

Posted at

まえがき

梅雨のせいでコインランドリーで乾燥機を回している時ふと、
「どれくらいのデータ量だとRedis(キャッシュ)導入に価値がある?
だって、一般的な処理だと早くなっても数ミリ秒の世界だし」と考え始めました。(唐突)
とりあえず100万件のデータでRedis導入にどれ程の価値があるのか、いざ検証してみます。

検証環境

  • ローカルPC (RAM 8GB)
  • Redis 6.0.5 (maxmemory 0=無制限)
  • Rails 6.0.3 (API mode)
  • MySQL

※ 今回はDBに存在する100万件のレコードをそのままRedisに突っ込みます。

100万件の投稿データ作成

今回検証のアプリケーションとしてRailsを使うのでseed ※でデータ投入しても良かったんですけど、何分遅いのでストアドプロシージャでinsertしていきます。


delimiter //
 create procedure bulkinsert()
 begin
    SET @count = 0;
    SET @limit = 1000000;
    WHILE @limit > @count DO
      INSERT INTO comedians (name, created_at)
      VALUES ('渡部 建', now());
      SET @count = @count + 1;
    END WHILE;
 end
//
delimiter ;
call bulkinsert();
DROP PROCEDURE IF EXISTS bulkinsert;
count
[rails_dev_redis]> select count(*) from comedians;
+----------+
| count(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.26 sec)

はい、100万件データがありますね。

model.to_aの実行結果をキャッシュ

キャッシュの引数にActiveRecordを渡すと、select文だけキャッシュするので、to_aで。
技術ブログによくあるパターンですね。

Comedian.all.to_a

計測結果

Processing by ComediansController#benchmark as HTML
       user     system      total        real
Rails_Cache_Fetch 38.901311   2.737989  41.639300 ( 52.607612)
Completed 200 OK in 52610ms (Views: 0.7ms | ActiveRecord: 0.0ms | Allocations: 14003982)

ご、52秒...。(´-`).。oO(なぜだ)

Redisのデータの読み込みが遅い問題

A String value can be at max 512 Megabytes in length.

Redisの公式ドキュメントにあるようにstring型は512MBまでしかサポートされていない。
ここに原因があるのではないかと考え、サイズを調べてみることに。

調査結果

Processing by ComediansController#benchmark as HTML
Rails_Cache_Fetch length: 2401298
Completed 200 OK in 25ms (Views: 0.9ms | ActiveRecord: 0.0ms | Allocations: 329)

2.4MBしか使ってないので、sizeの問題ではなさそう。

Redis側のデータを確認してみると、バイナリ文字が入っている。
Rails.cacheがバイナリから正規化する時間分だけ、遅くなっているのかもと考え、
redis.setでjsonを突っ込んでみることに。

計測結果

Processing by ComediansController#benchmark as HTML
       user     system      total        real
Redis_Get  7.533286   0.696594   8.229880 ( 10.452844)
Redis_Get length: 86100083
Completed 200 OK in 12306ms (Views: 0.4ms | ActiveRecord: 0.0ms | Allocations: 7000593)

10秒か〜。早くはなったんですけど、こういうことじゃないんですよね。
データの中身がバイナリじゃないので、8600万文字入ってます。

試行錯誤の末、高速化の成功

検索しても打開策がなかったので、色々試してみることに。
key-valueを複数もつパターンもやってみましたが、結局ループするとその分だけ遅いんですよね。

試行錯誤した結果、Rails.cacheでjsonを突っ込むと高速で参照できることがわかりました。

以下3パターンのRead/Writeの速度計測結果を貼ります。

オブジェクト・関数 形式 ベンチマーク上の表記
redis json Redis_Set・Redis_Get
Rails.cache json Rails_Cache_Write_Json・Rails_Cache_Read_Json
Rails.cache Array Rails_Cache_Write_Array・Rails_Cache_Read_Array

Set時の速度計測結果

Processing by ComediansController#benchmark as */*
       user     system      total        real
Redis_Set  Comedian Load (968.2ms)  SELECT `comedians`.* FROM `comedians`
  ↳ app/controllers/comedians_controller.rb:9:in `block (2 levels) in benchmark'
134.868704   4.608951 139.477655 (150.200997)
Rails_Cache_Write_Json  CACHE Comedian Load (0.0ms)  SELECT `comedians`.* FROM `comedians`
  ↳ app/controllers/comedians_controller.rb:12:in `block (2 levels) in benchmark'
117.643508   2.911988 120.555496 (121.557569)
Rails_Cache_Write_Array  CACHE Comedian Load (0.0ms)  SELECT `comedians`.* FROM `comedians`
  ↳ app/controllers/comedians_controller.rb:15:in `block (2 levels) in benchmark'
 39.717218   1.970096  41.687314 ( 41.873064)

意外にもArrayが一番早いです。

Get時の速度計測結果

       user     system      total        real
Redis_Get 12.294169   0.375882  12.670051 ( 12.914826)
Rails_Cache_Read_Json  0.196984   0.170824   0.367808 (  0.321568)
Rails_Cache_Read_Array 24.414510   1.228965  25.643475 ( 25.746937)
Get_DB  CACHE Comedian Load (0.1ms)  SELECT `comedians`.* FROM `comedians`
  ↳ app/controllers/comedians_controller.rb:30:in `block (2 levels) in benchmark'
117.189727   4.600185 121.789912 (122.321526)

Rails_Cache_Read_Jsonだと0.3秒で100万件を読み込めています。
Get_DB(122秒)はDBから直接fetch allした場合の速度で、比較の為に入れてます。

文字数のカウント

Redis length: 86099997
Rails_Cache_Json length: 2744225
Rails_Cache_Read_Array length: 2399348

redis.setでjsonを格納した場合、8600万文字入っていたのに対し、
Rails.cacheは270万文字と驚異的な短さです。
バイナリで保存しているのは容量の面で非常に有利ということですね。

あとがき

「どれくらいのデータだと導入に価値があるか」という本題でしたが、
100万未満のデータ量で参照に1秒以上かかってたら導入の価値はあるかなと思います。
たった4行のコード追加だけで、100万件読み込むのに1秒切れると思っていなかったので、ただただ脱帽しております。

ありがとうサルバトーレ、ありがとうここまで読んでくれた読者。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?