对于每分钟要处理近1000万的流水,每天流水近1亿的量,如何高效的写入和查询,是一项比较大的挑战。还是老办法,分库分表分区,读写分离,只不过这一次,我们先分表,再分库,最后分区。我们将消息流水按照不同的业务类型进行分表,相同业务的消息流水进入同一张表,分表完成之后,再进行分库。我们将流水相关的数据单独保存到一个库里面去,这些数据,写入要求高,查询和更新到要求低,将它们和那些更新频繁的数据区分开。分库之后,再进行分区。
这是基于业务垂直度进行的分库操作,垂直分库就是根据业务耦合性,将关联度低的不同表存储在不同的数据库,以达到系统资源的饱和利用率。这样的分库方案结合应用的微服务治理,每个微服务系统使用独立的一个数据库。将不同模块的数据分库存储,模块间不能进行相互关联查询,如果有,要么通过数据冗余解决,要么通过应用代码进行二次加工进行解决。若不能杜绝跨库关联查询,则将小表到数据冗余到大数据量大库里去。假如,流水大表中查询需要关联获得渠道信息,渠道信息在基础管理库里面,那么,要么在查询时,代码里二次查询基础管理库中的渠道信息表,要么将渠道信息表冗余到流水大表中。
将每天过亿的流水数据分离出去之后,流水库中单表的数据量还是太庞大,我们将单张流水表继续分区,按照一定的业务规则,(一般是查询索引列)将单表进行分区,一个表编程N个表,当然这些变化对应用层是无法感知的。
分区表的设置,一般是以查询索引列进行分区,例如,对于流水表A,查询需要根据手机号和批次号进行查询,所以我们在创建分区的时候,就选择以手机号和批次号进行分区,这样设置后,查询都会走索引,每次查询Mysql都会根据查询条件计算出来,数据会落在那个分区里面,直接到对应的分区表中检索即可,避免了全表扫描。
对于每天流水过亿的数据,当然是要做历史表进行数据迁移的工作了。客户要求流水数据需要保存半年的时间,有的关键流水需要保存一年。删数据是不可能的了,也跑不了路,虽然当时非常有想删数据跑路的冲动。其实即时是删数据也是不太可能的了,delete的拙劣表演先淘汰了,truncate也快不了多少,我们采用了一种比较巧妙方法,具体步骤如下:
创建一个原表一模一样的临时表1 create table test_a_serial_1 like test_a_serial;
将原表命名为临时表2 alter table test_a_serial rename test_a_serial_{date};
将临时表1改为原表 alter table able test_a_serial_1 rename able test_a_serial; 此时,当日流水表就是一张新的空表了,继续保存当日的流水,而临时表2则保存的是昨天的数据和部分今天的数据,临时表2到名字中的date时间是通过计算获得的昨日的日期;每天会产生一张带有昨日日期的临时表2,每个表内的数据大约是有1000万。
将当日表中的历史数据迁移到昨日流水表中去 这样的操作都是用的定时任务进行处理,定时任务触发一般会选择凌晨12点以后,这个操作即时是几秒内完成,也有可能会有几条数据落入到当日表中去。因此我们最后还需要将当日表内的历史流水数据插入到昨日表内; insert into test_a_serial_{date}(cloumn1,cloumn2….) select(cloumn1,cloumn2….) from test_a_serial where LEFT(create_time,8) > CONCAT(date); commit;
如此,便完成了流水数据的迁移;
根据业务需要,有些业务数据需要保存半年,超过半年的进行删除,在进行删除的时候,就可以根据表名中的_{date}筛选出大于半年的流水直接删表;