按秒进行分库分表是个好的选择吗?

2022-10-12 11:33:04 +08:00
 RedisMasterNode

背景

有个服务要生成唯一 ID (支持批量),后续的查询(仅支持单条)也按照这个唯一 ID 来查,因此毫无疑问分库分表依据只能是这个唯一 ID 。

这个唯一 ID 可以看作雪花算法,里面有比如机器号码、时间戳、自增 ID 等信息(可以反解出来)。

问题

设计分库分表方法的时候,考虑了以下的方案:

  1. 均匀分布,例如按照 hash1(唯一 ID) % 10 来分库,按 hash2(唯一 ID) % 100 来分表。这样的好处是数据都会均匀,没有什么热点的问题。但是后来又觉得,因为生成的时候是批量生成的,例如生成了 200 个 ID 想写进表里,那如果全都是 hash 的,就会需要写多个库、多个表,而且很大可能没办法合并(因为是完全均匀的)。

  2. 为了减少事务数量,又想了个办法按照 "唯一 ID 中的时间戳+机器号码" % 10 来分库,这样同一台机器每秒内生成的 ID 都可以落到相同相同的 DB ,只用执行 1 次事务 INSERT 。

  3. 再后来又想把对表的写入也能批量完成,因为按照方案 2 ,如果分表的依据依然是 hash2(唯一 ID)%100 ,那有可能是在一个事务里面要写很多条 INSERT 到不同的表,效率也不搞。所以就想把分表的方案改成 hash2(唯一 ID 中的时间戳),这样同一秒内生成的所有唯一 ID 都会落入相同的表了。

现在的实现是第三种方案,但是在表维度来说,短时间内会有热点问题(但是拉长了看依然是均匀的),想问下有经验的老哥,这种场景有风险、有更好的方案吗( MySQL ,且暂不考虑替换其他中间件、数据库的方案)?

PS: 写入量可以暂且预估为每天要写入几千万行左右,数据量不大。

2626 次点击
所在节点    数据库
15 条回复
soupu626
2022-10-12 12:04:01 +08:00
看你的业务属性,唯一 id 的生成规则可以改下,前面还是雪花,然后尾部上加用户 id 后几位位或者租户 id 后几位或者你们业务划分的字段的后几位,然后按用户 /租户 id 取模来分库分表,这样保证同一业务属性的数据落在同一个库 /表里,按时间分的话,表太不均匀了吧,高峰数据很多,半夜基本没有数据
RedisMasterNode
2022-10-12 12:39:26 +08:00
@soupu626 这个已经不能改了,背景里应该补充一下是要把原来没分库分表的数据拆到分库分表里面。

而且按时间分的话为何会不均匀呢,因为每秒的请求量应该都是相对均匀的呀,你说的这个时间粒度听起来好像比较宽了,不是按秒的
seth19960929
2022-10-12 13:38:49 +08:00
很简单, 不要分库分表. 直接按照 ID 分区. 可以去看一下 MySQL 的分区
RedisMasterNode
2022-10-12 14:30:21 +08:00
@seth19960929 是个合理的办法,但是这些东西不受业务研发管控,DBA 说不允许用,就是不允许了,只能自己想办法。
soupu626
2022-10-12 14:59:07 +08:00
@RedisMasterNode 除非是物联网类的机器上报数据,不然 qps 肯定是会波动的啊白天多晚上少,那么白天的秒对应的那些表里的数据肯定比晚上的秒对应的表里数据多啊
Maboroshii
2022-10-12 15:04:46 +08:00
第一种吧,批量生成的时候,分组合并插入,搞简单点。数据库的插入性能没有那么不堪,只管用
RedisMasterNode
2022-10-12 15:37:16 +08:00
@soupu626 没理解,早上和晚上分表都要 % 分表数量的呀

例如有 100 个表,第 1 秒的话,1 % 100 = 1 ,所以落在第一个表,第 101 秒 101 % 100 = 1 ,也落在第 1 个表。

这个具体怎么样会受到时间影响呢
RedisMasterNode
2022-10-12 15:39:05 +08:00
这个很难合并呀,例如我的接口的 limit 是 200 ,按照 hash 生成的话,再分组,其实每张表基本都要写入,分组每个组可能也就只有 1-3 条数据。

MySQL 性能是 OK 的但是 for 循环里面 insert 会导致 P50 P90 P99 大幅上涨
zmal
2022-10-13 10:14:11 +08:00
分库分表策略,和分表后的插入效率,是两个问题,不要混在一起处理。
批量插入效率的担忧有其他方式可以解决,但数据库设计后期很难打补丁了。
RedisMasterNode
2022-10-13 10:32:30 +08:00
@zmal 批量插入效率的担忧有其他方式可以解决

这个具体是指比如什么方案?
zmal
2022-10-13 10:39:14 +08:00
比如消息队列异步,做一个单独的服务来消费,这个服务可以根据数据量任意扩容缩容,不用改动主服务。主服务写入消息队列后就返回。很常规的方案了。
RedisMasterNode
2022-10-13 12:33:40 +08:00
@zmal 我们这个是 2C 的服务呀,这些方案面对延迟敏感的服务怎么能落地呢。那你延迟写入,用户怎么去访问呢,是不是还要兜底
zmal
2022-10-13 17:40:17 +08:00
异步方案并不意味着延迟啊,下游消费跟得上就没有延迟。
如果消息产生了积压,你不异步的方案估计已经挂了。
RedisMasterNode
2022-10-13 17:57:19 +08:00
@zmal 不行,写入不行挂掉就是符合预期的表现,银行业务,不能这样做。

“下游消费跟得上就没有延迟” 这个思路在系统设计里面绝对是有问题的吧,为什么引入一个额外流程能描述为 “如果 xxx 跟得上,那就没有 xxx”。

在设计时你应该认定 “如果引入异步,那它一定会(或早或晚)造成延迟”,造成延迟时会如何影响我的业务,这样考虑事情才对的呀,不能想当然,认为我把消费者处理得足够高性能足够快,就能解决这个问题。
zmal
2022-10-13 18:10:08 +08:00
你的业务场景描述的不够清晰,日写入量多少? qps 多少?
按你目前的需求,举个例子,不一定精准:你可以按天分表,几千万数据 1 天 1 张表也是完全 ok 的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/886317

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX