本文主要介绍redis硬件功能的性能。有需要的朋友可以参考一下。
在关注这个功能之前,一直使用Memcache的数据存储方式。然而,自从redis被取代后,Memcache存储和检索hash的数据就方便多了。但是问题来了。如果一个hash表的量不大,hGetAll函数几乎没有问题。一旦列表超过50以上,此时用hGetAll函数就能直观的看出性能问题,这里就不做数据分析了。
Redis是单线程的!当它处理一个请求时,其他请求只能等待。通常,请求被快速处理,但是当我们使用HGETALL时,我们必须遍历每个字段来获取数据。在此期间,消耗的CPU资源与字段数量成正比。如果我们也使用流水线,无疑会使事情变得更糟。
复制代码如下:
性能=CPUs操作
也就是说,在这种场景下,为了提升性能,要么增加运算过程中的CPU数量;或者减少操作过程中的操作次数。为了继续使用具有哈希结构的数据,并且为了解决这个问题,将哈希存储为序列化的字符串更加方便。取数时,先取出反序列化的数据,再取出hGet(key,array(hash.))使用。
例如:
复制代码如下:
.
$ arr key=array( DBF ba 184 bef 630526 a 75 F2 CD 073 a 6098 , DBF ba 184 bef 630526 a 75 F2 CD 0 dswet 98 )
$ strKey= test
$obj-hGet($strKey,$ arrKey);
将原来的hGetAll运算简化为hGet,也就是说,不再需要遍历hash中的每个字段,所以即使多个CPU不能参与运算,运算次数也大大减少,所以性能提升依然显著;当然,缺点也很明显。像所有冗余方法一样,这种方案会浪费大量内存。
有人会问,虽然没有遍历字段的过程,但是增加了反序列化的过程,反序列化的成本往往很高。这样也能提高性能吗?问题的关键在于,一开始遍历字段的操作是在一个cpu上完成的,后来的反序列化操作,不管什么语言,都可以通过多进程或者多线程保证在多个CPU上完成,所以性能普遍得到提升。
另外,很多人的直觉是通过运行redis的多个实例来解决问题。诚然,这可以增加运算过程中的CPU数量,有助于提高性能。但需要注意的是,hGetAll和流水线往往会使运算过程中的运算数量呈几何级数爆炸。相比之下,我们所能增加的redis多实例数量简直是杯水车薪,所以这个例子中的这个方法并不能完全解决问题。
记Redis那坑人的HGETALL
世界上本来没有坑,但是很多人掉下去,就成了坑。
我听人说过Redis的HGETALL是个坑,但我就是不信邪:不管是什么坑,我都要踩上去跺跺脚才罢休。说的好听点,就是不到黄河不死。说白了,看到棺材才会哭。
程序开始运行非常稳定,稳定到我想送一句话给每一个说HGETALL是坑的人:呸!这时候我就像温水里的青蛙一样,忘记了危险的存在,时间就这样过去了。突然有一天,需求变了,我要把HASH数据的内容从十几个字段扩展到一百多个字段。与此同时,我使用流水线技术一次获得数百个HGETALL结果。于是我就掉进坑里了:服务器宕机。
为什么会这样?Redis是单线程的!当它处理一个请求时,其他请求只能等待。通常,请求被快速处理,但是当我们使用HGETALL时,我们必须遍历每个字段来获取数据。在此期间,消耗的CPU资源与字段数量成正比。如果我们也使用流水线,无疑会使事情变得更糟。
如何解决这个问题?请允许我严肃地给出一个公式:
复制代码如下:
性能=CPUs操作
也就是说,在这种场景下,为了提升性能,要么增加运算过程中的CPU数量;或者减少操作过程中的操作次数。具体来说,我大致想到了以下方法:
借助Memcached
Redis存储方法不会改变任何东西。此外,我们使用Memcached来实现一组缓存,这些缓存存储Redis中最初需要的HGETALL的散列。当然,由于Memcached中存储的所有散列都是字符串,所以当我们存储散列时,我们实际上存储的是散列的序列化字符串,然后在查询时将它们反序列化。通常,Memcached客户端驱动程序可以透明地实现序列化和反序列化的过程。这种方案的好处是,由于Memcached支持多线程,可以让更多的CPU参与到操作中,同时,由于不需要遍历每个字段,相应的操作也会减少。当然也有很多缺点。因为引入了新的缓存层,所以浪费了内存并增加了复杂性。另外,有时候即使只需要获取少数几个字段的数据,也要先查询完整的数据,再进行筛选,这无疑是浪费带宽的。当然,在这种情况下,我们可以直接查询Redis,但无疑增加了一些复杂性。
对了,Memcached支持Multiget,可以达到类似流水线的效果,但是你要格外小心Memcached的坑,也就是Multiget的无底洞问题。
序列化字段冗余
Redis存储HASH时,会保存一个名为“all”的额外字段,其内容是原始HASH数据的序列化。在实际查询中,只需要反序列化冗余字段HGET。这种方案的优点在于,我们通过序列化字段的冗余,将原来的HGETALL运算简化为HGET,也就是说,不再需要遍历HASH中的每一个字段,因此即使不能有多个CPU参与运算,运算次数也大大减少,因此性能提升依然显著;当然,缺点也很明显。像所有冗余方法一样,这种方案会浪费大量内存。
有人会问,虽然没有遍历字段的过程,但是增加了反序列化的过程,反序列化的成本往往很高。这样也能提高性能吗?问题的关键在于,一开始遍历字段的操作是在一个CPU上完成的,后来的反序列化操作,不管什么语言,都可以通过多进程或者多线程保证在多个CPU上完成,所以性能普遍得到提升。
…
另外,很多人的直觉是通过运行Redis的多个实例来解决问题。诚然,这可以增加运算过程中的CPU数量,有助于提高性能。但需要注意的是,HGETALL和流水线往往会使运算过程中的运算数量呈几何级数爆炸。相比之下,我们所能增加的Redis多实例数量简直是杯水车薪,所以这个例子中的这个方法并不能完全解决问题。
…
坑是用来踩的。不要怕掉进去,当然前提是自己能爬出来!