redis的list 功能实践

redis特点是单线程复用IO,支持list,set,sorted set,hash等众多数据结构,线程安全。
最近是用list做了一个商品交易记录的承载。需要显示给用户最近一个月的交易记录,且每个商品显示不超过750条。
原有实现:MySql+memcached。由于交易记录的即时性,缓存不宜太久,所以很容易把memcache击穿,mysql查询压力山大,对于交易记录太大的商品甚至出现超时错误。所以,需要改进。
分析一下这个场景,需要保存最近一个月的交易记录,而且只是提供最多750条的显示,因此对每个商品来说,这个记录数量是一定的。从数据存取角度来看,这是一个需要不断添加数据,查询方式基本固定的场景。由于可以保持列表长度在控制范围,因此考虑应用redis的list作为存储的主力。
这里的主要问题,一是如何存、二是如何取、三是原有数据的订正。
存。所用接口:Lpush+Ltrim。redis List是个双向列表,提供左侧和右侧的数据顺序插入功能,这里由于要把最近的记录放到列表头,采用左侧插入(Lpush接口),同时为了控制列表长度,判断长度超过一个阈值时进行截断操作Ltrim,这里有个技巧就是截断后的长度最好低于阈值一个区间,可以防止频繁的trim操作。
取。所用接口:LRange。这里有点麻烦,因为是需要查询一个时间点之前的记录,那么需要把List对象取出来进行核对,好在整个List长度是有设置上限的且是按照时间先后顺序存入的,那么可以采用二分查找的方式,这样比遍历性能要好很多。其次,还要在此基础上实现一个分页的查询。这里需要注意LRange(key,start,end)的start和end都是包括在取出列表的,跟java的list有些不同。
数据订正。这是所有数据迁移项目的必然痛点,问题关键在于如何把发生在新老存储切换时间所产生的新纪录写到新的redis存储上,其实万全的方式是吧这些新纪录写到另外一个地方,当存储切换完毕后再从这个地方回写。虽然描述的很简单,但这其中的数据一致性校验等种种问题足够你喝几壶的。考虑种种,最后还是采取了简单粗暴的方式,凌晨时执行老数据全量覆盖,期间产生的新数据丢弃,因为考虑到交易记录更多的时候是作为流水账来出现,有时是要根据数据的重要性评估投入产出比的。
整个实现之后,较原有实现性能提升了一大截,但是有几个问题在里面:1)由于rdb并非关系型数据不能实现像sql一样的条件查找,导致需要按照条件查找并做分页时,需要把整个列表都取出来再判断,造成不必要的内存损耗。但由于列表持有时间不长,暂不会造成FullGC困扰 。2)这样实现只能记一个流水账,因为内存有限。如果要查一个月前的记录目前还要查询订单。解决这个问题倒是有个好东东--Hbase,可以一直往里丢而且查询极快,奈何那是高富帅系统用的装备,维护成本很高,小网站用不起啊。。
后记:redis确实是个好东东,适合的场景可以大胆尝试一下

 

猜你喜欢

转载自jokert.iteye.com/blog/1782001
今日推荐