记Redis那坑人的HGETALL
世上本没有坑,掉进去的人多了,也便成了坑。
早就听人说过Redis的HGETALL是个坑,可我偏偏不信邪:不管什么坑,一定要自己踩上去跺两脚才肯罢休。说好听点这是不到黄河心不死,说难听点就是不见棺材不落泪。
开始程序运行的非常稳定,稳定到我想送所有说HGETALL是个坑的人一个字:呸!此时的我就像温水里的青蛙一样忘记了危险的存在,时间就这样一天一天的过去,突然有一天需求变了,我不得不把HASH数据的内容从十几个字段扩展到一百多个字段,同时使用了Pipelining一次性获取上百个HGETALL的结果。于是我掉坑里了:服务器宕机。
为什么会这样?Redis是单线程的!当它处理一个请求时其他的请求只能等着。通常请求都会很快处理完,但是当我们使用HGETALL的时候,必须遍历每个字段来获取数据,这期间消耗的CPU资源和字段数成正比,如果还用了Pipelining,无疑更是雪上加霜。
如何解决这个问题?请容许我煞有其事的给出一个公式:
PERFORMANCE = CPUs / OPERATIONs
也就是说,此场景下为了提升性能,要么增加运算过程中的CPU数量;要么降低运算过程中的操作数量。具体来说,我大致想到了以下几种方法:
借助Memcached
Redis存储方式不做任何改变,额外的,我们借助Memcached实现一套缓存,里面存储原本需要在Redis里HGETALL的HASH,当然,由于Memcached里存储的都是字符串,所以当我们存储HASH的时候,实际上存储的是HASH序列化后的字符串,查询的时候再反序列化即可,通常Memcached客户端驱动可以透明实现序列化和反序列化的过程。此方案的优势在于因为Memcached支持多线程,所以可以让更多的CPU参与运算,同时由于不用再遍历每一个字段,所以相应的操作会减少;当然劣势也不少,因为引入了一个新的缓存层,所以浪费了内存,增加了复杂性,另外,有时候即便我们只需要获取少数几个字段的数据,也不得不先查询完整的数据,然后再筛选,这无疑浪费了带宽。当然这种情况下我们可以直接查询Redis,但是无疑又提升了一些复杂性。
顺便说一句,Memcached支持Multiget,可以实现类似Pipelining的效果,但你要格外小心这里面有关Memcached的坑,也就是Mulitiget无底洞问题。
多实例冗余
Redis存储方式不做任何改变,但同时运行多个实例,实际查询时随机挑选一个实例。此方案的优势在于多个Redis实例可以让多个CPU参与运算;劣势是浪费了内存,此外虽然多个Redis实例可以让多个CPU参与运算,但毕竟冗余新的实例成本是很高的,同时运行很多个实例往往并不现实,与此同时,操作数量却没有改善,比如说Redis的HASH有一百个字段,Pipelining一次需要HGETALL一百个这样的HASH,那么实际的操作数就是以万为单位的,相对应的,我们增加一两个冗余实例对性能的提升简直是杯水车薪。
序列化字段冗余
Redis在存储HASH的时候,多保存一个名为「all」的字段,其内容是原HASH数据的序列化,实际查询的时候,只要HGET这个冗余字段后再反序列化即可。此方案的优势在于通过序列化字段冗余,我们把原本的HGETALL操作简化为HGET,也就是说,不再需要遍历HASH中的每一个字段,因此即便不能让多个CPU参与运算,但是却大幅降低了操作数量,所以性能的提升仍然是显著的;当然劣势也很明显,和所有的冗余方式一样,此方案浪费了大量的内存。
有人会问,这样虽然没有了遍历字段的过程,但是却增加了反序列化的过程,而反序列化的成本往往也是很高的,难道这样也能提升性能?问题的关键在于开始我们遍历字段的操作是在一个CPU上完成的,后来反序列化的操作,不管是什么语言,都可以通过多进程或多线程来保证是在多个CPU上完成的,所以性能总体上是提升的。
…
坑,就是用来踩的。不用怕掉进去,当然前提是你能自己爬出来!
建议继续学习:
- redis源代码分析 - persistence (阅读:31140)
- Redis消息队列的若干实现方式 (阅读:10703)
- 基于Redis构建系统的经验和教训 (阅读:9283)
- 浅谈redis数据库的键值设计 (阅读:8286)
- redis在大数据量下的压测表现 (阅读:7379)
- redis运维的一些知识点 (阅读:7416)
- Redis和Memcached的区别 (阅读:6776)
- Redis作者谈Redis应用场景 (阅读:6564)
- redis 运维实际经验纪录之一 (阅读:6470)
- Redis内存存储结构分析 (阅读:6177)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:老王 来源: 火丁笔记
- 标签: HGETALL Redis
- 发布时间:2013-01-22 14:03:36
- [56] Oracle MTS模式下 进程地址与会话信
- [56] IOS安全–浅谈关于IOS加固的几种方法
- [55] 如何拿下简短的域名
- [54] 图书馆的世界纪录
- [52] android 开发入门
- [52] Go Reflect 性能
- [50] 读书笔记-壹百度:百度十年千倍的29条法则
- [49] 【社会化设计】自我(self)部分――欢迎区
- [38] 程序员技术练级攻略
- [33] 视觉调整-设计师 vs. 逻辑