苦于写了很多重复代码,想了解一下大家在 Go 的项目里面是怎么用 GORM (或者其他 MySQL 客户端,不限 ORM / Raw SQL )来实现分表读写的?
例如 user_tab_00000000
- user_tab_00000199
,一共 200 张表,按 user_id % 200
来分表。
了解过的方案:
CreateUser()
、GetUserByUserID()
、UpdateUserByUserID()
SELECT * FROM user_tab WHERE user_id = 199
最终会被改写成 SELECT * FROM user_tab_00000199 WHERE user_id = 199
发往 MySQL ;个人感觉方案 2 是个挺好的思路但是 Go 里面好像没有什么特别好用的中间件。
1 dobelee 2023-03-21 11:14:06 +08:00 存储层手动调不同的读写实例。稳健。 |
2 p23XnFNH1Wq953rV 2023-03-21 11:17:46 +08:00 如果可以的话, 让业务同学更专心写业务吧, 分表这种交给运维 如果没条件, 只能自己搞, 2 更好一些 |
![]() | 3 RedisMasterNode OP @dobelee 我们也是这么做的,确实稳健,所有的流程都能在 debug 的时候看清楚,其实就是相当于嵌入业务了,能感知到,坏处在于多加几行代码,库表越多,写得越多 |
4 dobelee 2023-03-21 11:26:35 +08:00 @RedisMasterNode 也不用加什么代码,微服务下单个服务一般不会管理太多库表。相反我认为下层自动读写分离反而不太好,如特殊情况就得在主库做查询呢?脱离开发控制了。还是看实际情况吧,没遇到什么问题维持现状即可。 |
![]() | 5 RedisMasterNode OP @dobelee 在理 |
![]() | 6 直接上分布式的数据库 |
![]() | 7 RedisMasterNode OP @Nooooobycat 也是个解决方案 |
![]() | 8 zhongjun96 2023-03-21 11:33:41 +08:00 好奇分这么多表,列表查询怎么做的? |
9 EAFA0 2023-03-21 11:43:37 +08:00 在 grom 上单写一个 table 方法, 接收相关分表参数, 返回一个 gorm.DB 对象, 然后每次都调用 table 方法获取对应 gorm.DB 对象就好了 类似于 ``` golang func (d UserDao) table(userID int64) *gorm.DB { return mysql.Client("user").Table(fmt.Sprintf("user_table_%d", userID%1000)) } ``` |
![]() | 10 xuanbg 2023-03-21 11:56:54 +08:00 分库:配置中心实现不同服务实例连不同的库 分表:程序中按规则拼表名 简单粗暴,但效果非常好。 |
![]() | 11 RedisMasterNode OP @zhongjun96 一般有扫表的方案和异构存储 /离线数仓的方案;另外在决定分表之前也是需要考虑业务是否要频繁的批量查询,如果非常多批量操作的话,设计的时候要看能不能把分表依据和批量操作的依据结合起来,或者不用分表的方案实现 |
![]() | 12 RedisMasterNode OP @EAFA0 嗯嗯是个方案,我们现在也在用这样的写法 |
![]() | 13 noparking188 2023-03-21 12:31:13 +08:00 听播客,PingCAP 的 CTO 说他也遇到这种问题,然后就开发了 TiDB |
14 zeonll 2023-03-21 12:56:10 +08:00 @zhongjun96 一个用户的数据一般都在一个表内。跨用户查询的 case 比较少吧 |
![]() | 15 OutOfMemoryError 2023-03-21 12:58:14 +08:00 mysql 的分区表,直接从数据库引擎层解决 |
![]() | 16 Famio 2023-03-21 13:31:35 +08:00 为啥不用分区表搞啊,还要代码逻辑去判断有点。。。 ALTER TABLE table_name PARTITION BY RANGE COLUMNS(userid) ( PARTITION p1 VALUES LESS THAN (200), PARTITION p2 VALUES LESS THAN (400), PARTITION p3 VALUES LESS THAN (600), PARTITION p4 VALUES LESS THAN (MAXVALUE) ); 多香 |
18 kiddingU 2023-03-21 14:23:44 +08:00 gorm 好像有 db resolve 插件,这玩意自己写也比较方便吧 |
![]() | 19 ZeroDu 2023-03-21 14:24:13 +08:00 方案 2 吧 ShardingSphere 就是实现的这种;直接在 jdbc 层动手脚,跟 orm 框架无关;至于 go 这边就不清楚了; 或者直接上分布式数据库 |
![]() | 20 echo1937 2023-03-21 14:48:16 +08:00 前排求推荐开源的分布式 OLTP 数据库,大家用哪个比较多? |
21 thinkingbullet 2023-03-21 15:08:28 +08:00 https://github.com/flike/kingshard 这个应该是你要找的解决方案虽然我没用过 |
![]() | 22 samin 2023-03-21 15:20:17 +08:00 2 、3 是一样吧 区别在于造轮子还是用现成的工具 最近看了一个帖子 “分库分表必死,分布式数据库必成主流” 意思就是分库分表以后会成为数据库产品自身特性而不是业务系统需要去考虑的 针对题主目前情况 还是建议用 2 灵活些 |
24 xiaocaiji111 2023-03-21 15:56:41 +08:00 没有比较成熟得,毕竟 go 生态摆在这,建议使用分布式数据库,或者使用云厂商服务比如 DRDS |
![]() | 25 liuxu 2023-03-21 16:32:49 +08:00 其实 mysql 的分区表还是很好用的 |
![]() | 27 kongkongyzt 2023-03-21 16:55:40 +08:00 我们最开始用的 1 后面用的 2 之后改用 3 最后用了腾讯云上一款对标 TiDB 的产品 |
![]() | 28 lance6716 2023-03-21 16:57:23 +08:00 via Android |
![]() | 29 zhongjun96 2023-03-21 18:08:46 +08:00 @zeonll #14 水平分表,比如题主的 “例如 user_tab_00000000 - user_tab_00000199 ,一共 200 张表,按 user_id % 200 来分表。” 假设 id 递增,需要查询最近新增的 200 个用户,那不就是每一张表都要查询。 这种情况怎么处理的? |
![]() | 30 RedisMasterNode OP |
![]() | 31 RedisMasterNode OP @kiddingU resolver 只解决连不同 db 的问题吧,好像不是解决分表的问题的 |
![]() | 32 RedisMasterNode OP @thinkingbullet Nice to hear. 不管好不好用,可以了解一下,感谢~ |
![]() | 33 RedisMasterNode OP @Famio @OutOfMemoryError 对了还有一个原因是因为有些 sharding 算法并不是简单的取余,也是因为业务的特殊原因,需要实现一些奇奇怪怪的内容进去,所以不能只依靠 partition |
![]() | 34 dreamramon 2023-03-21 19:52:18 +08:00 直接上 ShardingSphere |
![]() | 35 dreamramon 2023-03-21 19:57:05 +08:00 或者直接 yugabytedb ,sharding 非常容易,一个命令的事情。 |
36 zeonll 2023-03-22 17:06:04 +08:00 @zhongjun96 这种类型的查询应该不会是 toC 量级的 qps 吧,如果次数不多就遍历呗,如果是相对高频(比如营销平台),那就搞离线数据库,汇总数据 |
![]() | 38 RedisMasterNode OP @zhongjun96 binlog 同步到 clickhouse 或者 es 直接查询,相当于用双倍的空间分别支撑 OLTP 和 OLTP 查询,如果能接受较长延迟 /执行耗时,或者说有超大批量查询要求的话应该转移到离线数仓去。 |