此文已由做者张镐薪受权网易云社区发布。
web
欢迎访问网易云社区,了解更多网易技术产品运营经验。sql
直接介绍概念太枯燥了,仍是拿个和背景篇类似的例子介绍 业务场景:客户完成下单,快递员接受并更新运单状态,客户能够随时查看运单状态的任务。一票快递可能有多个子母件。同时,咱们须要标记每一个运单的状态,运单状态的解释和含义保存在运单状态字典表中。 所以,咱们须要创建以下表:咱们如今按照业务将数据库垂直拆分红运单库(单表2000tps,6000W数据),快递员库(单表1500tps,100W数据),客户库(单表1500tps,1000W数据记录);假设每一个MySQL数据库单表不能超过2000W数据,单表不能超过1000tps。那么运单库则须要分红3片,客户库须要分红2片,统一由MyCat管理。以下图所示:数据库
MyCat做为一个中间件,对应用应为无感知的。 应用访问MyCat,根据以前所述,应用感知到后台只是一个(或者多个,和访问MySQL实例同样)数据库(假设只有一个数据库,这个库叫SF,里面有运单相关表,快递员相关表和客户相关表);这里MyCat的数据库就是逻辑库。访问MyCat,结果应该以下面所示虽然其中的表可能存在于不一样的库,可是表面上,他们属于同一个MyCat实例中的同一个逻辑库。因此,虽然上面的架构图显示他们不在同一个数据库,可是在MyCat中,他们在同一个逻辑库。安全
在逻辑库下的表就是逻辑表。逻辑表能够分片,也能够不分片。 orders代表显是要分片的表,可是在MyCat看来,他们虽然分布在不一样的分片节点上(分布在不一样的MySQL数据库上),但仍视为是同一个逻辑表,在同一个逻辑库里。架构
分片表,是指那些原有的很大数据的表,须要切分到多个数据库的表,这样,每一个分片都有一部分数据,全部分片构成了完整的数据。分片表都有本身的分片规则,根据分片规则肯定分片。 配置里面,以下配置:
分布式
<table name="orders" primaryKey="id" dataNode="test$1-2" rule="mod-long"></table>
意思就是用mod-long规则根据主键id将运单表orders分割到test1,test2这两个数据库(分片节点)上。请求状况1:性能
select * from orders where id = 1;
对于分片表的查询,若是按照分片列查询,则请求只会被发送到一个分片上。请求状况2:大数据
select * from orders where id < 100 and id > 0;
对于分片表的查询,若是按照分片列范围(在字段类型支持范围的状况下)查询,则请求会根据分片规则计算两个边界值,而后将请求发送到对应结果的分片上,并合并每一个分片的结果。请求状况3:spa
select * from orders where initialpoint = 'Beijing';
像这种根据非分片列查询的状况,请求会被发送到全部分片上,并合并每一个分片的结果。请求状况4:请求为更新类型的sql语句,与查询的三种状况相同处理。.net
一个数据库中并非全部的表都很大,某些表是能够不用进行切分的,非分片是相对分片表来讲的,就是那些不须要进行数据切分的表。 例如:
<table name="courier" primaryKey="id" dataNode="test3"></table>
意思就是快递员表不用分片,保存在test3这个分片节点上。 对于非分片表的操做和对普通数据库的同样,由于不涉及到分布式数据库。
关系型数据库是基于实体关系模型(Entity-Relationship Model)之上,经过其描述了真实世界中事物与关系,Mycat中的ER表便是来源于此。根据这一思路,提出了基于E-R关系的数据分片策略,子表的记录与所关联的父表记录存放在同一个数据分片上,即子表依赖于父表,经过表分组(Table Group)保证数据Join不会跨库操做。 表分组(Table Group)是解决跨分片数据join的一种很好的思路,也是数据切分规划的重要一条规则。 以下:
<!-- 运单表,对主键id对2取模 --><table name="orders" primaryKey="id" dataNode="test$1-2" rule="mod-long"> <!-- 运单子母件表,运单表的子表,order_id与orders的id列对应 --> <childTable name="orders_cargo" joinKey="order_id" parentKey="id"> </childTable> </table>
运单表为分片表,运单表和运单子母件表为一对多关系,能够作成父子表。 对于子表的sql请求,都是经过joinKey对应到父表对应字段后,按照以前分片表的规则进行处理。
一个真实的业务系统中,每每存在大量的相似字典表的表,这些表基本上不多变更,字典表具备如下几个特性:
变更不频繁
数据量整体变化不大
数据规模不大,不多有超过数十万条记录。
对于这类的表,在分片的状况下,当业务表由于规模而进行分片之后,业务表与这些附属的字典表之间的关联,就成了比较棘手的问题,因此Mycat中经过数据冗余来解决这类表的join,即全部的分片都有一份数据的拷贝,全部将字典表或者符合字典表特性的一些表定义为全局表。 数据冗余是解决跨分片数据join的一种很好的思路,也是数据切分规划的另一条重要规则 好比:
<!-- 运单状态信息表,公共表,放在和运单表一样的分片上 --><table name="order_status_interception" primaryKey="id" type="global" dataNode="test$1-2"></table>
运单状态信息字典表,只是注释每种运单状态,就是典型的字典表,与分片表orders为多对一的关系。 对于全局表,全部的查询请求,只会发送到其中一个全局表分片上执行,全部的更新请求,会在每一个全局表分片上执行。
根据以前的描述,咱们能够推断出,对于分片表的修改和查询,若是是按照分片字段进行查找的话,则请求会被转发到一个分片上。若是不是按照分片字段的话,就会把请求发到每个分片上进行查找。因此,分片字段的选择比较重要!对于全局表,至关于在每一个分片上有一份相同的复制,修改请求会在每个分片上执行,可是查询只会落到一个分片上。因此,全局表尽可能是不会改变的并且是须要和分片表作Join操做的,若是常常改变或者不须要作join,最好仍是作成非分片表。
先抛出了这几种逻辑表的概念,你们先有个印象。如今咱们结合具体实际讨论如何决定表的类型。
首先,orders表可定是分片表。orders_cargo表是子母件表,一个order可能有多个子母件,因此,最好把orders_cargo做为orders的子表。 这种状况下,orders与orders_cargo按照对应键(就是子表按照哪一个键与主表的哪一个键对应进行分片。好比orders_cargo就是order_id与orders的id对应。这是以order_id与orders的id进行join结果就是对的)join结果也是正确的。像这种简单的从属关系一对n的表,咱们处理起来很简单,通常将它们按照须要作join的键设为父子表便可。
可是下面的场景很麻烦,好比快递员与运单就是多对多的关系,客户对于运单也是多对多的关系(一个收方,一个寄方)。咱们既有快递员须要查看本身的全部运单的场景和客户查看本身全部运单的场景。相对的,咱们也有查看一个运单涉及到的快递员还有客户的场景。 customer表(客户表)以及courier表(快递员表)由于与分片表orders之间不作join操做,因此不用做为公共表。 首先,关系表能够做为公共表,这样的话,涉及到与分片表的join操做没有限制,由于在每一个分片,公共表都是完整的。可是,关系表的更新很频繁,咱们可能不能忍受每更新一次关系表就跑到每一个分片上都更新一次(性能,可靠性考虑)。 那么做为运单的子表呢?那么查找一个运单涉及到的快递员还有客户就比较简单。由于根据运单号(也就是分片id)查询,MyCat就会根据分片规则给他定位到具体分片,而不是去按分片搜索。
可是相应的,快递员查看本身全部运单的场景就比较慢,由于请求是发送到每个分片上查找。做为快递员的子表也有一样的缺陷。 还有一种方法,就是这种关系表同时做为运单和快递员的子表。可是这样,目前须要应用本身去作双写。MyCat目前还没实现这种。固然,我以为这是一个咱们本身能够根据须要改进的地方。MyCat中间件根据关系冗余表关系进行双写
另外,究竟取哪一种方法,都是从业务出发去考虑的。在这里,若是从快递员出发去查找以及从运单出发去查找的业务压力差很少大的话,那么最好就采用关系表同时做为运单和客户的子表这种方法。而后将快递员和运单的业务独立,每一个业务应用都去维护本身的关系表,同时经过消息队列来保持关系表之间的一致性。这样也不失为一种方法。
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 大数据技术在金融行业的应用前景
【推荐】 寓教于乐——玩转角色互换游戏
【推荐】 巧用Scrum与Kanban