首页 > 代码库 > 订单表的分库分表方案设计

订单表的分库分表方案设计

 

 一般业界,对订单数据据笔者了解,有两类思路:

 

第一类:按照订单号来做hash计算。订单号看作是一个字符串,做hash,分散到多个服务器去。

 

具体到哪个库、哪个表存储数据呢?订单号里面的数字来记录着。比如微信红包,订单号末尾的3位数记录是哪个库、哪个表。

 

 

第二类:按照用户id作为切分的key,打散订单数据。

 

下面笔者,分析一下按照用户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改小一些。

 

 

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

 

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*一千万。哇,可以表示很多数据了。

 

 

 

思考:优点和缺点

 

优点

 

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

 

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

 

原因在于:

 

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

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

 

 

缺点

 

 这种方式也不是没有缺点。

 

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

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

 

  总结一下:每种分库分表方案也不是十全十美,都是有利有弊的。目前来说,这种使用用户id来切分订单数据的方案,还是被大部分公司给使用。实际效果还不错。程序员省事,至于数据量暴涨,以后再说呢。毕竟公司业务发展到什么程度,不知道的,项目存活期多久,未来不确定。先扛住再说。

 

比较好的方案是不是:又能均匀分散、又能避免单表数据量暴涨方便扩容。以前看过一篇文章介绍过使用节点来存储分库分表。笔者暂时没完整的思路。

 

 

后续一些考虑

 

在实际应用中一些很频繁的查询需求:

 

1、后台、前台,往往是输入一个订单号,查询这个订单的数据。select操作

2、然后修改这个订单的相关状态。update操作。

 

 

  既然是按照用户编号将订单数据分散在各个库、各个表中。

 

  那输入订单号,怎么知道去哪个库、哪个表查询呢?不可能所有的库、所有表都查询一遍吧,这样效率就太低了。绝对不可行。

 

  写到这里,发现真的没有一种技术方案是十全十美的,看,使用用户id来切分订单,好处是有了,坏处也出来了。

 

  不过没关系,早要有心里承受,不要觉得技术是完美无缺的。针对这种情况,想办法去解决办法。

 

   解决办法:建立用户id和订单号的索引关系表

 

      既然是根据用户id来分散订单数据的。那么只要知道了这个订单号是谁的(得到了用户id),就能知道去哪个库、哪个表查询数据了。

 

      那怎么知道是谁的呢?建立一个索引关系表,暂且叫做订单用户关系索引表order_user_idx。咱们命名为了保持维护性,还是一看能够知道是干嘛用的。

 

     存储的数据包括两项:订单号、用户编号。

 

     这样输入订单号,可以去查询索引关系表,获取到用户编号。

 

     得到了用户编号,问题解决了。订单信息是根据用户编号分库分表的,可以直接定位到x库x表了。

 

    

 

 

     订单用户索引关系表的性能优化

 

     

     考虑到,一个用户的下的订单可能是几十个,也可能是几百个,随着时间的推移,会越来越多。这个索引关系表,也不能使用单表存储。

 

     所以对这个订单用户关系索引表,也要进行分库分表:直接根据订单号取模进行分库分表。是不是感觉挺麻烦了。确实麻烦。不过能解决问题就好。暂时没想到其他办法了。

 

    一个订单,在创建的时候,就已经分配好给指定用户了。只是一个关系对应,以后也不会变化。

 

    根据这个特点。订单用户索引关系表,其实可以放到内存中缓存起来应对查询需求(数据库那张索引关系表也要有,数据要持久化)。

 

    平时查询的时候,走内存缓存查询。如果查询不到,再走数据库查询一下关系。这样速度就很快了。

 

 

 

    结语:水平分表,其实折腾起来工作量挺大的,切分了后,出现新的问题,代码查询又得改,要提供其他解决办法。所以经常看到别人说,能不水平分表,尽量不要分。看银行、联通这些有钱的企业,使用性能强劲的oracle搭配小型机服务器,单表的数据量达到十多亿。小型机是专门定制的,几十万一台。性能很强。

 

订单表的分库分表方案设计