单线程

四季春风,不厌冬

  1. “变更”队列数据结构
  2. 读取key
    1. 改善
  3. 参考

  缓存同步数据库,采取的是定时定量同步策略。由缓存维护一个”变更”队列,用以存储变更过的key,定时任务将会读取前N个key,再由应用程序实时查询这些key的value,将其写入数据库,最后再把这些key从”变更”队列中移除。
  
缓存批量同步数据库流程

  接下来,我介绍下”变更”队列的数据结构,以及优化读取key的方案。

“变更”队列数据结构

  队列使用redis的set集合,这个集合的key为:xx:activity_stock:update_key(实际更复杂),value为变更的热点数据的key,形如:
  

1
2
3
4
  redis 127.0.0.1:6379> SMEMBERS xx:activity_stock:update_key
1) "key1"
2) "key2"
3) "key3"

读取key

  程序中使用了smembers 命令查询该set集合中全部元素,然后在程序中遍历前N个key同步数据库,然后将前N个从”变更“队列中移除。这里会出现三个问题:
  1. smembers 时间复杂度为O(N),且阻塞线程(致命)
  2. 如果队列中某个key的所索引一直大于N,那么该key可能永远无法同步数据库(该key一直被更新)
  3. (3)(4)(5)非事务操作,当(3)(4)完成后,如果(1)于(5)先执行,那么本应该重新进入set的key立刻被移除出set,至少在一个同步周期内,key无法同步数据库。如果key永远不再变更,那么key永远无法再进入set,也就无法同步数据库。

改善

  针对上述问题,我的解决方案如下:
  1. 获取set集合元素,使用 sscan 命令,虽然该命令的时间度为O(N),但是它不会阻塞线程(由主线程fork出一个线程)
  2. 再定义一个全量同步”变更”队列的定时任务,周期可以拉长,比如一天一次
  3. (3)(4)(5)放在一个事务内,因为(4)是数据库操作,那么只要保证(3)(5)在一个事务内,且执行完返回key的value,即顺序为(3)(5)(4)

参考

本文作者 : pengqin.zhou
本文使用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议
本文链接 : https://www.zhoupq.com/blog/%E7%BC%93%E5%AD%98%E6%89%B9%E9%87%8F%E5%90%8C%E6%AD%A5%E6%95%B0%E6%8D%AE%E5%BA%93%E6%B5%81%E7%A8%8B/

本文最后更新于 天前,文中所描述的信息可能已发生改变