聚合写是什么
聚合写是什么?字面意思就是把很多次写操作,进行聚合,变为一次性写入
这里可以类比一下MySQL的redoLog日志,不是一个事务就刷一次盘,而是多个事务按照规则进行一次性刷盘这种思想
用生活中简单的例子,平常家庭生活中,都会产生很多日常的垃圾,如果每有一个垃圾就下楼扔一次,那么一天要下去好多次,
这时候我们把垃圾先扔到垃圾桶,最后再一次性处理掉
聚合写有什么价值
为什么需要聚合写?简单来说,聚合写是将多次写操作聚合成一次写操作以提升效率的手段
最常见的例子就是写数据库,原本写数据是直接入库的,而数据库写入性能通常不怎么高,比如MySQL
常见的设备的QPS都在5000以内,而如果是一些热点数据更新,则会更慢。
针对这种情况,我们很容易想到是否能现在内存中进行聚合,每隔一段时间再入一次库,解决几倍的性能是没问题的
场景 - 聚合写在计数场景的应用
在看视频或者文章的时候,都会存在点赞功能,即你喜欢这个内容就对内容进行一次点击点赞的操作,那么这个点赞计数就会 + 1
如果你点1000次,或者有1000个人点赞,那么操作都执行完成之后,总数量就增加了1000.
我们把一次点赞操作抽象为一次点赞请求,那么此时1000个请求就是分别 + 1,最终写入数据库也写了1000次,
为了提升效率,我们其实可以在内存里先计算,最后再一次性向数据库 + 1000
场景 - 聚合写在数据插入场景的应用
举个例子,现在有个需求是要创建一些任务,这些任务之间是没有关系的,我们可以一次性插入数据库,为了提高效率,
我们也可以聚合起来一次性插入到数据库,计数我可以内存里算好再插入数据库,多个任务怎么一起插入呢?
我们可以进行聚合拼接,将多个sql整合成一个sql,本质是节约网络I/O,但是到数据库的时候,其实还是插入了多条任务
用一张表为例子说明:
create table `t_user`(
`id` bigint(20) not null AUTO
`name` varchar(32) not null
)
假设我们要插入5条数据:
Insert into t_user(name) values("test1");
Insert into t_user(name) values("test2");
Insert into t_user(name) values("test3");
Insert into t_user(name) values("test4");
Insert into t_user(name) values("test5");
对于MySQL来说,这是5次请求,如果我们聚合一下,进行批量插入:
Insert into t_user(name) values("test1"),("test2"),("test3"),("test4"),("test5");
我们怎么定聚合插入的数据条数呢?
MySQL对一条sql语句的大小是有限制的,这个可以修改,一般来说字段数目没有那么爆炸的话,500条是不会有问题的
如果数据条数一直达不到指定的条数怎么办?那不是一直不进行插入了吗?这里可以设置一个时间
像Kafka的批量生产一样,有两种规则,达到某个大小就发送一次,多少秒发送一次
扩展 - 聚合写对可靠性的影响
聚合写这种方案有个缺点,就是数据丢失问题,比如计数服务节点挂了,那么在内存中保存的数据就会丢失
以聚合写计数举例,如果计数服务在刷入数据库前挂了,导致点赞数据丢失
这个需要根据业务来选择的,如果业务能接受一定程度的数据丢失,比如损失几秒的数据是可以接受的
那么就可以考虑用内存聚合,典型的例子就是直播点赞计数场景,直播点赞计数对于准确性的要求不高,能提升效率还是可以的
如果是精准度高的场景,比如金融之类的,显然用这种聚合写的方式肯定是不合适的,安全 > 效率
换个角度,如果不放在本地呢?我们把点赞数据放到Redis呢?Redis不是有持久化机制吗?放Redis可以吧
首先Redis持久化并不能保证完完全全不丢失一点数据,哪怕你把AOF调整成每次刷盘的机制,也不能保证Redis不丢失数据
Redis刷盘是异步的,也就是说Redis是把刷盘丢给子进程去执行,然后直接返回成功,也就是子进程都没有持久化完成,就返回成功
扩展 - 聚合写对查询的影响
除了可靠性,还需要再探讨一下聚合写会不会对我们的查询结果造成影响,比如一些场景,原本是同步写入,成功了数据肯定就变了
如果是聚合写的话,就从同步操作变成了异步操作,虽然返回处理成功,但是如果立马去查,这个时候数据可能还没有刷到数据库
我们需要针对查询做一些考虑:
-
如果是写入之后不需要调用方关注结果的场景,比如推送一些不重要的消息,那么用聚合写就非常轻松自然
-
如果写入之后要查询数据,我们就需要分情况来讨论了
-
对及时性要求不高的,比如直播点赞,那么不用做什么额外的处理,查到旧数据没什么问题
-
对及时性要求高点的,比如创建一个任务,这时候要立刻查任务的话就可能查不到,我们可以将数据写入缓存,支持查询,
也可以根据聚合的间隔时间,hold住请求一段时间,等待聚合写入的刷新时间到,这个时候数据大概率已经入库了
-