在没关注这个函数之前,一直用的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多实例数量简直就是杯水车薪,所以本例中这种方法不能彻底解决问题。
…
坑,就是用来踩的。不用怕掉进去,当然前提是你能自己爬出来!
redis,hgetall,性能
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。