关于分库分表的一点疑惑 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
irisdev
V2EX    数据库

关于分库分表的一点疑惑

  •  1
     
  •   irisdev 278 天前 4811 次点击
    这是一个创建于 278 天前的主题,其中的信息可能已经有所发展或是发生改变。
    平时做的项目都比较小,一般都是几台应用服务器+几台数据库服务器,虽然也有数据库拆分,但基本上都是把不同业务的表放在不同库中,比如一个 erp 系统,将财务相关的表放在某台服务器的某个库中,将仓库相关的表放在某台服务器的某个库中,没有拆分到更细的颗粒度,比如物料表,快上亿条数据了,也没有将这一张表拆分成不同表或者到不同库中。

    目前即使是这样也给写代码带来了一些麻烦,比如我负责生产模块,但是有时候要查一些物料数据,这时候就不可避免地要跨库查数据,这样我还可以理解。但是如果有一天物料表要拆分成两张表者到两个库中,那我写 sql 时岂不是要一样的语句写两遍然后 union 一下了?联想到那些大厂,比如淘宝,所有用户的“足迹”,就是浏览记录,至少得有百亿条数据了吧?这种业务是怎么处理的呢...应用服务器我可以理解,可以 cdn ,可以负载均衡,反正运行的是一套代码。但是数据库我就理解不了了,就算是一个简单的查询,“根据用户得到下单记录”,那也得全表查询吧?难不成这张订单表是单表?不现实。如果这张订单表被拆过,那淘宝的程序员在写代码的时候要考虑各个子库各个子表的情况吗?那不得累死。还是说“数据库集群”(只听过没概念...)可以像服务器集群一样屏蔽连接,程序员写代码就好了?不过在我理解中,服务器集群部署的是代码,转发到哪一台都可以,数据库集群里面的数据都是不一样的,那如果要屏蔽细节提供跟单体数据库一样的服务,内部肯定也通过网络通信然后统一提供给用户,这样感觉会不会对效率也有影响?感觉这些大公司的应用速度还是很快的,尤其是拼多多,感觉查订单记录就跟查几万几十万的表一样。

    因为这块懂得太少,表达地比较乱,也没法专业地表达,见谅..但意思应该表达出来了,这个疑问困扰我很久了,有没有明白的说两句,感谢
    48 条回复    2025-01-09 17:59:21 +08:00
    paopjian
        1
    paopjian  
       278 天前   1
    分库分表不是中间件干的活吗, 好多大价钱买中间件就是用来自动分库分表的
    irisdev
        2
    irisdev  
    OP
       278 天前 via Android
    @paopjian 还是在应用层解决吗,还以为数据库自带什么机制可以提供单体类似的数据库链接
    SethShi
        3
    SethShi  
       278 天前   2
    分实时和非实时吧, 你看订单都是最近 xxx 订单是查看的, 这部分进行分表|分区, 按照用户维度来划分, 太久的数据就直接存到另一个地方
    分表你可以简单的在代码层面分(通过用户 id hash), 不过现在很多云数据库都有中间件, 你连接的数据库并不是数据, 而是中间件, 然后中间件来帮你实现读写分离, 智能分表

    还有另一个方案就是, 订单数据冗余存储, 你给用户看的是一个分表, 给财务看的是另一个分表
    irisdev
        4
    irisdev  
    OP
       278 天前 via Android
    @seth19960929 明白了,谢谢。我把问题想简单了,没有一劳永逸的方案
    tanranran
        5
    tanranran  
       278 天前
    同好奇
    R18
        6
    R18  
       278 天前 via Android   3
    推荐一本书《数据密集型应用系统设计》
    luciankaltz
        7
    luciankaltz  
       278 天前   2
    1. 较早的数据库,例如 MySQL 这种对外提供的表概念就是最小化的逻辑单位的情况。通过特定的第三方中间件去实现分库分表,例如在数据库集群上包装入口,或者使用特定的数据库驱动库去链接数据库。本质上是通过特定的逻辑实现自动分库分表

    以查询用户的订单为例。设定所有用户的 UID 前两位或者前三位均匀分布在 00-99 或者 000-999 ,然后针对用户的订单(或者任意的用户数据)的操作必须带上用户 UID 信息。中间件拦截这个 UID 字段,并且自动解析,路由到对应的分库分表

    虽然使用上是几乎无感的,但是作为研发要知道实际下面可能数据路由到了不同的库表(甚至可能会路由到不同的物理数据库集群中)。这点在事务中要尤其注意

    2. 现代的数据库(?)在底下存储的时候,除了有表的概念,有些会进一步细分 region ,或者叫数据块的逻辑单位。数据实际存储在 region 中,表只是 region 的逻辑集合,region 通过主键自动或者手动进行划分,对数据进行分割存储

    相比于分库分表这样的作法理论上可以保持单表的无限扩容,并且可以保证数据在同一个数据库的逻辑概念中(不至于要分库)
    irisdev
        8
    irisdev  
    OP
       278 天前 via Android
    @luciankaltz 哥们你这是 ai 还是手打的。。我已经分不清了。现代数据库指分布式数据库吗
    luciankaltz
        9
    luciankaltz  
       278 天前 via Android   2
    @irisdev 当然是手打的,这哪有 ai 那味儿( xs

    现代数据库并不是一个精确的概念,只是我自己想的一个词,用来代指理念上设计更现代的的数据库,比如内置的 region 的设计?
    现代数据库也不等同于分布式,根据适用场景现代数据库也有单点(大部分现代分布式数据库都可以同时单点部署和分布式部署)和“嵌入式”数据库(比如有名的 duckdb )
    eleganceoo
        10
    eleganceoo  
       278 天前
    我是能不分就不分,复杂度嘎嘎增加,尽量采用归档,十亿分区,百亿分表
    smallparking
        11
    smallparking  
       278 天前 via Android
    数据库有分区表 把这些分表的逻辑写在数据库中了
    fds
        12
    fds  
       278 天前
    哦,我觉得可以问下 AI 或者搜索下,比如 https://www.oceanbase.com/solution/sharding
    dacapoday
        13
    dacapoday  
       278 天前
    @luciankaltz 这个 region 的概念会泄露到 SQL 中吗?是否相当于内置了中间件,有现代的数据库 的具体代表性产品 吗?
    afeiche
        14
    afeiche  
       278 天前
    @dacapoday 我知道的 tidb 是有 region 的
    Kumo31
        15
    Kumo31  
       278 天前   1
    @irisdev #2 分布式数据库基本都是透明 sharding 的
    dxddd
        16
    dxddd  
       278 天前   1
    1 代码实现 就是 sql 写各种 union all
    2 框架实现 相当于封装了一层,本质可能也是 union all
    3 中间件 相当于把框架抽出来独立做了一个服务
    4 数据库 几乎也是 union all
    我这里说的 union all 可能不限于是 sql 语句,就是表达一个汇总的含义。本质上就是把一块大资源拆成若干个小资源。以提升 IO 性能和查询性能。
    irisdev
        17
    irisdev  
    OP
       278 天前
    @dxddd 我一开始不理解这样"union"速度真的会快很多吗,因为在 sql 里面写 union 应该是串行查的。但是昨晚琢磨了一下,一个两千万的表,索引没建好可能查的很慢,但是拆成 5 个 200 万的表,,如果可以做到并行查,并行查然后汇总的话,也许某些场景下,查询是会快很多,但是如 2 楼所说,也不是什么银弹,并不是分布式或是分库分表就一定可以高效解决数据量大慢的问题,原理还是不太懂,但大概知道是这么个意思了,不知道理解对不对
    netizenHan
        18
    netizenHan  
       278 天前   1
    一般根据查询条件就能定位到对应的表了,比如 userId = 123 ,算法算完就能定位到是在 order 表的第五张上,所以最后还是单表查询,最后实际拼接的数据库的语句是 select * from order5 where xxxxx
    javak
        19
    javak  
       278 天前   1
    努力卷去大公司混两三年,你对这个世界的理解都不一样了
    irisdev
        20
    irisdev  
    OP
       278 天前
    @javak 刚毕业时大公司干过一段时间,不过干的前端。现在能力不够没机会去大公司了~
    crysislinux
        21
    crysislinux  
       278 天前 via Android   1
    我对这块也有类似的疑问。我自己的感觉是下面的实现很可能还真就是大力出奇迹,我们往往低估数据库的能力,就像我们容易从身边的生活经验去估计工业材料的强度一样。

    另外很多大型应用从 UX 设计上就会规避查询范围波及太多的库。
    ptg2008
        22
    ptg2008  
       278 天前   1
    对于分库分表,会根据唯一键通过算法(常用一致性哈希)将用户算到某个固定的库里面,这个用户的增删改查就只在这个库不会产生跨库事务(同一业务模块设计初要避免跨库事务);
    对于即席数据的查询,一般是将数据同步到 ES 或者 clickhouse 或者 trino 等用于做专门的查询对于冷数据,
    对于已经完成的订单或者交易公司内部基本都会有专门的归档平台,会将这些数据归档到冷库 做冷热分离, 你查询时会根据查询条件后端去冷库查询, 这时候就会比较慢点了,所以一般类似这种记录只会允许你查一年范围的
    dxddd
        23
    dxddd  
       278 天前   1
    @irisdev 原理就是空间/资源换时间。单一实例,因为 CPU 内存 操作系统的限制,无论是数据库还是 JAVA 应用,吞吐量都是有上限的。所以人们发明了各种拆分工具,比如一次请求,从 DNS 解析->NGINX->多实例和服务化治理->数据库主从/读写分离/分库分表,都是在干这个事情。
    wtml
        24
    wtml  
       278 天前   1
    不冲突啊,写入的时候可以根据数据取模进行分库分表,查询的时候也可以根据数据取模去各个库表里全都查出来,至于一些只有在单库单表里才能实现的功能,比如数据合并汇总这些,可以在查出来之后在代码逻辑里完成,无非多耗点内存的事,处理速度很快的
    sardina
        25
    sardina  
       278 天前   1
    分表分库的原则要用你们在业务里不会“交叉”的数据值做哈希来分,比如说你负责的生产模块,物料表应该需要有归属,比如属于某个 User ,就用 User 的 id 做哈希分,或者属于某个 Team 就用 team 的 id 做哈希去分
    ifuture
        26
    ifuture  
       278 天前   2
    本质上就是分而治之,分的话,可以多维度的分,可以用地区、时间或者哈希,反正目的就是保证最终落在每个数据库的数据规模是相对有限的,比如在生成用户 id 时候,可以携带地区编号和时间戳信息,这样通过用户 id 就能快速定位到对应的库表,对于订单表这种数据规模更大的数据表,订单本质上是基于用户产生,为了保证某个用户产生的订单都在一个库表里面,完全可以用户 id 哈希后获得对应的订单库表,然后进行订单数据的存储和查询
    Huelse
        27
    Huelse  
       278 天前
    本质上就是分机器,然后用中间件合起来,当作一个集群
    huguang3320
        28
    huguang3320  
       278 天前
    最近要做知识分享,也在学习分库分表,就像你说的,这个事不是一劳永逸的,涉及到事务的一致性,ID 一致性,还有就是在代码应用层上的聚合(不知道有些中间件是不是已经做好了),需要考虑的问题好像很多
    chinafengzhao
        29
    chinafengzhao  
       278 天前   1
    MIT 6.824 , 值得学一学
    kingwang
        30
    kingwang  
       278 天前
    如果我的业务全在存储过程里,各位又会如何应对?
    chutianyao
        31
    chutianyao  
       278 天前   1
    如果只是 sql 数据库的话, 两种方案: mycat 或者 tidb 都可以.
    mycat 有点老了, 是在分库上加了一个 porxy 层,跟使用单库没啥区别.我们订单、支付等很多库都是用它的, 稳定性还可以, 但高并发不建议使用.
    tidb 稳定性不太行,我们遇到好几次故障了,还查不到原因. 可用性要求不高的话可以使用
    chutianyao
        32
    chutianyao  
       278 天前   1
    不过很多场景,特别是复杂的条件搜索,不会直接查数据库, 而是通过 binlog,再存一份 es,使用 es 来存储查询.
    irisdev
        33
    irisdev  
    OP
       278 天前
    @chutianyao 老哥用 mycat 这种中间件会有事务问题吗,是不是一致性不太好保证,尤其高并发场景
    chutianyao
        34
    chutianyao  
       278 天前   1
    @irisdev 事务不垮库就没问题啊. 不知道你说的高并发是多大, 我们 qpm 约 3k 左右没啥问题.
    irisdev
        35
    irisdev  
    OP
       278 天前
    @chutianyao 懂了,谢谢
    promiser3d
        36
    promiser3d  
       278 天前
    @chutianyao tidb 这么菜吗?他们也做好了好多年了。
    heiya
        37
    heiya  
       278 天前   1
    1.分库分表是有一些中间件能做的,按照现有数据及未来规划把已有的一张大表均匀的分散到一些实际小表上(比如按照 id 取模分成 32 、64 、1024 张物理表),查询时只需要查逻辑表就可以了。
    2.分表之后可能会有效率问题,这取决于你的业务。有些查询很复杂,速度比较慢,流量一上来就会噶。这时候需要其他数据库对查询进行辅助,不走分表的数据。
    GBdG6clg2Jy17ua5
        38
    GBdG6clg2Jy17ua5  
       278 天前   1
    1.大表查询,面向客户端他就不会涉及到很复杂的查询。基本上用的是用户 id 做分库分表了,所以问题不会很大的。
    2.运营后台之类的做统计分析等复杂查询,数据可能后台算好结果的是 t+1 的。或者是多个分表冗余。
    最后总结,就是能不分库分表,坚决不要分。总体的开发复杂度会提高很多
    onepunch
        39
    onepunch  
       277 天前   1
    分享一下我的经验

    1. 数据规模较大可以创建一个库,仅保留最近一段时间的数据(几个月或一年),这样满足了部分数据查询的需求,又控制了整体数据的规模,能够保证查询的性能
    2. “下单记录”这样的功能一般都是默认查询当年,可以插叙指定年的数据。换句话说,历史数据一般都按年归档分表了
    3. 有分析数据的需求,一般数据库查不动,需要考虑其他数据库或者大数据方案
    4. 或者将物料相关接口内聚,独立成一个服务,对外提供接口查询数据。服务内部,再根据不同的场景选择不同的数据库来管理数据
    taoohe
        40
    taoohe  
       277 天前   1
    1. 一般都不跨库查询,所以要问自己是不是拆库和拆表是否合理
    2. 如果真的需要跨库一般解决方案是洗在 es 里查询。
    pangzipp
        41
    pangzipp  
       277 天前
    建议看下 mongodb 的 sharding 方案。
    antipro
        42
    antipro  
       277 天前 via Android
    数据库也可以上集群,出钱就行,统一存储,全闪存。
    irisdev
        43
    irisdev  
    OP
       277 天前
    我找到了一篇相当不错的文章解决了部分疑问: https://tech.meituan.com/2016/11/18/dianping-order-db-sharding.html
    night98
        44
    night98  
       277 天前   1
    现代分布式数据库已经实现你说的那些了,基本上可以直接当作单体数据库来用,除了查询的时候有些时候有一点小坑外,其他的和单体数据库区别已经不大了,所谓的分库分表基本上可以说是时代的眼泪了,再学习的意义不大,除了面试拿来吹牛有点用
    areless
        45
    areless  
       277 天前   1
    最简单的做法就是主键取模分表。基本上也能撑。但是 like 得用 ES 。基本这样也能撑。现在有更好得方案,可是还是以前的好理解,现在应该不再解释这些原理了,直接上操作就可以了。
    huzhizhao
        46
    huzhizhao  
       277 天前
    其实 如果你是 MySQL 或者 pg sql
    普通的业务,我理解做分区已经非常够用了。
    chutianyao
        47
    chutianyao  
       277 天前
    @promiser3d 反正我们遇到好几次了,还查不到原因, 差点把订单系统搞挂,是不敢在核心系统使用了
    promiser3d
        48
    promiser3d  
       276 天前
    @chutianyao 那你们现在是用的 mysql + 中间件的方案?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5542 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 07:19 PVG 15:19 LAX 00:19 JFK 03:19
    Do have faith in what you're doing.
    ubao 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