乐趣区

关于分库分表:好好的系统为什么要分库分表

大家好,我是小富~

说在前边

明天是《分库分表 ShardingSphere 原理与实战》系列的开篇文章,之前写过几篇对于分库分表的文章反应都还不错,到当初 公众号: 程序员小富 后盾一直的有人留言、征询分库分表的问题,我也没想到大家对于分库分表的话题会这么感兴趣,可能很多人的工作内容业务量较小很难接触到这方面的技能。这个系列在我脑子里策划了挺久的,奈何手说啥也不干活,就始终拖到了当初。

其实网上对于分库分表相干的文章很多,但我还是保持出这个系列,次要是本人学习钻研,顺便给分享,对于一个常识,不同的人从不同的角度了解的不尽相同。

网上的材料看似很多,不过值得学有价值的得认真挑,很多时候在筛选甄别的过程中,逐步的磨灭了本就不高的学习激情。搬运剽窃雷同的货色太多,而且知识点又都比拟系统,很少有粗疏的原理实战案例。对老手来说妥妥的从入门到放弃,即使有成体系的基本上几篇后就断更了(心愿我不会吧!)。

我不太喜爱堆砌名词概念,相熟我的敌人不难发现,我的文章素来都是讲完原理紧跟着来一波实战操作。学习技术原理必须配合实操坚固一下,不然三天半不到忘得干干净净,纯纯的经验之谈。

上图是我初步列举的 ShardingSphere 提纲,在官网文档根底上补充了很多基础知识,这个系列会用几十篇文章,具体的梳理分库分表基础理论,手把手的实战 ShardingSphere 5.X 框架的性能和解读源码,以及开发中容易踩坑的点,每篇附带代码案例 demo,旨在让老手也能看的懂,后续系列完结全部内容会整顿成 PDF 分享给大家,期待一下吧!

话不多说,咱们这就进入正题~

不急于上手实战 ShardingSphere 框架,先来温习下分库分表的根底概念,技术名词大多艰涩难懂,不要死记硬背了解最重要,当你捅破那层窗户纸,发现其实它也就那么回事。

什么是分库分表

分库分表是在海量数据下,因为单库、表数据量过大,导致数据库性能继续降落的问题,演变出的技术计划。

分库分表是由 分库 分表 这两个独立概念组成的,只不过通常分库与分表的操作会同时进行,以至于咱们习惯性的将它们合在一起叫做分库分表。

通过肯定的规定,将本来数据量大的数据库拆分成多个独自的数据库,将本来数据量大的表拆分成若干个数据表,使得繁多的库、表性能达到最优的成果(响应速度快),以此晋升整体数据库性能。

为什么分库分表

单机数据库的存储能力、连接数是无限的,它本身就很容易会成为零碎的瓶颈。当单表数据量在百万以里时,咱们还能够通过增加从库、优化索引晋升性能。

一旦数据量朝着千万以上趋势增长,再怎么优化数据库,很多操作性能仍降落重大。为了缩小数据库的累赘,晋升数据库响应速度,缩短查问工夫,这时候就须要进行分库分表。

为什么须要分库?

容量

咱们给数据库实例调配的磁盘容量是固定的,数据量继续的大幅增长,用不了多久单机的容量就会承载不了这么多数据,解决办法简略粗犷,加容量!

连接数

单机的容量能够随便扩大,但数据库的连接数却是无限的,在高并发场景下多个业务同时对一个数据库操作,很容易将连接数耗尽导致 too many connections 报错,导致后续数据库无奈失常拜访。

能够通过 max_connections 查看 MySQL 最大连接数。

show variables like '%max_connections%'

将本来单数据库按不同业务拆分成订单库、物流库、积分库等不仅能够无效摊派数据库读写压力,也进步了零碎容错性。

为什么须要分表?

做过报表业务的同学应该都体验过,一条 SQL 执行工夫超过几十秒的场景。

导致数据库查问慢的起因有很多,SQL 没命中索引、like 扫全表、用了函数计算,这些都能够通过优化伎俩解决,可唯独数据量大是 MySQL 无奈通过本身优化解决的。慢的根本原因是 InnoDB 存储引擎,聚簇索引构造的 B+tree 层级变高,磁盘 IO 变多查问性能变慢,具体原理自行查找一下,这里不必过多篇幅阐明。

阿里的开发手册中有条倡议,单表行数超 500 万行或者单表容量超过 2GB,就举荐分库分表,然而现实和实现总是有差距的,阿里这种体量的公司不差钱当然能够这么用,实际上很多公司单表数据几千万、亿级别依然不抉择分库分表。

什么时候分库分表

技术群里常常会有小伙伴问,到底什么状况下会用分库分表呢?

分库分表要解决的是 现存海量数据 拜访的性能瓶颈,对 继续激增 的数据量所做出的架构预见性。

是否分库分表的要害指标是数据量 ,咱们以fire100.top 这个网站的资源表 t_resource为例,零碎在运行初始的时候,每天只有可怜的几十个资源上传,这时应用单库、单表的形式足以支持系统的存储,数据量小简直没什么数据库性能瓶颈。

但某天开始一股神秘的流量进入,零碎每日产生的资源数据量暴增至十万甚至上百万级别,这时资源表数据量达到千万级,查问响应变得迟缓,数据库的性能瓶颈逐步浮现。

以 MySQL 数据库为例,单表的数据量在达到亿条级别,通过加索引、SQL 调优等传统优化策略,性能晋升仍旧微不足道时,就能够思考做分库分表了。

既然 MySQL 存储海量数据时会呈现性能瓶颈,那么咱们是不是能够思考用其余计划代替它?比方高性能的非关系型数据库MongoDB

能够,但要看存储的数据类型!

当初互联网上大部分公司的外围数据简直是存储在关系型数据库(MySQL、Oracle 等),因为它们有着 NoSQL 如法比较的稳定性和可靠性,产品成熟生态系统欠缺,还有外围的事务性能个性,也是其余存储工具不具备的,而评论、点赞这些非核心数据还是能够思考用 MongoDB 的。

如何分库分表

分库分表的外围就是对数据的分片(Sharding)并绝对平均的路由在不同的库、表中,以及分片后对数据的疾速定位与检索后果的整合。

分库与分表能够从:垂直(纵向)和 程度(横向)两种纬度进行拆分。下边咱们以经典的订单业务举例,看看如何拆分。

垂直拆分

1、垂直分库

垂直分库一般来说依照业务和性能的维度进行拆分,将不同业务数据别离放到不同的数据库中,核心理念 专库专用

按业务类型对数据拆散,剥离为多个数据库,像订单、领取、会员、积分相干等表放在对应的订单库、领取库、会员库、积分库。不同业务禁止跨库直连,获取对方业务数据一律通过 API 接口交互,这也是微服务拆分的一个重要依据。

垂直分库很大水平上取决于业务的划分,但有时候业务间的划分并不是那么清晰,比方:电商中订单数据的拆分,其余很多业务都依赖于订单数据,有时候界限不是很好划分。

垂直分库把一个库的压力摊派到多个库,晋升了一些数据库性能,但并没有解决因为单表数据量过大导致的性能问题,所以就须要配合后边的分表来解决。

2、垂直分表

垂直分表针对业务上字段比拟多的大表进行的,个别是把业务宽表中比拟独立的字段,或者不罕用的字段拆分到独自的数据表中,是一种大表拆小表的模式。

例如:一张 t_order 订单表上有几十个字段,其中订单金额相干字段计算频繁,为了不影响订单表 t_order 的性能,就能够把订单金额相干字段拆出来独自保护一个 t_order_price_expansion 扩大表,这样每张表只存储原表的一部分字段,通过订单号 order_no 做关联,再将拆分进去的表路由到不同的库中。

数据库它是以行为单位将数据加载到内存中,这样拆分当前外围表大多是拜访频率较高的字段,而且字段长度也都较短,因此能够加载更多数据到内存中,缩小磁盘 IO,减少索引查问的命中率,进一步晋升数据库性能。

程度拆分

上边垂直分库、垂直分表后还是会存在单库、表数据量过大的问题,当咱们的利用曾经无奈在细粒度的垂直切分时,仍旧存在单库读写、存储性能瓶颈,这时就要配合程度分库、程度分表一起了。

1、程度分库

程度分库是把同一个表按肯定规定拆分到不同的数据库中,每个库能够位于不同的服务器上,以此实现程度扩大,是一种常见的晋升数据库性能的形式。

例如:db_orde_1db_order_2两个数据库内有完全相同的 t_order 表,咱们在拜访某一笔订单时能够通过对订单的订单编号取模的形式 订单编号 mod 2(数据库实例数),指定该订单应该在哪个数据库中操作。

这种计划往往能解决单库存储量及性能瓶颈问题,但因为同一个表被调配在不同的数据库中,数据的拜访须要额定的路由工作,因而零碎的复杂度也被晋升了。

2、程度分表

程度分表是在 同一个数据库内,把一张大数据量的表按肯定规定,切分成多个构造完全相同表,而每个表只存原表的一部分数据。

例如:一张 t_order 订单表有 900 万数据,通过程度拆分进去三个表,t_order_1t_order_2t_order_3,每张表存有数据 300 万,以此类推。

程度分表只管拆分了表,但子表都还是在同一个数据库实例中,只是解决了繁多表数据量过大的问题,并没有将拆分后的表扩散到不同的机器上,还在竞争同一个物理机的 CPU、内存、网络 IO 等。要想进一步晋升性能,就须要将拆分后的表扩散到不同的数据库中,达到分布式的成果。

数据存在哪个库的表

分库分表当前会呈现一个问题,一张表会呈现在多个数据库里,到底该往哪个库的哪个表里存呢?

上边咱们屡次提到过 肯定规定,其实这个规定它是一种路由算法,决定了一条数据具体应该存在哪个数据库的哪张表里。

常见的有 取模算法 范畴限定算法 范畴 + 取模算法 预约义算法

1、取模算法

关键字段取模(对 hash 后果取余数 hash(XXX) mod N),N 为数据库实例数或子表数量)是最为常见的一种路由形式。

t_order 订单表为例,先给数据库从 0 到 N- 1 进行编号,对 t_order订单表中 order_no 订单编号字段进行取模 hash(order_no) mod N,失去余数ii=0 存第一个库,i=1存第二个库,i=2存第三个库,以此类推。

同一笔订单数据会落在同一个库、表里,查问时用雷同的规定,用 t_order 订单编号作为查问条件,就能疾速的定位到数据。

长处

实现简略,数据分布绝对比拟平均,不易呈现申请都打到一个库上的状况。

毛病

取模算法对集群的伸缩反对不太敌对,集群中有 N 个数据库实·hash(user_id) mod N,当某一台机器宕机,本应该落在该数据库的申请就无奈失去解决,这时宕掉的实例会被踢出集群。

此时机器数缩小算法发生变化 hash(user_id) mod N-1,同一用户数据落在了在不同数据库中,等这台机器复原,用user_id 作为条件查问用户数据就会少一部分。

2、范畴限定算法

范畴限定算法以某些范畴字段,如 工夫 ID 区 拆分。

用户表 t_user 被拆分成 t_user_1t_user_2t_user_3 三张表,后续将 user_id 范畴为 1 ~ 1000w 的用户数据放入t_user_1,1000~ 2000w 放入t_user_2,2000~3000w 放入t_user_3,以此类推。按日期范畴划分同理。

长处

  • 单表数据量是可控的
  • 程度扩大简略只需减少节点即可,无需对其余分片的数据进行迁徙

毛病

  • 因为间断分片可能存在 数据热点,比方按工夫字段分片时,如果某一段时间(双 11 等大促)订单骤增,存 11 月数据的表可能会被频繁的读写,其余分片表存储的历史数据则很少被查问,导致数据歪斜,数据库压力摊派不平均。

3、范畴 + 取模算法

为了防止热点数据的问题,咱们能够对上范畴算法优化一下

这次咱们先通过范畴算法定义每个库的用户表 t_user 只存 1000w 数据,第一个 db_order_1 库存放 userId 从 1 ~ 1000w,第二个库 1000~2000w,第三个库 2000~3000w,以此类推。

每个库里再把用户表 t_user 拆分成 t_user_1t_user_2t_user_3 等,对 userd 进行取模路由到对应的表中。

无效的防止数据分布不平均的问题,数据库程度扩大也简略,间接增加实例无需迁徙历史数据。

4、地理位置分片

地理位置分片其实是一个更大的范畴,按城市或者地区划分,比方华东、华北数据放在不同的分片库、表。

5、预约义算法

预约义算法是当时曾经明确晓得分库和分表的数量,能够间接将某类数据路由到指定库或表中,查问的时候亦是如此。

分库分表进去的问题

理解了上边分库分表的拆分形式不难发现,相比于拆分前的单库单表,零碎的数据存储架构演变到当初曾经变得非常复杂。看几个具备代表性的问题,比方:

分页、排序、跨节点联结查问

分页、排序、联结查问,这些看似一般,开发中应用频率较高的操作,在分库分表后却是让人十分头疼的问题。把扩散在不同库中表的数据查问进去,再将所有后果进行汇总合并整顿后提供给用户。

比方:咱们要查问 11、12 月的订单数据,如果两个月的数据是扩散到了不同的数据库实例,则要查问两个数据库相干的数据,在对数据合并排序、分页,过程繁琐简单。

事务一致性

分库分表后因为表散布在不同库中,不可避免会带来跨库事务问题。后续会别离以阿里的 Seata 和 MySQL 的 XA 协定实现分布式事务,用来比拟各自的劣势与有余。

全局惟一的主键

分库分表后数据库表的主键 ID 业务意义就不大了,因为无奈在标识惟一一条记录,例如:多张表 t_order_1t_order_2 的主键 ID 全副从 1 开始会反复,此时咱们须要被动为一条记录调配一个 ID,这个全局惟一的 ID 就叫 分布式 ID,发放这个 ID 的零碎通常被叫发号器。

多数据库高效治理

对多个数据库以及库内大量分片表的高效治理,是十分有必要,因为像某宝这种大厂一次大促下来,订单表可能会被拆分成成千上万个 t_order_n 表,如果没有高效的治理计划,手动建表、排查问题是一件很恐怖的事。

历史数据迁徙

分库分表架构落地当前,首要的问题就是如何平滑的迁徙历史数据,增量数据和全量数据迁徙,这又是一个比拟麻烦的事件,后边具体讲。

分库分表架构模式

分库分表架构次要有两种模式:client客户端模式和 proxy 代理模式

客户模式

client模式指分库分表的逻辑都在你的零碎利用外部进行管制,利用会将拆分后的 SQL 直连多个数据库进行操作,而后本地进行数据的合并汇总等操作。

代理模式

proxy代理模式将应用程序与 MySQL 数据库隔离,业务方的利用不在须要直连数据库,而是连贯 proxy 代理服务,代理服务实现了 MySQL 的协定,对业务方来说代理服务就是数据库,它会将 SQL 散发到具体的数据库进行执行,并返回后果。该服务内有分库分表的配置,依据配置主动创立分片表。

如何抉择

如何抉择 client 模式和 proxy 模式,咱们能够从以下几个方面来简略做下比拟。

1、性能

性能方面 client 模式体现的稍好一些,它是间接连贯 MySQL 执行命令;
proxy代理服务则将整个执行链路缩短了,利用 -> 代理服务 ->MySQL,可能导致性能有一些损耗,但两者差距并不是十分大。

2、复杂度

client模式在开发应用通常引入一个 jar 能够;
proxy代理模式则须要搭建独自的服务,有肯定的保护老本,既然是服务那么就要思考高可用,毕竟利用的所有 SQL 都要通过它转发至 MySQL。

3、降级

client模式分库分表个别是依赖基础架构团队的 Jar 包,一旦有版本升级或者 Bug 批改,所有利用到的我的项目都要跟着降级。小规模的团队服务少降级问题不大,如果是大公司服务规模大,且波及到跨多部门,那么降级一次老本就比拟高;

proxy模式在降级方面劣势很显著,公布新性能或者修复 Bug,只有重新部署代理服务集群即可,业务方是无感知的,但要保障公布过程中服务的可用性。

4、治理、监控

client模式因为是内嵌在利用内,利用集群部署不太不便对立解决;proxy模式在对 SQL 限流、读写权限管制、监控、告警等服务治理方面更优雅一些。

结束语

本文次要是回顾一下分库分表的一些根底概念,为大家在后续 ShardingSphere 实际中更好上手了解,内容里很多概念一笔带过没具体开展,接下来的篇幅会逐个解读。

下一篇预报《分库分表 ShardingSphere 的根底知识点梳理》

欢送关注 公众号:程序员小富,咱们下期再见!

退出移动版