最近公司有展示实时数据的需求,采用了spark streaming + redis的实时方案。通过spark streaming将流水数据插入到redis中,后端从redis中获取到实时数据展示到前端。在这一周的时间里,由于实时数据组合多,五分钟计算一个数据,数据量特别大。在从redis获取数据方面踩了很多的坑,因此记录下redis的一些性能问题。

1、不要在生产环境使用keys命令。

在应用中,我们需要通过特定的组合获取到db中所有符合条件的key集合,然后通过key的集合来获取对应的数据。在这个过程中,可能会用到keys来正则匹配出符合条件的key集合。那么用keys这个命令来匹配组合是非常危险的。因为在执行keys命令的时候,redis会锁定,如果数据量庞大,则会造成redis锁定,而且因为匹配所有的key,会搞成CPU运算达到一个高峰,在生产环境来说,这是很危险的事情。因为我们应该避免使用keys来匹配符合条件的key集合。

解决方案:
可以将db中的所有key放在一个set(key_set)中,通过获取key_set的值得到当前db的所有key的集合,然后后端通过正则表达式在程序中做处理,过滤出符合条件的key集合。再通过符合条件的key集合到redis中获取数据。

2、不要在生产环境中用到hgetall命令

由于数据的特殊性,每个组合每5分钟得对数据进行一次聚合,因此采用hash类型存储组合来存每五分钟的数据。当我们需要获取某个组合或者多个组合一天所有的数据,那么我们则需要拿到当前组合内所有的数据,然后通过程序进行汇总。

因此这里最便捷的方式则是采用hgetall来获取hash类型数据的所有数据。hgetall的原理是通过循环遍历当前hash的field和value然后将数据返回。那么它的复杂度则是o(n)。

但是这里有个问题,我们拉取某个组合1天的数据,那么这个hash类型的数据的field字段有2460/5(288)个,如果有15000个组合,则需要拉取15000288(43.2w)个数据。一次性拉去这么多数据,对于redis来说是非常吃力的。这导致在做需求的时候,一个指标就需要8s的时间。还会给redis造成很大的压力。

解决方案:
尽量避免取太多组合,通过spark streaming对数据组合进行汇总。减少一次性取太多的组合数据,将组合数据控制到500以下。

3、可以使用pipeline来减少网络传输的损耗。

在一次性取多个组合的时候,可以利用redis的pipeline特性一次性将所有的取数操作发送给redis-server,然后redis-server执行后,将每条取数操作的数据缓存起来,在执行完之后一次性发送给后端,这样可以极大的提高网络传输和redis的执行效率。

但是值得注意的是:pipeline和hgetall要一起使用需要特别谨慎,尤其是数据量太大的时候。可能会造成redis宕机。

小结

关于redis的使用,坑还是挺多的,需要熟练运它,还有太多需要进行探索和摸索。尤其在遇到问题的时候,应该从根源上去寻找问题的原因,通过一些工具来不断分析。

参考资料:

关于redis的keys命令的性能问题
记Redis那坑人的HGETALL
KEYS pattern
HGETALL key

发表评论

电子邮件地址不会被公开。 必填项已用*标注