dallaslu
2015-10-14 10:09:30 +08:00
近期在做一个高并发场景的项目,每秒处理数万请求;所以呢,用 Redis 的首要目标是缓存数据库查询结果。
某种数据的数据量和查询量都很大,数亿乃至数十亿条目,每秒数十万次查询;所以搭建了一套 6 节点的集群来处理。原来定的是用 512GB 内存的服务器,后来才知道内存大到一定程序性能未必跟得上来,又换用 64GB 的了,反正未来随时可以增加节点来扩展性能嘛。
不过发现 Java 的 Redis API 还不够健全,同时某些方法在 Redis 集群里也支持的不好。取交并集、计数等都存在一些问题,为了处理其他几种小量数据,于是又建了一套 2 节点、主从结构的 Redis ,这次有必要的话可以用得上 512GB 的了。><
系统中有一处涉及交易金额的计算,需要在集群环境中实现一个延时队列,以保证及时退还超时交易的冻结款。因 Web 服务节点众多,共享队列不好实现,所以这个担子自然就落在了 Redis 身上。通过有序集合,将 key 与时间 Redis ,另在 Web 节点上起线程执行解冻操作。后来发现几乎是某节点独立完成了所有的解冻操作,让人啧啧称奇;直到很久以后发现这台机器的 NTP 没有工作,系统时间快了几秒。
冻结操作需要判断,以避免高并发下账户超额支出;因为一开始对 Redis 不太熟悉,考虑了用锁的方式。不过,乐观锁也不适合这种高并发的场合。后来索性通过程序,维护冻结款和余额等之间的变化关系,虚拟出一个可用金额的变量,它们之间实时的存在等式关系;然后用单一变量「可用金额」扣除价格是否为正数,来判断是否可以交易。整个过程使用 zincrebyfloat ,原子操作,妈妈再也不用担心高并发出错啦!
其实用了 zincrebyfloat 之后呢,金额就变成了 100.049999000000099 这样,不过精度已经足够使用了。
缓存中的数据也经常变化,所以隔上相当一段时间,肯定要清空一次数据。但是担心这么大的数据操作影响实时性能,只好用 zscan 慢慢的遍历数据,悄悄的判断,从后面抹了无用数据的脖子,把它们逐个干掉。
但是在某些情况下,还是需要直接的清空数据。摸索之后发现逐个节点清空就可以做到,因为担心主从结构会导致问题,于是想办法来智能区分每节点是主还是从,后来一想反正在 master 上的修改会自动同步到 slave ,如果 slave 上发生问题就可以直接跳过的嘛,于是工作量上又节(neng)省(tou)了(lan)些(le)。