你们在 Go 项目中如何实现分表读写的?插件?手写?代理? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
RedisMasterNode
V2EX    Go 编程语言
你们在 Go 项目中如何实现分表读写的?插件?手写?代理?
  •  1
     
  •   RedisMasterNode 2023-03-21 11:04:47 +08:00 5138 次点击
    这是一个创建于 945 天前的主题,其中的信息可能已经有所发展或是发生改变。

    苦于写了很多重复代码,想了解一下大家在 Go 的项目里面是怎么用 GORM (或者其他 MySQL 客户端,不限 ORM / Raw SQL )来实现分表读写的?

    例如 user_tab_00000000 - user_tab_00000199,一共 200 张表,按 user_id % 200 来分表。

    了解过的方案:

    1. DAO 层(或操作 DB 层)时根据传入参数计算,相关代码会出现在每个 Query 方法,例如 CreateUser()GetUserByUserID()UpdateUserByUserID()
    2. 挂载插件,init 时定义、注册分表字段、分表算法等等,DAO 层无特殊处理(就像在操作单表一样),实际执行的时候被改写,例如应用里执行 SELECT * FROM user_tab WHERE user_id = 199 最终会被改写成 SELECT * FROM user_tab_00000199 WHERE user_id = 199 发往 MySQL ;
    3. 应用层外挂载 MySQL 的代理,类似 ShardingSphere 这种,流程跟方案 2 类似,只是改写由外部中间件完成。

    个人感觉方案 2 是个挺好的思路但是 Go 里面好像没有什么特别好用的中间件。

    38 条回复    2023-03-22 20:41:59 +08:00
    dobelee
        1
    dobelee  
       2023-03-21 11:14:06 +08:00
    存储层手动调不同的读写实例。稳健。
    p23XnFNH1Wq953rV
        2
    p23XnFNH1Wq953rV  
       2023-03-21 11:17:46 +08:00
    如果可以的话, 让业务同学更专心写业务吧, 分表这种交给运维
    如果没条件, 只能自己搞, 2 更好一些
    RedisMasterNode
        3
    RedisMasterNode  
    OP
       2023-03-21 11:19:02 +08:00
    @dobelee 我们也是这么做的,确实稳健,所有的流程都能在 debug 的时候看清楚,其实就是相当于嵌入业务了,能感知到,坏处在于多加几行代码,库表越多,写得越多
    dobelee
        4
    dobelee  
       2023-03-21 11:26:35 +08:00
    @RedisMasterNode 也不用加什么代码,微服务下单个服务一般不会管理太多库表。相反我认为下层自动读写分离反而不太好,如特殊情况就得在主库做查询呢?脱离开发控制了。还是看实际情况吧,没遇到什么问题维持现状即可。
    RedisMasterNode
        5
    RedisMasterNode  
    OP
       2023-03-21 11:27:35 +08:00
    @dobelee 在理
    Nooooobycat
        6
    Nooooobycat  
       2023-03-21 11:31:31 +08:00
    直接上分布式的数据库
    RedisMasterNode
        7
    RedisMasterNode  
    OP
       2023-03-21 11:32:07 +08:00
    @Nooooobycat 也是个解决方案
    zhongjun96
        8
    zhongjun96  
       2023-03-21 11:33:41 +08:00
    好奇分这么多表,列表查询怎么做的?
    EAFA0
        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))
    }

    ```
    xuanbg
        10
    xuanbg  
       2023-03-21 11:56:54 +08:00
    分库:配置中心实现不同服务实例连不同的库
    分表:程序中按规则拼表名

    简单粗暴,但效果非常好。
    RedisMasterNode
        11
    RedisMasterNode  
    OP
       2023-03-21 12:18:30 +08:00 via Android
    @zhongjun96 一般有扫表的方案和异构存储 /离线数仓的方案;另外在决定分表之前也是需要考虑业务是否要频繁的批量查询,如果非常多批量操作的话,设计的时候要看能不能把分表依据和批量操作的依据结合起来,或者不用分表的方案实现
    RedisMasterNode
        12
    RedisMasterNode  
    OP
       2023-03-21 12:18:58 +08:00 via Android
    @EAFA0 嗯嗯是个方案,我们现在也在用这样的写法
    noparking188
        13
    noparking188  
       2023-03-21 12:31:13 +08:00
    听播客,PingCAP 的 CTO 说他也遇到这种问题,然后就开发了 TiDB
    zeonll
        14
    zeonll  
       2023-03-21 12:56:10 +08:00
    @zhongjun96 一个用户的数据一般都在一个表内。跨用户查询的 case 比较少吧
    OutOfMemoryError
        15
    OutOfMemoryError  
       2023-03-21 12:58:14 +08:00
    mysql 的分区表,直接从数据库引擎层解决
    Famio
        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)
    );

    多香
    lopssh
        17
    lopssh  
       2023-03-21 14:16:07 +08:00
    @EAFA0 咋扩容?如果后面扩展到了 999 个表。。。你这个 UserID 算出来的结果不一致了咋办。
    kiddingU
        18
    kiddingU  
       2023-03-21 14:23:44 +08:00
    gorm 好像有 db resolve 插件,这玩意自己写也比较方便吧
    ZeroDu
        19
    ZeroDu  
       2023-03-21 14:24:13 +08:00
    方案 2 吧 ShardingSphere 就是实现的这种;直接在 jdbc 层动手脚,跟 orm 框架无关;至于 go 这边就不清楚了;

    或者直接上分布式数据库
    echo1937
        20
    echo1937  
       2023-03-21 14:48:16 +08:00
    前排求推荐开源的分布式 OLTP 数据库,大家用哪个比较多?
    thinkingbullet
        21
    thinkingbullet  
       2023-03-21 15:08:28 +08:00
    https://github.com/flike/kingshard 这个应该是你要找的解决方案虽然我没用过
    samin
        22
    samin  
       2023-03-21 15:20:17 +08:00
    2 、3 是一样吧 区别在于造轮子还是用现成的工具

    最近看了一个帖子 “分库分表必死,分布式数据库必成主流” 意思就是分库分表以后会成为数据库产品自身特性而不是业务系统需要去考虑的

    针对题主目前情况 还是建议用 2 灵活些
    EAFA0
        23
    EAFA0  
       2023-03-21 15:27:32 +08:00
    @lopssh 我们目前是直接分千表, 短期内用户不大量增长的话都够用了
    如果需要频繁扩容的话确实不太好处理
    xiaocaiji111
        24
    xiaocaiji111  
       2023-03-21 15:56:41 +08:00
    没有比较成熟得,毕竟 go 生态摆在这,建议使用分布式数据库,或者使用云厂商服务比如 DRDS
    liuxu
        25
    liuxu  
       2023-03-21 16:32:49 +08:00
    其实 mysql 的分区表还是很好用的
    poembre
        26
    poembre  
       2023-03-21 16:52:21 +08:00
    @liuxu
    @Famio
    分区字段 有个限制 必须主键
    kongkongyzt
        27
    kongkongyzt  
       2023-03-21 16:55:40 +08:00
    我们最开始用的 1
    后面用的 2
    之后改用 3
    最后用了腾讯云上一款对标 TiDB 的产品
    lance6716
        28
    lance6716  
       2023-03-21 16:57:23 +08:00 via Android
    zhongjun96
        29
    zhongjun96  
       2023-03-21 18:08:46 +08:00
    @zeonll #14 水平分表,比如题主的 “例如 user_tab_00000000 - user_tab_00000199 ,一共 200 张表,按 user_id % 200 来分表。”
    假设 id 递增,需要查询最近新增的 200 个用户,那不就是每一张表都要查询。
    这种情况怎么处理的?
    RedisMasterNode
        30
    RedisMasterNode  
    OP
       2023-03-21 18:46:29 +08:00
    @Famio
    @OutOfMemoryError
    有历史原因的 Partition 有些场景不让使用(不管它是否好用,但是出于某些原因确实是不能使用)
    RedisMasterNode
        31
    RedisMasterNode  
    OP
       2023-03-21 18:47:05 +08:00
    @kiddingU resolver 只解决连不同 db 的问题吧,好像不是解决分表的问题的
    RedisMasterNode
        32
    RedisMasterNode  
    OP
       2023-03-21 18:47:58 +08:00
    @thinkingbullet Nice to hear. 不管好不好用,可以了解一下,感谢~
    RedisMasterNode
        33
    RedisMasterNode  
    OP
       2023-03-21 18:54:28 +08:00
    @Famio @OutOfMemoryError 对了还有一个原因是因为有些 sharding 算法并不是简单的取余,也是因为业务的特殊原因,需要实现一些奇奇怪怪的内容进去,所以不能只依靠 partition
    dreamramon
        34
    dreamramon  
       2023-03-21 19:52:18 +08:00
    直接上 ShardingSphere
    dreamramon
        35
    dreamramon  
       2023-03-21 19:57:05 +08:00
    或者直接 yugabytedb ,sharding 非常容易,一个命令的事情。
    zeonll
        36
    zeonll  
       2023-03-22 17:06:04 +08:00
    @zhongjun96 这种类型的查询应该不会是 toC 量级的 qps 吧,如果次数不多就遍历呗,如果是相对高频(比如营销平台),那就搞离线数据库,汇总数据
    S1ngleton
        37
    S1ngleton  
       2023-03-22 20:00:15 +08:00 via iPhone
    @lopssh 一致性 hash
    RedisMasterNode
        38
    RedisMasterNode  
    OP
       2023-03-22 20:41:59 +08:00
    @zhongjun96 binlog 同步到 clickhouse 或者 es 直接查询,相当于用双倍的空间分别支撑 OLTP 和 OLTP 查询,如果能接受较长延迟 /执行耗时,或者说有超大批量查询要求的话应该转移到离线数仓去。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3747 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 00:50 PVG 08:50 LAX 17:50 JFK 20:50
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86