博客文章除注明转载外,均为原创。转载请注明出处。
本文链接地址:http://blog.chinaunix.net/uid-31396856-id-5781751.html

       本文是mycat笔记整理:
       Mycat是一个开源的数据库中间件。在业务和数据库中间提供的代理层,介于数据库与应用之间,进行数据处理与交互的中间服务,其核心功能分库分表。
1.mycat原理
       Mycat接收前段对数据库对访问请求,是通过模拟MySQL协议的接口实现的。在前端同Mycat建立会话连接后,将请求的SQL发给Mycat后,mycat并不是直接交由数据库处理。而是对sql进行拦截后,进行解析优化、分片分析、读写分离分析、缓存分析,根据解析的结果进行路由处理。把sql发给复合规则的后端数据库群执行。
     Mycat同后端数据库使用基于MySQL实例的连接池方式进行通信,mycat接收后端数据库返回的执行结果,对结果进行处理,比如分页处理,排序处理、聚合处理、结果集合并等等,然后返回前端业务层。Mycat实现实现读写分离和分片分库,甚至备份容灾等功能。
架构如下图
      
Mycat笔记整理-LMLPHP

图1:架构图(出自官方)
      Mycat实现了MySQL协议,前端用户可以把它看作是一个数据库代理,用MySQL客户端工具和命令行访问,而其后端可以用MySQL原生协议与多个MySQL服务器通信,也可以用JDBC协议与大多数主流数据库服务器通信,其核心功能是分表分库,即将一个大表水平分割为N个小表,存储在后端MySQL服务器里或者其他数据库里。


2.Mycat SQL机制

下图说明Mycat的SQL拦截机制
Mycat笔记整理-LMLPHP
有上图可知,mycat模拟MySQL的socket协议或者jdbc协议同业务端(application)连接,在接收到SQL后,并不是直接发给MySQL数据库,而是在SQL解析器组件对SQL进行解析优化,通过SQL Router中对SQL进行路由分析,进行匹配路由规则匹配,然后交给SQL执行组件,发送到响应的写、读数据库的分片节点,执行SQL。SQL执行简单过程如下所示:    
Mycat笔记整理-LMLPHP

      当Mycat收到前端发送的一个SQL时,首先会先解析这个SQL,查找涉及到的表,然后看此表的定义,如果有分片规则,则获取到SQL里分片字段的值,并匹配分片函数,得到该SQL对应的分片列表,然后将SQL发往这些分片去执行,最后收集和处理所有分片返回的结果数据,并输出到客户端。以select * from orders where area='xxx'语句为例,查到area='beijing',按照分片函数,beijing返回dn1,于是SQL就发给了MySQL1,去取DB1上的查询结果,并返回给用户。
Mycat笔记整理-LMLPHP



     如果上述SQL改为select * from orders where area in ('shanghai','beijing),那么,SQL就会发给MySQL1与MySQL2去执行,然后将Dn1和Dn2返回的结果集进行合并(merge)处理后输出给用户。


3.Mycat主要线程说明
(1)Timer线程:调度线程,是一个单线程,负责调度,任务的具体动作交给 timerExecutor。
(2) TimerExecutor(线程池)
默认大小 N=2,任务通过 timer 单线程和 timerExecutor 线程池这种1+N模式共同完成。但是 timerExecutor 跟 aioExecutor 大小默认一样,不太合理,定时任务没有那么大的运算量。
(3) NIOConnect: 主动连接事件分离器,负责作为客户端连接 MySQL 的主动连接事件
(4)Server 被动连接事件分离器,负责作为服务端接收来自业务系统的连接事件
(5) Manager线程:被动连接事件分离器,负责作为服务端接收来自管理系统的连接事件
(6)NIOReactor 读写事件分离器
默认个数 N=processor size,通道建立连接后处理 NIO 读写事件。
由于写是采用通道空闲时其它线程直接写,只有写繁忙时才会注册写事件,再由 NIOReactor 分发。NIOReactor 主要是处理读操作
(7)BusinessExecutor 线程池
默认大小 N=processor size,任务队列采用的 LinkedTransferQueue
所有的 NIOReactor 把读出的数据交给 BusinessExecutor 做下一步的业务操作
全局只有一个 BusinessExecutor 线程池,所有链接通道随机分成多个组,然后每组的多个通道共享一Reactor,所有的 Reactor 读取且解码后的数据下一步处理操作,又共享一个 BusinessExecutor 线程池

4.Mycat使用的原则
(1)分库分表原则:
原则一:能不分就不分。
        分表是为了解决的问题,但是也会带来性能上的损失。 所以分片第一原则是:能不分就不分。对于1000 万以内的表,不建议分片,通过合适的索引,读写分离等方式,可以很好的解决性能问题。
原则二:分片数量尽量少,分片尽量均匀分布在多个 DataHost 上
        因为一个查询 SQL 跨分片越多,则总体性能越差,虽然要好于所有数据在一个分片的结果,只在必要的时候进行扩容,增加分片数量。
原则三:分片规则需要慎重选择。    
       分片规则的选择,需要考虑数据的增长模式,数据的访问模式,分片关联性问题,以及分片扩容问题,最近的分片策略为范围分片,枚举分片,一致性 Hash 分片,这几种分片都有利于扩容
原则四:尽量不要在一个事务中的 SQL 跨越多个分片,分布式事务一直是个不好处理的问题
原则五:查询条件尽量优化,尽量避免 Select * 的方式,大量数据结果集下,会消耗大量带宽和 CPU 资源,查询尽量避免返回大量结果集,并且尽量为频繁使用的查询语句建立索引。

(2)数据拆分原则
1. 达到一定数量级才拆分( 800 万左右)。
2. 不到 800 万但跟大表(超 800 万的表)有关联查询的表也要拆分,在此称为大表关联表。
3. 大表关联表如何拆:
     小于 100 万的使用全局表;
     大于 100 万小于 800 万跟大表使用同样的拆分策略;
     无法跟大表使用相同规则的,可以考虑从 java 代码上分步骤查询,不用关联查询,或者采用特例使用全局表。
4. 使用全局表。
       全局表的作用:可充当数据字典表,这张数据表会在所有的数据库中存在,但对外而言,只是一个逻辑数据库存在的数据表,当对该表进行变更操作时,所有数据库的该表都会发生相应的变化。

       满足于global表的条件是:很少对表进行并发 update,如多线程同时 update 同一条 id=1 的记录(多线程 update,但不是操作同一行记录是可以的,多线程 update 全局表的同一行记录会死锁)。批量 insert没问题。
符合使用全局表的特征:
   变动不频繁,很少对表进行并发update
   数据量总体变化不大
   数据规模不大,很少有超过数十万条记录。

5. 拆分字段是不可修改的
6. 拆分字段只能是一个字段,如果想按照两个字段拆分,必须新建一个冗余字段,冗余字段的值使用两个字段的值拼接而成(如大区+年月拼成 zone_yyyymm 字段)。
7.拆分算法的选择和合理性评判:按照选定的算法拆分后每个库中单表不得超过 800 万
8. 能不拆的就尽量不拆。如果某个表不跟其他表关联查询,数据量又少,直接不拆分,使用单库即可。

(3)DataNode 的分布问题
DataNode 代表 MySQL 数据库上的一个 Database,因此一个分片表的 DataNode 的分布可能有以下3种:
1.都在一个 DataHost 上.
2. 在几个 DataHost 上,但有连续性,比如 dn1 到 dn5 在 Server1 上, dn6 到 dn10 在 Server2上,依次类推.
3. 在几个 DataHost 上,但均匀分布,比如 dn1,dn2,d3 分别在 Server1,Server2,Server3 上, dn4 到 dn6 又重复如此.
第一种情况不大推荐。至于是采用数据连续性分布还是数据均匀分布要看具体情况。如果是要考虑高并发的IO的分布、热数据处理、尽可能提高sql响应时间,采取数据均匀分布;如果是考虑业务数据冷热分布,方便进行数据归档,则考虑数据连续分布。

5、分片规则
分片规则用于定义数据与分片的路由关系,也就是 insert, delete, update, select的基本 sql 操作中,如何将 sql 路由到对应的分片执行。
在分片之前,应该考虑以下因素:
分片要考虑数据的增长情况,是实时大量增长还是缓慢增长,数据和数据之间的关联关系。
通过抓取系统中实际执行的SQL,分析SQL的运行规律,频率、响应时间、对系统性能和功能的影响程度。
要保证系统中不同数据表的可靠性,以及操作模式。
业务的特点:系统中哪些业务操作是严格事务的,哪些是普通事务或可以无事务的。
要考虑数据的备份模式,对系统的影响。

分片后,SQL性能的损耗,据测算大约20%左右,因此在作分片处理的时候需要考虑
分片数据如何插入,在插入数据的时候,应考虑到数据分布多节点的特点,根据分片规则定位数据对应到相应的节点,分解SQL进行插入。
分片数据如何关联查询,应尽量避免大的关联查询,尽量使用小的事务,尽量使用简单的查询,尽量在前端进行一些数据处理操作。
分片数据删除。由于数据是分布在各个数据节点,进行数据删除的时候,尽量避免join操作。应该对数据进行分析,根据分片规则定位数据,在对应分片删除相应数据。
分片数据修改。把查询条件识别出来,然后执行一下分片函数即可定位到节点,再执行对应的SQL即可,对了提高执行效率,尽量避免join操操作,必须避免对分片键进行修改的操作。

分片键的选择需要考虑
尽可能将数据均匀的分布到各个节点
该业务字段是最频繁的或者最重要的查询条件
尽量避免跨库join操作;
尽量减少表join操作,可以考虑全局表,可以考虑适当的冗余字段等。

常用分片规则算法:
取模分库:mod-long
范围分库:auto-sharding-long
Hash分库:hash-int
分片枚举:hash-int
截取数字 hash :sharding-by-stringhash
自然月分库:sharding-by-month
按日期(天):sharding-by-date
日期范围hash:range-date-hash
冷热数据:sharding-by-hotdate
ER模型分库:childTable
自定义分库:CustomRule(该方式需要自己实现分库算法)

6.配置文件:
conf目录下面的三个配置文件:
schema.xml中定义逻辑库,表、分片节点等内容,逻辑库标签、datanode标签、datahost标签;
rule.xml中定义分片规则,包括分片规则标签和分片函数标签;
server.xml中定义用户以及系统相关变量,包括system标签、用户标签、防火墙标签等;

---The end
10-22 23:40