京东的内容创作平台有很多的样式,比如文章、单品推荐、搭配、店铺上新、秒杀、直播预告、优惠卷。有些样式可以投稿到不同的频道,频道就好比露出的位置,频道露出的前提是内容质量审核通过后,频道侧二审通过。上面列举的有些样式因为时效性的考虑所以是不需要审核就可以外露的,比如直播预告、优惠卷,其他的样式则需要在CMS后台管理中经过一道或者两道审核,或者在质检抽查中复活。
我们知道内容体裁类都是读多写少的,所以一般强依赖缓存。其中,刚刚提到的很多场景都会直接写缓存或更新缓存,比如有些内容不需要审核、内容审核一次后、内容审核两次后、内容编辑后、内容编辑标签后、内容定时上下线等等,那么就需要有一个组件化的服务来满足同步。如果由你来设计一种方案来同步缓存或者多级缓存,你会怎么做呢?这就是我今天要和大家讨论的数据异构,将数据进行异地异构存储,比如说需要整合多张表数据构成一条记录然后异地存储。
我们先来看下第一种方案,就是双写,业务代码在对数据库操作时同步缓存。不过用这种对业务侵入的硬编码方式有很多缺点,我们首先得考虑事务性,考虑怎么保证同步数据库和同步缓存两者要么都成功要么都失败,但是一旦使用上事务,性能下降会非常明显。你可能看到过这种方案,更新操作时,先删除缓存后更新数据库,让查询操作来同步刷数据到缓存,这种方案最大的问题就是如果你删除的缓存是热点数据,那将导致大量的请求直接达到数据库。更极端的情况是,更新操作时,缓存节点不可用、程序和缓存节点之间网络延时增高、程序重启来不及同步缓存节点,那就会导致服务性能受到影响、数据无法达到一致性。
刚刚的双写是同步进行,如果换成使用MQ异步双写呢?也就是,程序处理完业务逻辑后发送MQ事件通知,由不同的应用消费MQ然后分别写入数据库和缓存中,其中写缓存的应用再反查其他表。很明显,异步双写的方案比前面的简单双写性能肯定高很多,甚至我们还可以利用MQ的重试机制保证数据不丢失。不过它依然存在硬编码、与业务强耦合的问题,还引入了时延问题。因为有些MQ消息必须是串行处理的,或者由于网络原因导致无法及时被消费,就会导致操作结果不一定能马上看到。
那我们再来看下第三种方案,使用定时任务异步双写。数据表中增加一个时间标示的字段,任何更新操作都导致该字段的值发生变化。然后增加一个定时器程序,每隔一段时间扫描表,把该时间段内发生变化的数据提取出来,然后逐条同步到缓存中。不过这种方式对数据库有很大的轮询压力,所以一般都不采用这种方式。
那有没有更好的方案完成数据异构呢?答案就是利用Mysql的binlog日记。
Mysql的binlog日记主要用来记录对mysql数据更新的操作,并以事务的形式保存在磁盘中,一般用来做Mysql的主从复制、数据恢复、增量备份。可以看出binlog日记具备高可靠性、低时延性,所以我们可以利用binlog日记来完成数据异构。整体流程大概是这样的,构建一个中间件系统,伪造成master的一个slave,当读取到binglog中的数据变化后,将其二进制内容格式化成MQ消息后传输,程序拿到消息后同步缓存。不过缺点也很明显,需要有一个中间件系统支撑,如果没有的话,使用前面提到的MQ异步双写或许是最佳的选择。
好,今天我主要和你讨论了关于如何利用数据异构实现多级缓存,这个技术还可以解决下面这种问题,比如数据库分库分表后如何进行数据迁移,当然后者的实现更加复杂,需要考虑数据校验问题,就不再展开了。如果今天的文章有帮助到你,欢迎分享给你朋友或者点个在看。
BLOG地址:www.liangsonghua.me
介绍:分享在京东工作的技术感悟,还有JAVA技术和业内最佳实践,大部分都是务实的、能看懂的、可复现的