关于mysql:百亿级数据分表后怎么分页查询

13次阅读

共计 2577 个字符,预计需要花费 7 分钟才能阅读完成。

当业务规模达到肯定规模之后,像淘宝日订单量在 5000 万单以上,美团 3000 万单以上。数据库面对海量的数据压力,分库分表就是必须进行的操作了。而分库分表之后一些惯例的查问可能都会产生问题,最常见的就是比方分页查问的问题。个别咱们把分表的字段称作 shardingkey,比方订单表依照用户 ID 作为 shardingkey,那么如果查问条件中不带用户 ID 查问怎么做分页?又比方更多的多维度的查问都没有 shardingkey 又怎么查问?

惟一主键

个别咱们数据库的主键都是自增的,那么分表之后主键抵触的问题就是一个无奈防止的问题,最简略的方法就是以一个惟一的业务字段作为惟一的主键,比方订单表的订单号必定是全局惟一的。

常见的分布式生成惟一 ID 的形式很多,最常见的雪花算法 Snowflake、滴滴 Tinyid、美团 Leaf。以雪花算法举例来说,一毫秒能够生成 4194304 多个 ID。

第一位 不应用,默认都是 0,41 位工夫戳 准确到毫秒,能够包容 69 年的工夫,10 位工作机器 ID高 5 位是数据中心 ID,低 5 位是节点 ID,12 位序列号 每个节点每毫秒累加,累计能够达到 2^12 4096 个 ID。

分表

第一步,分表后要怎么保障订单号的惟一搞定了,当初思考下分表的问题。首先依据本身的业务量和增量来思考分表的大小。

举个例子,当初咱们日单量是 10 万单,预估一年后能够达到日 100 万单,依据业务属性,个别咱们就反对查问半年内的订单,超过半年的订单须要做归档解决。

那么以日订单 100 万半年的数量级来看,不分表的话咱们订单量将达到 100 万 X180=1.8 亿,以这个数据量级局部表的话必定单表是扛不住的,就算你能扛 RT 的工夫你也根本无法承受吧。依据教训单表几百万的数量对于数据库是没什么压力的,那么只有分 256 张表就足够了,1.8 亿 /256≈70 万,如果为了保险起见,也能够分到 512 张表。那么考虑一下,如果业务量再增长 10 倍达到 1000 万单每天,分表 1024 就是比拟适合的抉择。

通过分表加上超过半年的数据归档之后,单表 70 万的数据就足以应答大部分场景了。接下来对订单号 hash,而后对 256 取模的就能够落到具体的哪张表了。

那么,因为惟一主键都是以订单号作为根据,以前你写的那些依据主键 ID 做查问的就不能用了,这就波及到了历史一些查问性能的批改。不过这都不是事儿对吧,都改成以订单号来查就行了。这都不是问题,问题在咱们的题目说的点上。

C 端查问

说了半天,总算到了正题了,那么分表之后查问和分页查问的问题怎么解决?

首先说带 shardingkey 的查问,比方就通过订单号查问,不论你分页还是怎么样都是能间接定位到具体的表来查问的,显然查问是不会有什么问题的。

如果不是 shardingkey 的话,下面举例说的以订单号作为 shardingkey 的话,像 APP、小程序这种个别都是通过用户 ID 查问,那这时候咱们通过订单号做的 sharding 怎么办?很多公司订单表间接用用户 ID 做 shardingkey,那么很简略,间接查就完了。那么订单号怎么办,一个很简略的方法就是在订单号上带上用户 ID 的属性。举个很简略的例子,本来 41 位的工夫戳你感觉用不完,用户 ID 是 10 位的,订单号的生成规定带上用户 ID,落具体表的时候依据订单号中 10 位用户 ID hash 取模,这样无论依据订单号还是用户 ID 查问成果都是一样的。

当然,这种形式只是举例,具体的订单号生成的规定,多少位,蕴含哪些因素依据本人的业务和实现机制来决定。

好,那么无论你是订单号还是用户 ID 作为 shardingkey,依照以上的两种形式都能够解决问题了。那么还有一个问题就是如果既不是订单号又不是用户 ID 查问怎么办?最直观的例子就是来自商户端或者后盾的查问,商户端都是以商户或者说卖家的 ID 作为查问条件来查的,后盾的查问条件可能就更简单了,像我碰到的有些后盾查问条件能有几十个,这怎么查???别急,接下来离开说 B 端和后盾的简单查问。

事实中真正的流量大头都是来自于用户端 C 端,所以实质上解决了用户端的问题,这个问题就解了大半,剩下来自商户卖家端 B 端、后盾反对经营业务的查问流量并不会很大,这个问题就好解。

其余端查问

针对 B 端的非 shardingkey 的查问有两个方法解决。

双写,双写就是下单的数据落两份,C 端和 B 端的各自保留一份,C 端用你能够用单号、用户 ID 做 shardingkey 都行,B 端就用商家卖家的 ID 作为 shardingkey 就好了。有些同学会说了,你双写不影响性能吗?因为对于 B 端来说轻微的提早是能够承受的,所以能够采取异步的形式去落 B 端订单。你想想你去淘宝买个货色下单了,卖家略微提早个一两秒收到这个订单的音讯有什么关系吗?你点个外卖商户晚一两秒收到这个订单有什么太大影响吗?

这是一个解决方案,另外一个计划就是走 离线数仓或者 ES查问,订单数据落库之后,不论你通过 binlog 还是 MQ 音讯的都模式,把数据同步到数仓或者 ES,他们反对的数量级对于这种查问条件来说就很简略了。同样这种形式必定是略微有提早的,然而这种可控范畴的提早是能够承受的。

而针对治理后盾的查问,比方经营、业务、产品须要看数据,他们人造须要简单的查问条件,同样走 ES 或者数仓都能够做失去。如果不必这个计划,又要不带 shardingkey 的分页查问,兄弟,这就只能扫全表查问聚合数据,而后手动做分页了,然而这样查出来的后果是有限度的。

比方你 256 个片,查问的时候循环扫描所有的分片,每个片取 20 条数据,最初聚合数据手工分页,那必然是不可能查到全量的数据的。

总结

分库分表后的查问问题,对于有教训的同学来说其实这个问题都晓得,然而我置信其实大部分同学做的业务可能都没来到这个数量级,分库分表可能都停留在概念阶段,面试被问到后就不知所措了,因为没有教训不晓得怎么办。

分库分表首先是基于现有的业务量和将来的增量做出判断,比方拼多多这种日单量 5000 万的,半年数据得有百亿级别了,那都得分到 4096 张表了对吧,然而理论的操作是一样的,对于你们的业务分 4096 那就没有必要了,依据业务做出正当的抉择。

对于基于 shardingkey 的查问咱们能够很简略的解决,对于非 shardingkey 的查问能够通过落双份数据和数仓、ES 的计划来解决,当然,如果分表后数据量很小的话,建好索引,扫全表查问其实也不是什么问题。

正文完
 0