秒杀 :分库分表是提升数据库性能最合适的方式

?????? 欢迎点赞收藏关注哟

一. 前言

数据库作为整个系统的一大件,在秒杀场景下同样有很多需要考虑的地方。

这一篇先从宏观角度来看,不谈具体的优化细节。能用钱解决的,就先不考虑小优化。

文章主题

分库分表并不是并发场景标准解决方案,但是就像标题里面说的 :最合适

  • 首先单库的性能其实没我们想的那么差,如果 愿意花钱, Oracle 数据库扛百亿数据一点问题没有。我见过给数据库2T内存 ,什么功能实现不了。

  • 其次使用特别的存储介质其实效果能更好,把热点数据放 Redis ,把订单数据放 MongoDB , 用 ES 做索引查询,甚至于上图数据库,列数据库 ,在一些特殊的场景其实最终的效果能更好。

  • 但是呢,这些 NoSQL数据库 需要更多的资源,在人力,学习上面都有更多的成本

  • 所以 ,综合成本和经济 ,以及扩展和切换等因素的情况下,通过分库分表来存储大数据量的同时保证性能就是最合适的了。

二. 深入理解分库分表的目的

2.1 分表是为了解决什么问题?

也许你还在用单体数据库

首先要有一个清晰的认知,单机始终有着极限,哪怕性能给的再高,单机也会存在限制,例如带宽,CPU线程,端口极限,甚至于 MySQL 各种功能 (索引,文件系统)都导致存储系统无法通过单机性能来解决超大并发量。

以 MySQL 为例 ,上亿的数据光索引就有十几G ,在十几G数据里面进行查询,给多好的硬件都难免吃力

演化

如果单机无法解决,那么解决思路就往多台服务器上面去想。简单的方式有以下几种 :

  • 方案一 :代码层面 去定制多个数据库,也就是通过调用不同的 DataSource 就可以简单实现对多库的调用

  • 方案二 :读写分离,将读取的压力分流到其他的读库,从而提高系统的整体并发能力。

  • 方案三 : 结合其他存储介质(Redis缓存,ES全文索引),来实现功能的分流

方案一 已经具有分库的思想了,也就是一般微服务的处理方式,每个微服务对应自己的业务库。从而让整个应用的吞吐提高。 而 方案二 同样是一种分流,通过读库均摊了性能的压力 , 但是治标不治本.

突破

当量达到一定的地步,上述的方案还是会遇到瓶颈,除非完全丢弃关系型数据库,否则总要面对性能危机。

这个时候,就要考虑分库分表了。这里只针对 MySQL 来说 , 在我看来分库分表有技术层面和逻辑层面2种 :

  • 逻辑层面 : 大库分小库,基于业务微服务划分多个库。 宽表改小,关联性的数据放在其他的表,减少单表的体积。

  • 技术层面 :这个层面的分库分表就是一般我们说的 ,把一个表分成 库A/B/C/D , 表分成 01/02/03/04 ,通过分库来释放当实例的压力,再通过分表来释放单表的上限

2.2 分库分表该怎么设计

下面来主要针对技术层面的分库分表进行讨论,一般业界对于如何分有两大方案,其核心都是通过一定的计算来将数据指向不同的库/表 (后面我统一称为分区)

  • 方案一 :Hash分区 ,基于特定主键进行 Hash 取模分库数量(分表数量),从而确定数据具体应该落在哪个分区里面

  • 方案二 :范围分区 ,通过特定主键进行数值切分,从而得到对应的分区 ,例如 1-10万到库A ,10万到20万到库B

一般对项目的建议都是早做分表,原因在于后续迁移带来很大的困难。但是难点也很明显,初创公司没有谁能预知未来的并发情况,所以一般会有几种设计方式 :

直接选择范围分库

这种类型可以有2种处理方式 :

  • 方式一 : 通过 ID 进行分库 ,就像上面说的 0-100万库A ,100万-200万库B。
    • 好处就是可以一直横向扩展,且每个表的数据总量比较均衡

    • 坏处就是数据会耦合,可能热点数据始终在后面的库,前面的库又没办法拿掉,数据操作不均衡

  • 方式二 : 通过 Domain 等租户ID分库 ,这种更适合 Saas 系统,租户A在A库,租户B被分到B库
    • 好处是租户的数据通常可以聚合在一个实例里面

    • 坏处是数据分布会不均衡,可能租户A的数据量很大,租户B就几个用户

通过 Hash 分库

通过 Hash 分库分表应该是最常见的方式,使用的时候选择一个字段作为分片键,通过对这个字段进行 Hash 后取模,平均分配到不同的数据分片中。

使用 Hash 方式的情况下,数据是很均衡的 。但是同样要考虑后期扩展问题,也有多种方案 :

  • 早期场景数据量不大的情况下,可以考虑数据由单库迁移到多库。当然不是自己写程序迁移 ,一般都是走双写,有的云厂商还会提供迁移工具使用,两者数据一致后,进行数据库切换。 因为数据量不大,所以难度不高。

  • 而针对发展比较迅速的企业,可以直接一开始就进行分库分表(比如直接建好64个分表丢到不同库中),但是为了节省资源,可以在一个实例上建多个库,等数据量大了之后,再将整个库进行迁移即可。 虽然还是要迁移 ,但是复杂度要低很多。

2.4 分库分表有什么难点

  • ?难点一 : 分库分表时很多语法是不支持的

这种特性尤其在使用 Mycat 和 云厂商的时候尤为明显,Mycat 原理是做了数据库代理,相当于把 Client 的 MySQL 请求接收到后 转发到实际的 MySQL 中,这就导致版本上就会带来很多函数的不匹配。

而对于云厂商的数据库,通常都不是原生MySQL , 有的是进行了改造,有的更是自研后适配了 MySQL 协议,语法自然不支持。

  • ?难点二 :连表查询会很困难

因为涉及到了多个表,所以必然会带来一些连表查询的问题。最核心的点在于没有命中分片键或者连表导致分片键不生效的时候,就会进行分区多表扫描。

  • ?难点三 : 事务处理复杂

以 MySQL 为例,当单个库的时候,很简单的 Begin + Commit 就实现了事务。当多库的时候,其本质上就演变成了分布式事务的场景,由此就会带来数据操作的复杂性。当事务处理问题的时候,回滚也会很复杂。

像 Mycat ,其只支持弱 XA ,而我在使用 MyCAT 的时候,也尽量会控制事务的粒度,避免复杂的事务出现。

  • ?难点四 : 后期扩展会比较麻烦

一般分库分表的后期扩展难免需要进行数据迁移,归根的方案大多数都是空闲迁移 + 双写。

  • ?难点五 : 分库分表也不仅仅是处理数据库

虽然我们做了数据库的分库分表,但是在一些业务的设计上还要考虑其他的环节 ,例如 Redis 缓存等。

这些东西同样会在分库分表中影响某个环节的最终性能。这个时候就要考虑逻辑上进行分桶设计

具体来说就是逻辑分桶后的数据处在自己的一套数据集里面,并不会和其他的数据集打角度。这个数据集从缓存到数据库应该都是完善和独立的。

三. 具体的实现方案

3.1 Mycat 分表方式

关于 Mycat 的使用方式我这里就不写了,之前也只写过两篇其他的 :

# MyCat : 主流程学习与源码入门[4]

# Mycat : Mycat 作为代理服务端的小知识点[5]

MyCAT 对于应用系统来说就是一个数据,应用系统可以用常见的数据库连接协议去发起调用(JDBC / MySQL客户端)。

然后在 Mycat Server 进行分库分表的处理 , 包括 数据节点的计算 ?? 数据的查询 ?? 结果的汇总和返回

Mycat 有什么优势,又有什么问题?

下面就来宏观的分析一下 Mycat 的好处和问题。 Mycat 是通过数据库代理来实现分库分表的。 对于应用系统来说,调用 Mycat 和调用数据库没有任何区别

  • 优势
    • 开发成本低,调用方式和调用 MySQL 没有区别,用户只需要在 Zookeeper 中进行 Mycat 分库的配置就能快速实现分库

    • 分库分表运算的压力不在应用系统中, 将应用系统 和 分库分表处理器 进行解耦 (PS :如果需要可以部署 Mycat 集群来减轻单台的压力)

    • Mycat 数据查询带来的负载并不会影响到应用系统,对风险进行了分摊

    • 通过 Navicat 等工具进行表数据查询的时候会简单的多,就想查询一个系统一样

  • 问题
    • 黑盒 ,黑盒 还是黑盒 ,Mycat 的特点就导致了如果 Mycat 出现问题,分析和解决起来都特别蛮烦

    • 生态收缩 , Mycat 第一代差不多发布到了 1.6 就截止了 ,Mycat 2.0 的使用要远低于一代,生态现在要差很多

    • 对事务的支持弱,定制困难。 mycat 本身基于 XA 的弱事务,加上内聚到了 Mycat Server 里面 ,就导致事务其实只能按照这一套去玩了

3.2 Sharding 分表方式

Sharding 我本身也用了一段时间,和 Mycat 对比 ,Sharding JDBC 将分表的逻辑放在了客户端,也就是应用系统里面

# 盘点Sharding-JDBC : 读写分离[6]

# 盘点Sharding-JDBC : 分库分表[7]

  • 优点 :
    • Sharding 相当于将分表的运算和汇总放在了应用系统这个客户端来做,均摊压力到客户端

    • 分表处理在客户端后 , 问题的排查会简单的多,切面,日志,代理都可以安排上

    • 分布式事务的处理要相对简单一点

  • 缺点 :
    • 分表的处理方式会直接影响到应用的性能

    • 业务查询的时候不是很好处理

    • 相对而言,写 ShardingJDBC 的分表规则回比写 Mycat 复杂些,或许可以说自定义的能力强了点,就牺牲了简洁性

3.3 一些云厂商的分表方式

云上的分表方式也有一些,我接触的主要是案例的 PolarDB 系列,例如 Polar-X , 其本身就支持范围和Hash分区,还支持动态扩展。

@ 水平扩展 · PolarDB-X 产品文档 (polardbx.com)[8]

这里就不细说了,文档里面比较完善。

总结

分库分表大概就这么多东西,到了这一步基础准备应该就差不多了,后续就是业务功能上的处理了。

这一篇主要是聊思想,对一些具体的业务没有体现,后续碰到有意思的会一起分析下。