一次基于CI的Redis性能问题定位


点击访问原文
您还可以加入全栈技术交流群(QQ群号:254842154)


使用PHP的CI 3.0框架已经有一段时间了,它内置的类库虽然比较少,但是基本的都有,例如Cache模块,它提供了几种最常用的快速缓存的封装,包括apc、memcached和redis等。其中redis是比较常用的缓存,用于存储程序中经常用到并且不会频繁变化的数据。用法:

//加载类库
$this->load->driver('cache', array('adapter' => 'redis'));
//存字符串
$this->cache->save('key_str_hello', 'value_hello', 3600);
//存数组
$this->cache->save('key_array_hello', array('a' => 123), 3600);
//取
$data = $this->cache->get('key_hello');
//删除
$this->cache->delete('key_hello');

问题描述

在一次接口性能测试中,发现取数据时即使命中缓存,耗时也会比较长,基本在200-300毫秒左右。按常理隐约感觉缓存可能出现了问题。

问题定位

问题的关键是找出耗时的方法或代码片段。检查了跟业务相关的逻辑,没有耗时的操作。由于调用了比较多的CI库函数,假如要层层深入跟踪代码,可能会比较难定位。于是想到了<a href='http://pecl.php.net/package/xhprof' target='_blank'>xhprof</a>,定位性能问题的利器,大家可以自行查资料如何使用。

于是在测试环境装了xhprof,并在待测试的方法中开启了xhprof的数据收集。得到了一个详细的函数调用路径及耗时,如下图:

image

可知,此次请求耗时341ms左右,从每个方法的耗时中不难发现,其中有一个Redis::sMembers方法耗时289ms之多,占到了将近85%时间。继续深入查看,发现Redis::get函数耗时19.4%占用了大部分时间。

image

sMembers是一个redis集合操作函数,用于获取指定key值的集合信息。于是马上想到了CI封装的redis类库,找到system/libraries/Cache/drivers/Cache_redis.php 文件。

经过代码阅读,发现在构造函数 __construct中有一行代码读取了_ci_redis_serialized这个key对应的值:

// Initialize the index of serialized values.
$serialized = $this->_redis->sMembers('_ci_redis_serialized');
empty($serialized) OR $this->_serialized = array_flip($serialized);

完整阅读完代码后发现,CI的redis类库在存储数据时,会区分普通字符串数据,和对象或数组数据,对于对象或数组,在存储到redis之前,会进行序列化操作,并把这个数据的key值存储到redis集合中(这个集合使用_ci_redis_serialized作为key);在取出数据时,首先从集合中查找对应的key是否存在,假如存在则进行反序列化操作。

也就是说,随着redis中存储的数组或对象越来越多,集合中存储的key也就越来越多。而每次redis的操作,都会触发一次取集合数据的操作。当数据量较大时,这个取集合数据的网络延时也会变得比较大。

问题解决

改写CI的redis类库,在构造函数中去掉取集合数据的操作,并通过 sIsMember 方法来判断当前的key是否为对象或数组。

总结

  • 对于一些顽疾问题,追根溯源时别忘了借助一些外部工具

  • 不要太相信框架

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 32,016评论 2 89
  • 最近背英语单词,有一个“blunt”(钝的),我就把它写的方方正正,一个个字母呆呆的,就好像看到我自己呆呆的一样,...
    我是你好哇阅读 163评论 0 0
  • 2017年5月9日 晴 我闭上眼睛像是活着 花香涌入水流 高山温柔的低头 悦耳的不仅有鸟雀 呼吸的细腻衬托着喜悦 ...
    鲜栗子阅读 138评论 1 1
  • 吃大餐常觉得贵。 其实路边摊也不便宜。 一碗28元的张亮麻辣烫,细想也是挺贵的。 可是它特别能抚慰人心。大餐不能比...
    布衣乔木阅读 198评论 0 0