当ElastiCache的Redis SET操作出现堵塞时,我们遇到了困境

故事梗概

在AWS上构建Web服务时,逐渐发现整体响应速度在恶化。这是常见的情况。经过测量,发现将摘要结果缓存的ElastiCache的Redis成为瓶颈。进一步详细测量发现,似乎对Redis的SET操作花费了大约8秒的时间,哎呀,有些不对劲。

キャプチャ2.PNG

现场开始一片混乱。如果不尽快解决这个瓶颈问题,情况将变得非常糟糕,我们的加班从此开始。希望这篇文章记录下这些努力的结果,能对某些人有所帮助。

先取結論

    • ElastiCacheの弱いインスタンスはネットワーク帯域が狭い

ElastiCacheのデフォルトのインスタンスが r3.large なのには意味がある

キャッシュをしたけりゃ現金を払え

只有可以通过GET筛选出慢日志,并且有一种氛围缓存的选项。

当我们在AWS控制台上查看指标时,CPU使用率明显增加了大约5%,内存使用量也有足够的余地(具体数值忘记了)。这让我们非常困惑。从这一点可以明确,这并不是由于Redis是单线程的特性所引起的。

こういう時は、いつだって初歩の初歩から始めるのがベストです。Redisに SLOWLOG という便利そうなコマンドが存在することを確認した私は、スローログを片っ端から洗い出して原因を追求しようとしました。
コマンドは $ redis-cli -h slowlog get です。 $ redis-cli -h を実行してから slowlog get するのも一つの手段です。便利なサブコマンド補完が我々をサポートしてくれるでしょう!
しかしSLOWLOGのサブコマンドはGETかLENしか指定出来ません、私が求めているのはSETのスローログなのに!
これには大いに悩まされました。何故なら、我々の環境下では一番遅いGETでも0.08秒程度しか掛かっていなかったからです!
この数字がキャッシュとして十分な速度なのかはともかく、目下のボトルネックの原因とは到底思えません。

接下来,我想要了解Redis的处理状态,因为不断接收查询请求。我使用了MONITOR命令。
命令是$ redis-cli -h monitor。
将其输出到日志也是有效的。
总之,我希望通过目视而非负载测量结果来确认Redis的负载情况。

するとどうでしょう、時折、異様に巨大なvalueがSETされているのが確認できました。
およそ130万文字のJSON文字列がStringとしてSETされていたのです。おっと、これは明らかに嫌な臭いがします。
JSONをRedisに保存するのなら、巨大なJSON文字列よりもHashで保存したほうがどう考えても適切な予感がします。これだ! 我々は歓喜の声を挙げました。終電が無くなる前には帰れるぞ! ……果たして本当にそうでしょうか?
RedisのドキュメントのString型の項目には次の通りに書かれています。

A String value can be at max 512 Megabytes in length.
(文字列は最大512MBまで可能です)

哎呀,我们认为是瓶颈的字符串大约有130万个字符,约1.3MB。只有这么少的数据量,会成为瓶颈吗?如果尝试直接在终端中设置一个1.3MB大小的字符串,终端本身会冻结,但我认为内部处理这个量应该没有问题。这需要进行验证。

验证

我在本地设备上安装了Redis,并在我最熟悉的Ruby语言中开始验证。
步骤非常简单,只需要使用 $ gem install redis 命令安装Redis的Gem,并编写非常简洁的代码。
让我们试着用150万个字符进行验证。

require 'redis'
require 'benchmark'
redis = Redis.new
Benchmark.bm do |r|
  r.report 'redis_test' do
    redis.set('test', "A" * 1500000)
  end
end

puts "result string length: #{redis.get('test').length}"

很简单吧。

       user     system      total        real
redis_test  0.010000   0.000000   0.010000 (  0.002447)
result string length: 1500000

這個結果完全輕鬆的樣子。現在我們可以知道,這個值的設定並不是造成問題的原因。
讓我們再向極限挑戰一下吧。我們將進行10億字的驗證。

       user     system      total        real
redis_test  0.070000   0.040000   0.110000 (  0.138954)
result string length: 100000000

我稍微晚了一些,但这完全在可接受范围内。以每秒1亿字符(约100MB)的速度来看,我不认为130万字符的SET会成为一个重大瓶颈。

我们去写五亿个字吧。

       user     system      total        real
redis_test  0.290000   0.230000   0.520000 (  9.759761)
result string length: 500000000

速度变得相当致命。开始设置五亿个字符(约500MB)明显成了瓶颈。请注意(考虑到机器的状态,此事无需多次执行,省略此项)。

顺便提一下,如果超过了6亿个字符的上限,会导致错误。当然很自然。

仅仅通过一次交流引导

如果不是因为130万个字符的SET,那么到底是什么导致了这个瓶颈呢?
在调查文档和用例的过程中,我发现了一个名为Google组的讨论(https://groups.google.com/forum/#!searchin/redis-db/size/redis-db/n7aa2A4DZDs/3OeEPHSQBAAJ)。

Stefano Fratini 

Memcache recommends values no bigger than 1mb
(Memcacheは1MB以上のvalueを推奨してないんだ)
I would suggest the same for Redis. Even more so not going over the 100kb is most likely a good idea
(Redisにも同じことが言えると思う。100KBを超えるのは良くないよ)
Especially if you are hosting on AWS, having bigger objects lead to networking bottlenecks (the amazon guys are very stingy with bandwidth - and smarty so)
(特に、AWSを使ってるんだったら、デカいオブジェクトはネットワークのボトルネックに繋がるからさ(Amazonの帯域は小さいからね!))

特别是如果使用AWS,大型对象会成为网络瓶颈。

网络!!!
想都没想过,ElastiCache实例的网络成为了瓶颈!
我们使用的ElastiCache的Redis实例是t2.small。由于默认实例的费用太高,为了节省成本,我们舍弃了实例类型。
t2.small的网络性能如下:
低到中等
到底是什么样!
无论如何,t2.small的网络性能让人有不好的预感。
仔细想想,ElastiCache的默认实例是r3.large。很难想象AWS会为了赚钱而设置强力的实例作为默认。
r3.large的网络性能是中等……中等是什么标准呢,不知道是什么低,但肯定比t2.small要好得多……于是,我们尝试更改为网络性能处于中等且最便宜的m3.medium。
结果如何呢,原本平均需要8秒的Redis的SET操作缩短到了2秒!
啊,果然把强力的实例作为默认是有意义的!

結尾

暫時我們判斷使用m3.medium,即使需要2秒的時間,對於系統沒有問題。這是真的嗎?
如果存在著網路性能好、CPU和記憶體價格合理的實例,我們所面臨的問題就可以充分解決了,不是嗎?
如果這個網頁服務得到充分的認識,資料量增加,我們很可能需要重新檢視ElastiCache的實例類型。屆時,我們相信可以選擇一個包含CPU和記憶體資源的r3.2xlarge等內存最佳化實例類型,並且具有高網路性能的實例。
畢竟,現金買來的解決方案就是我們唯一的解決辦法。
Redis並不差,它是可靠的夥伴。
我們通過這樣的方式完成了這個案子的結束。

嗯,只是加强了实例而已。

如果要很好地做到缓存,就需要很好地支付现金,这就是关键。

广告
将在 10 秒后关闭
bannerAds