前言
本专栏将讲述数据库中间件 —— Mycat,从架构原理出发,深入浅出,理论与实践相结合。
切分
简单来说,就是指通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库(主机)上面,以达到分散单台设备负载的效果。
数据的切分(Sharding)根据其切分规则的类型,可以分为两种切分模式。一种是按照不同的表(或者Schema )来切分到不同的数据库(主机)之上,这种切可以称之为数据的垂直(纵向)切分;另外一种则是根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种切分称之为数据的水平(横向)切分。
垂直切分
一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,分布到不同的数据库上面,这样也就将数据或者说压力分担到不同的库上面,如下图:
系统被切分为了:用户、订单交易、支付系统三个模块。
垂直切分优点:
- 拆分后业务清晰,拆分规则明确
- 系统之间整合或扩展容易
- 数据维护简单
垂直切分缺点:
- 部分业务表无法join,只能通过接口方式解决,提高了系统复杂度
- 受每种业务不同的限制存在单库性能瓶颈,不易数据扩展和性能提高
- 事务处理复杂
由于垂直切分是按照业务的分类将表分散到不同的库,所以有些业务表会过于庞大,存在单库读写与存储瓶颈,所以就需要水平拆分来做解决。
水平切分
相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中,如图:
拆分数据就需要定义分片规则。关系型数据库是行列的二维模型,拆分的第一原则是找到拆分维度。比如∶从会员的角度来分析,商户订单交易类系统中查询会员某天某月某个订单,那么就需要按照会员结合日期来拆分,不同的数据按照会员ID做分组,这样所有的数据查询join都会在单库内解决;如果从商户的角度来讲,要查询某个商家某天所有的订单数,就需要按照商户ID做拆分;但是如果系统既想按会员拆分,又想按商家数据,则会有一定的困难。如何找到合适的分片规则需要综合考虑衡量。
几种典型的分片规则包括:
- 按照用户ID求模,将数据分散到不同的数据库,具有相同数据用户的数据都被分散到一个库中
- 按照日期,将不同月甚至日的数据分散到不同的库中
- 按照某个特定的字段求模,或者根据特定范围段分散到不同的库中
如图,切分原则都是根据业务找到适合的切分规则分散到不同的库,下面用用户ID求模举例:
水平切分优点:
- 拆分规则抽象好,
Join操作基本可以数据库做 - 不存在单库大数据,高并发的性能瓶颈
- 应用端改造较少
- 提高了系统的稳定性和负载能力
水平切分缺点:
- 拆分规则难以想象
- 分片事务一致性难以解决
- 数据多次扩展难度和维护量极大
- 跨库join性能较差
不管是垂直切分和水平切分,每种方式都有优缺点,但共同的特点有:
- 引入分布式事务问题
- 跨节点
Join的问题 - 跨节点合并排序分页问题
- 多数据源管理问题
在设计数据库切分时要参考几个原则:
- 能不切分尽量不切分
- 如果要切分一定要选择合适的切分规则,提前规划好
- 数据切分尽量通过数据冗余或表分组来降低跨库
Join的可能 - 由于数据库中间件对数据
Join实现的优劣难以把握,而且实现高性能难度极大,业务读取尽量少使用多表Join
初始 Mycat
Mycat 是一个分布式数据库系统,但由于真正的数据库需要存储引擎,而 Mycat 并没有存储引擎,所以并不是完全意义的分布式数据库系统。
Mycat 是数据库中间件,介于数据库与应用之间,进行数据处理与交互的中间服务。是一个实现了 MySQL 协议的 Server,不仅可以用作读写分离、分库分表、灾容备份,而且可以用于多租户应用开发、云平台基础设施、让架构具备很强的适应性和灵活性。
一些数据库中间件的对比如下图:
Mycat 架构与核心概念
如上图所示,数据被分到多个分片数据库后,应用如果需要读取数据,就需要处理多个数据源的数据。如果没有数据库中间件,那么应用将直接面对分片集群,数据源切换,事务处理,数据聚合等都需要应用直接处理,原本该是专注于业务的应用,将会花大量的工作来处理分片后的问题。所以有了数据库中间件,应用只需要几种处理业务,大量的通用的数据聚合,事务,数据源切换都由中间件来处理,中间件的性能与处理能力将直接决定应用的读写性能,所以一款好的数据库中间件至关重要。
逻辑库(schema)
开发人员通常不知道中间件的存在,只需要关注数据库本身,所以数据库中间件可以被当做一个或多个数据库集群构成的逻辑库。
逻辑表(table)
分布式数据库中,对应用来说,读写数据的表就是逻辑表。逻辑表,可以是数据切分后,分布在一个或多个分片库中,也可以不做数据切分,不分片,只有一个表构成。
1、分片表
是指那些原有的很大数据的表,需要切分到多个数据库的表,这样,每个分片有一部分数据,所有的分片构成了完整的数据。
例如在 Mycat schema.xml 中配置的 <schema>标签的内部标签
<table name="t_node" primaryKey="vid" autoIncrement="true" dataNode="dn1,dn2" rule="rule1" />
这就是一个分片表,数据按照规则被切分到 dn1,dn2 两个节点。
2、非分片表
并非所有的表在数据量很大时都需要分片,某些表可以不用分片。非分片表是相对于分片表而言的不需要进行数据切分的表。
3、ER表
关系型数据库是基于实体关系模型的,Mycat 中的ER表便来源于此。基于此思想,Mycat 提出了基于E-R关系的数据分片策略,子表的记录与其所关联的父表的记录存放在同一个数据分片上,即子表依赖于父表,通过表分组(Table Group)保证数据关联查询不会跨库操作。
表分组(Table Group)是解决跨分片数据关联查询的一种很好的思路,也是数据切分的一条重要规则。
4、全局表
在一个真实的业务场景中往往存在大量类似的字典表,这些字典表中的数据变动不频繁,而且数据规模不大,很少有超过数十万条的记录。
当业务表因为规模进行分片后,业务表与这些附属的字典表之间的关联查询就成了比较棘手的问题,所以在Mycat中通过数据冗余来解决这类表的关联查询,即所有分片都复制了一份数据,我们把这些冗余数据的表定义为全局表。
数据冗余是解决跨分片数据关联查询的一种很好的思路,也是数据切分规划的另一条重要规则。
分片节点(dataNode)
将数据切分后,一个大表被分到不同的分片数据库上,每个表分片所在的数据库就是分片节点。
节点主机(dataHost)
将数据切分后,每个分片节点不一定会独占一台机器,同一台机器上可以有多个分片数据库,这样一个或多个分片节点所在的机器就是节点主机。为了规避单节点主机并发数量的限制,尽量将读写压力高的分片节点均匀的放在不同的节点主机上。
Mycat 原理
Mycat 会把应用程序发送过来的SQL语句进行拦截,拦截后对 SQL 进行做特定的分析,例如:分片分析、路由分析、读写分离分析、缓存分析等,然后将此SQL发往后端的真实数据库,并将返回的结果做适当处理,最终返回给用户,如下图示。
在上图中,orders 表被分为三个分片节点 dn1,dn2,dn3,他们分布在两台 dataHost 上,即 datanode=database@datahost,因此可以用 1-N 台服务器来分片,分片规则为典型的字符串枚举分片规则,一个规则的定义是分片字段+分片函数。这里的分片字段为 prov,分片函数则为字符串枚举方式。
当 MySQL 收到一条 SQL 时,首先解析 SQL 语句涉及的表,接着查看此表的定义,如果该表存在分片规则,则获取SQL语句里分片字段的值,并匹配分片函数,得到该SQL语句对应的分片列表,然后该 SQL 语句发送到相应的分片去执行,最后处理所有分片返回的数据并返回给客户端。以 select * from orders where prov=? 语句为例,查找 prov=wuhan,按照分片函数,wuhan值存放在 dn1 上,于是 SQL 语句被发送到 MySQL1,把 DB1 上的查询结果返回给用户。
如果将语句改成 select * from orders where prov in ('wuhan','beijing'); 那么SQL会被发送到 MySQL1 与 MySQL2 去执行,然后将结果集合并后输出给用户。但通常在业务中SQL语句会有 Order by 及 limit 翻页语法,此时就涉及结果集在 Mycat 端的二次处理。
小结
本文主要介绍了数据库切分、Mycat 的架构以及原理,如对 Mycat 感兴趣可继续关注本专栏其他文章。