• 关于电商订单系统分库分表


    一.需求

    一般业界,对订单数据的分库分表,有两类思路:按照订单号来切分、按照用户id来切分。

    二.按照订单号来做 hash分散订单数据  

    把订单号看作是一个字符串,做 hash,分散到多个服务器去。

    具体到哪个库、哪个表存储数据呢?订单号里面的数字来记录着。

    如果要查询某用户的所有订单呢?

    由于是根据订单号来分散数据的。他的订单分散在了多个库、多个表中。

    总不能去所有的库,所有的表扫描吧。这样效率很低。

    解决:

    维护uid和oid的关系表, 此表可以作为缓存,当数据量增大时此关系表也要进行分表。

    一般使用方案二的比较多,一个用户的所有订单,都在一张表里面,那么做分页展示的时候,就容易。

    三.按照用户 id 打散订单数据

    以uid来切分数据,有两种思路:

    1、第一种

    某个范围的uid订单到哪些库。0到2千万uid,对应的订单数据到a库、a表。2千万到4千万对应的订单到b库。

    为什么这种方案用得比较少呢?容易出现瓶颈。

    某个范围内的用户,下单量比较多,那么造成这个库的压力特别大。其他库却没什么压力。


    2、第二种

    使用uid取模运算。这种方案业界用得比较多。

    一方面、处理简单,程序上做取模运算就好了。

    另一方面、使用取模的方式,数据比较均匀分散到多个库去了。不容易出现单个库性能瓶颈。

    但是不好处也有:即要扩容的时候,比较麻烦。就需要迁移数据了。

    要扩容的时候,为了减少迁移的数据量,一般扩容是以倍数的形式增加。比如原来是8个库,扩容的时候,就要增加到16个库,再次扩容,就增加到32个库。

    这样迁移的数据量,就小很多了。这个问题不算很大问题,毕竟一次扩容,可以保证比较长的时间,而且使用倍数增加的方式,已经减少了数据迁移量。


    下面分析一下按照用户id取模的方式分库分表。按照用户id作为key来切分订单数据,

    具体如下:

    (1)库名称定位:

    用户id末尾4位 Mod 32。

    Mod表示除以一个数后,取余下的数。比如除以32后,余下8,余数就是8。代码符号是用%表示:15%4=3。

    (2)表名称定位:

    (用户id末尾4位 Dev 32) Mod 32。

    Dev表示除以一个数,取结果的整数。比如得到结果是25.6,取整就是25。

    代码用/来表示:$get_int = floor(15/4)。15除以4,是一个小数3.75,向下取整就是3。一定是向下取整,向上取整就变成了4了。

    按照上面的规则:总共可以表示多少张表呢?32个库*每个库32个表=1024张表。如果想表的数量小,就把32改小一些。

    库ID = userId % 库数量4

    表ID = userId / 库数量4 % 表数量8

    或者

    库ID = userId / 表数量4 % 库数量4

    表ID = userId % 表数量8


    上面是用计算机术语来表示, 下面用通俗的话描述。

    (1)库名称计算

    用户id的后4位数,取32的模(取模就是除以这个数后,余多少)。余下的数,是0-31之间。

    这样可以表示从0-31之间,总共32个数字。用这个32个数字代表着32个库名称:order_db_0、order_db_2.........................order_db_31

    (2)表名称计算

    最后要存储定到哪个表名里面去呢?

    用户id的最后4位数,除以32,取整数。将整数除以32,得到余数,能够表示从0-31之间32个数字,表示表名称。

    表名称类似这样:order_tb_1、order_tb_2..........................order_tb_31。一个库里面,总共32个表名称。

    比如用户id:19408064,用最后4位数字8064除以32,得到是251.9,取它的整数是251。

    接着将251除以32,取余数,余数为27。

    为了保持性能,每张表的数据量要控制。单表可以维持在一千万-5千万行的数据。1024*一千万。哇,可以表示很多数据了。

    四.总结

    1、优点

    订单水平分库分表,为什么要按照用户id来切分呢?

    好处:查询指定用户的所有订单,避免了跨库跨表查询。

    因为,根据一个用户的id来计算节点,用户的id是规定不变的,那么计算出的值永远是固定的(x库的x表)。

    那么保存订单的时候,a用户的所有订单,都是在x库x表里面。需要查询a用户所有订单时,就不用进行跨库、跨表去查询了。

    2、缺点

    缺点在于:数据分散不均匀,某些表的数据量特别大,某些表的数据量很小。因为某些用户下单量多,打个比方,1000-2000这个范围内的用户,下单特别多,

    而他们的id根据计算规则,都是分到了x库x表。造成这个表的数据量大,单表的数据量撑到极限后,咋办呢?

    总结一下:每种分库分表方案也不是十全十美,都是有利有弊的。目前来说,这种使用用户id来切分订单数据的方案,还是被大部分公司给使用。

    实际效果还不错。程序员省事,至于数据量暴涨,以后再说呢。毕竟公司业务发展到什么程度,不知道的,项目存活期多久,未来不确定。先扛住再说。

    3、问题

    b2b平台的订单分卖家和买家的时候,选择什么字段来分库分表呢?

    上面讨论的情况是,b2c平台。订单的卖家就一个,就是平台自己。

    b2b平台,上面支持开店,买家和卖家都要能够登陆看到自己的订单。

    先来看看,分表使用买家id分库分表和根据卖家id分库分表,两种办法出现的问题。

    如果按买家id来分库分表。有卖家的商品,会有n个用户购买,他所有的订单,会分散到多个库多个表中去了,卖家查询自己的所有订单,跨库、跨表扫描,性能低下。

    如果按卖家id分库分表。买家会在n个店铺下单。订单就会分散在多个库、多个表中。买家查询自己所有订单,同样要去所有的库、所有的表搜索,性能低下。

    所以,无论是按照买家id切分订单表,还是按照卖家id切分订单表。两边都不讨好。

    淘宝的做法是拆分买家库和卖家库,也就是两个库:买家库、卖家库。

    买家库,按照用户的id来分库分表。卖家库,按照卖家的id来分库分表。

    实际上是通过数据冗余解决的:一个订单,在买家库里面有,在卖家库里面也存储了一份。

    下订单的时候,要写两份数据。先把订单写入买家库里面去,然后通过消息中间件来同步订单数据到卖家库里面去。

  • 相关阅读:
    让开发效率“飞起”的VS Code 插件
    转-webpack学习笔记--整体配置结构
    十二、vue中watch原理
    十一、vue生命周期诠释--带图
    十、vue mixins 的用法
    八、Web移动端Fixed布局的解决方案
    七、vue中v-for有时候对页面不会重新渲染,数组变化后如何到渲染页面
    六、vue如何缓存页面
    五、vue常用UI组件
    vue组件递归
  • 原文地址:https://www.cnblogs.com/ZJOE80/p/12238373.html
Copyright © 2020-2023  润新知