java hgetall_redis的hGetAll函数的性能问题(记Redis那坑人的HGETALL)
在没关注这个函数之前,一直用的Memcache的数据存储方式,但是自从更换了redis之后,对于一个hash的数据存与取 对于Memcache方便甚多,但是问题来了,一个hash的列表如果量不大的情况,用hGetAll函数几乎看不出问题,一旦这个列表超过50或者更多时,此时用hGetAll函数便能很直观的看到性能问题,这里就不作数据分析了。
Redis是单线程的!当它处理一个请求时其他的请求只能等着。通常请求都会很快处理完,但是当我们使用HGETALL的时候,必须遍历每个字段来获取数据,这期间消耗的CPU资源和字段数成正比,如果还用了PIPELINING,无疑更是雪上加霜。
PERFORMANCE = CPUs / OPERATIONs
也就是说,此场景下为了提升性能,要么增加运算过程中的CPU数量;要么降低运算过程中的操作数量。在为了继续使用hash结构的数据,又要解决此问题,比较方便的方法就是将hash以序列化字符串存储,取的时候先取出反序列化的数据,再用hGet(key,array(hash..))。
例如:
....
$arrKey = array('dbfba184bef630526a75f2cd073a6098','dbfba184bef630526a75f2cd0dswet98')
$strKey = 'test';
$obj->hGet($strKey,$arrKey);
把原本的hGetAll操作简化为hGet,也就是说,不再需要遍历hash中的每一个字段,因此即便不能让多个CPU参与运算,但是却大幅降低了操作数量,所以性能的提升仍然是显著的;当然劣势也很明显,和所有的冗余方式一样,此方案浪费了大量的内存。
有人会问,这样虽然没有了遍历字段的过程,但是却增加了反序列化的过程,而反序列化的成本往往也是很高的,难道这样也能提升性能?问题的关键在于开始我们遍历字段的操作是在一个cpu上完成的,后来反序列化的操作,不管是什么语言,都可以通过多进程或多线程来保证是在多个cpu上完成的,所以性能总体上是提升的。
另外,很多人直觉是通过运行redis多实例来解决问题。确实,这样可以增加运算过程中的CPU数量,有助于提升性能,但是需要注意的是,hGetAll和PIPELINING往往会让运算过程中的操作数量呈几何级爆炸式增长,相比之下,我们能增加的redis多实例数量简直就是杯水车薪,所以本例中这种方法不能彻底解决问题。
记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在存储HASH的时候,多保存一个名为「all」的字段,其内容是原HASH数据的序列化,实际查询的时候,只要HGET这个冗余字段后再反序列化即可。此方案的优势在于通过序列化字段冗余,我们把原本的HGETALL操作简化为HGET,也就是说,不再需要遍历HASH中的每一个字段,因此即便不能让多个CPU参与运算,但是却大幅降低了操作数量,所以性能的提升仍然是显著的;当然劣势也很明显,和所有的冗余方式一样,此方案浪费了大量的内存。
有人会问,这样虽然没有了遍历字段的过程,但是却增加了反序列化的过程,而反序列化的成本往往也是很高的,难道这样也能提升性能?问题的关键在于开始我们遍历字段的操作是在一个CPU上完成的,后来反序列化的操作,不管是什么语言,都可以通过多进程或多线程来保证是在多个CPU上完成的,所以性能总体上是提升的。
…
另外,很多人直觉是通过运行Redis多实例来解决问题。确实,这样可以增加运算过程中的CPU数量,有助于提升性能,但是需要注意的是,HGETALL和PIPELINING往往会让运算过程中的操作数量呈几何级爆炸式增长,相比之下,我们能增加的Redis多实例数量简直就是杯水车薪,所以本例中这种方法不能彻底解决问题。
…
坑,就是用来踩的。不用怕掉进去,当然前提是你能自己爬出来!
java hgetall_redis的hGetAll函数的性能问题(记Redis那坑人的HGETALL)相关推荐
- php redis hgetall 慢,redis的hGetAll函数的性能问题(记Redis那坑人的HGETALL)
Redis是单线程的!当它处理一个请求时其他的请求只能等着.通常请求都会很快处理完,但是当我们使用HGETALL的时候,必须遍历每个字段来获取数据,这期间消耗的CPU资源和字段数成正比,如果还用了PI ...
- 记Redis那坑人的HGETALL
世上本没有坑,摔的人多了,也便成了坑. 早就听人说过Redis的HGETALL是个坑,可我偏偏不信邪:不管什么坑,一定要自己踩上去跺两脚才肯罢休.说好听点这是不到黄河心不死,说难听点就是不见棺材不落泪 ...
- redis的hGetAll函数的性能问题
在没关注这个函数之前,一直用的Memcache的数据存储方式,但是自从更换了redis之后,对于一个hash的数据存与取 对于Memcache方便甚多,但是问题来了,一个hash的列表如果量不大的情况 ...
- Java 7 vs Groovy 2.1性能比较
自从我与Grails上一次接触以来,我已经有两年没有使用Groovy了. 我陷入(硬)核心企业Java中,并且在后台遇到了一些性能方面的问题. 我几乎错过了学习Spock的机会,但是幸运的是, 华沙J ...
- Java静态编译技术:突破Java“冷启动”桎梏,实现启动性能“质”的飞跃
自1996年诞生以来,Java语言长期在最受欢迎的编程语言排行榜中占据领先地位.除了语言本身的优秀特性之外,Java语言持续演进.不断发展也是它能够保持长盛不衰的重要原因. |Java市场份额不断下降 ...
- php中include的作用,PHP 中关于 include() 函数的性能
简明现代魔法 -> PHP服务器脚本 -> PHP 中关于 include() 函数的性能 PHP 中关于 include() 函数的性能 2010-03-03 PHP程序员最常用的两个函 ...
- Java正则表达式--Matcher.group函数的用法
https://www.cnblogs.com/jiafuwei/p/6080984.html Java正则表达式--Matcher.group函数的用法 原来,group是针对()来说的,group ...
- Java中的回调函数学习-深入浅出
Java中的回调函数一般来说分为下面几步: 声明回调函数的统一接口interface A.包括方法callback(); 在调用类caller内将该接口设置为私有成员private A XXX; 在c ...
- java中的string函数_java中string.trim()函数的作用实例及源码
trim()的作用:去掉字符串首尾的空格. public static void main(String arg[]){ String a=" hello world "; Str ...
最新文章
- C++ STL: 基本六大部件概览 及 各个容器使用方式和底层实现概览
- python长度分割文本_python 按照固定长度分割字符串的方法小结
- cannot find -lcudart
- 提取pfx证书公钥和私钥
- 总结一些常用Android adb 命令
- mysql db compare_数据传输 | mysqldiff/mysqldbcompare 实现 DTLE 自动化测试
- 2000多一件的大牌T恤,真实成本有多少?
- 在IIS中启用父路径,不被黑客利用
- Matlab 中prod函数的使用
- 一步一步写算法(之合并排序)
- 今天,很高兴!我成为了园子里的一份子!
- SQL LIKE通配符 模糊查询
- 牢筑企业安全屏障,护航邮箱使用无忧【网易企业邮箱】
- 如何修改 WordPress 的用户默认头像?
- 2022N1叉车司机考试练习题及在线模拟考试
- 网站备案需要什么材料
- 区块链技术应用在金融领域之大数据风控
- Flink web ui面板功能简述
- 黑客攻击_我如何开始黑客攻击
- 系统运维应届生面试题
热门文章
- 基于单片机烟雾及温度报警器-火灾监测-毕设课设资料
- 《拳皇15》先导预告 多位角色回归、细节下月公开
- 猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,又多吃了一个第二天早上又将剩下的桃子吃掉一半,又多吃了一个。到第10天早上时,只剩下一个,求第一天有多少桃子。
- 如何查看邮件服务器名称,如何查找Exchange 服务器名称
- HTML5和CSS3核心笔记
- python去除图片水印_Python | 图中使用类的水印
- 含有单相铁芯变压器的铁磁混沌电路的分析及控制
- PythonGUI 使用Tkinter写一个简单时间间隔计算器
- 计算机教室灭火器配置标准,计算机教室与多媒体教室安装场地基本要求.doc
- 世界上最神奇的数字 算法求解