(1)概述
    本文仅仅是介绍分库分表的整体方案的设计,在进行具体的方案落地的时候,是需要数据库中间件技术的支持的,业内常用的一般有Sharding-Sphere以及Mycat,都比较成熟,可以自行选择 一个数据库中间件技术,关注一下官网文档。这两个都是国内开源的,都是有中文的,当有了分库分表的技术积累后,就可以直接进行本文的分库分表的案例了。

    (2)背景
    介绍平时见到的互联网公司的海量用户数据的分库分表的方案,其实任何一个互联网公司都会有用户中心,这个用户中心就是负责这家公司的所有用户的数据管理,包括用户的数据存储, 用户信息的增删改查,用户的注册登录之类的。而且一般互联网公司都是面向终端用户的,所以用户数据一般都很多,今天的案例选择一个背景是一个中型的电商公司,覆盖的用户大概一亿以内, 大概几千万吧。

    (3)过程
    一般面对几千万级的数据,刚开始可能都是把数据放在MySQL的一个单库单表里的,往往这么大量级的数据到了后期,后续数据库的查询速度很慢,因为数据量级太大会导致表的索引很大, 树的层级很高,进而导致搜索性能下降,而且能放内存缓存的数据页是比较少的。
    所以往往都建议MySQL单表数据量不要超过1000万,最好在500万以内,如果控制在100万以内,那是最佳选择,基本单表100万以内的数据,性能上不会有太大问题,前提是只要建好索引,保证 MySQL高性能通常没什特别高深的技巧,就是控制数据量不要太大,另外就是保证查询用上索引,一般就没问题。

    所以针对这个问题就可以进行分库分表了,可以选择把这个用户大表拆分成比如100张表,那么此时几千万数据瞬间分散到100个表里去,类似user_001,user_002,user_100 这样的100个表, 每个表也就几十万数据而已。其次可以把100个表分散到多台数据库服务器上去,此时要分散到几台服务器?要考虑两点:一个是数据量有多少个GB/TB,一个是针对用户中心的并发压力有多高。实际上 一般互联网公司对用户中心的压力不会高的太离谱,因为一般不会有很多人同时注册/登录,或者是同时修改自己的个人信息,所以并发这块不是太大问题。
    至于数据层面,有一个经验值,一般一亿行数据,大致在1GB到几个GB之间的范围,这个跟具体一行数据多少个字段有关系,大致是这个范围,所以说几千万的用户数据,往多了说也就几个GB 而已。这点数据量,对于服务器的存储空间来说,完全没压力,不是问题。

    (3)总结
    完全可以分配两台数据库服务器,放两个库,然后100张表均匀分散在2个服务器上就可以了,分的时候需要指定一个字段来分,一般来说指定userid,根据用户id进行hash后,对表进行取模, 路由到一个表里去,这样可以让数据均匀分散。
    到此就搞定了用户表的分库分表,只要给系统加上数据库中间件技术,设置好路由规则,就可以轻松的对2个分库上的100张表进行增删改查操作了。平时针对某个用户增删改查,直接对他的 userid进行hash,然后对表取模,做一个路由,就知道去那个表里去找这个用户数据了。
    但是会出现一些问题,比如,用户登录的时候,可能不是根据userid登录的,可能是根据username之类的用户名,手机号之类的来登录,此时又没有用户userid,怎么去知道哪个表里找这 个用户的数据判断是否能登录呢?


    关于这个问题,一般来说常规方案是建立一个索引映射表,就是搞一个表结构为(username,userid)的索引映射表,把username和userid一一映射,然后针对username再做一次分库分表, 把这个索引映射表可以拆分为比如100个表分散到两台服务器里。
    然后再用户登录的时候,就可以根据username先去索引映射表里查找对应的userid,比如对username进行hash然后取模到路由到一个表里去,找到username对应的userid,接着根据userid 进行hash再取模,然后路由到按照userid分库分表的一个表里去,找到完整的用户数据即可。


    但是这种方式会把一次查询转化为两个表的两次查询,先查索引映射表,再根据userid去查具体的数据,性能上是有一定损耗的,不过有时候为了解决分库分表的问题,也只能用这种类似的 方法。另外如果有一个用户管理模块,需要对公司的用户按照手机号,住址,年龄,性别,职业,等各种条件进行极为复杂的搜索,怎么办?其实没有更好的办法,基本上就是要对你的用户数据表 进行binlog监听,把要搜索的所有字段同步到ES里去,建立好搜索的索引。
    然后运营系统就可以通过ES去进行复杂的多条件搜索,ES适合做这个事,然后定位到userid,通过userid回到分库分表环境里去找出具体的用户数据,在页面上展示。
    这就是一套比较常规和完整的分库分表的方案。

    知识点:多个字段查询时,可以用ES然后定位到一个userid,根据userid定位分库分库的表对应的userid的字段

    问题
    聚合支付的订单系统怎么去分库分表?目前最大的场景时查询 商户订单,代理商查询旗下商户订单。如果用无规律的orderid hash 那么查询起来麻烦,分页的时候很不好解决,如果 用商户id hash可能会造成数据倾斜。有什么比较合适的方案。
    解答:可以选择es或者TIDB分布式数据库。

    问题:用username做分片键,应该时字符串类型的,用什么算法,能均匀分摊到不同的库?
    解答: 可以看下Sharding-Sphere

    问题: 为什么走了es再走数据库?如果走es搜索,干脆在ES上放完整用户信息不就可以了?
    解答: 是可以的,一般es中就存储完整的数据,存储不了就hbase存储行,数据字段太多的话,ES存储不了就用Hbase

    问题: 先按照用户ID 分库分表,然后按照用户名分库分表,写数据的时候会不会有跨实例的情况?有没有可能不在一个事务里面?
    解答: 是有可能的

    问题:用户ID维度分库分表,买家和卖家维度如何分库分表?
    解答:网上方案是数据异构的方式,主从同步解析binlog然后再根据买家或者卖家维度,分库分表

    其实海量数据,对MySQL关系数据库而言,并不太适合,可以使用大数据领域的Hbase,ES,等解决方案。