Mycat是数据库中间件,就是介于数据库与应用之间,进行数据处理与交互的中间服务。
Mycat是一个开源的分布式数据库系统,用户可以将它看作是一个数据库代理,Mycat后端可以支持MysqL、sql SERVER、Oracle、DB2、Postgresql等主流数据库,也支持MongoDB这种Nosql方式的存储,在最终用户看来,无论是那种存储方式,在Mycat里都是一个传统的数据库,支持标准的sql语句进行数据的操作。
Mycat本身并不存储数据,数据是在后端存储的,因此数据可靠性及事务等都是后端保证的,默认端口是8066,建议对于分片表,尽量使用基础的sql,因为这样能达到最佳性能。
Mycat是一个强大的数据库中间件,不仅仅可以用作读写分离、以及分表分库、容灾备份,而且可以用于多租户应用开发、云平台基础设施、让我们的架构具备很强的适应性和灵活性。借助于ELK可以对数据访问瓶颈和热点一目了解然,根据这些统计分析数据,我们可以自动或手工调整后端存储,将不同的表映射到不同的存储引擎上,而整个应用的代码一行也不用改变。
Mycat拦截用户发过来的sql语句,首先去sql语句做一些特定分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此sql发往后端的真实数据库,并将返回结果做适当的处理,最终返回给用户。
Mycat应用场景
单纯的读写分离,此时配置最为简单,支持读写分离、主从切换
分表分库,对于超过1000万的表进行分片,最大支持1000亿的单表分片
多租户应用,每个应用一个库,但应用程序只连接mycat,从而不改造程序本身,实现多租户化
报表系统,借助于mycat的分表能力,处理大规模报表的统计
替代Hbase,分析大数据
-
作为海量数据实时查询的一种简单有效方案,比如100亿条频繁查询的记录需要3秒内查询出来结果,除了基于主键的查询,还可能存在范围查询或其他属性查询,此时mycat可能是最简单有效的选择。
mycat对数据进行分片处理之后,从原有的一个库,被切分为多个分片数据库,所有分片数据库集群构成了整个完整的数据库存储,此时,应用如果需要读取数据,就需要处理多个数据源的数据,如果没有数据库中间件,那么应用将直接面对分片集群、数据源切换、事务处理、数据聚合都需要应用直接处理,原本该是专注于业务的应用,将会花大量的工作来处理分片后的问题,最重要的是每个应用处理将是完全重复造轮子,所以有了数据库中间件,应用只需要集中与业务处理,大量的通用的数据聚合、事务、数据源切换都由中间件来处理,中间件的性能与处理能力将直接决定应用的读写性能,所以一款好的数据库中间件至关重要。
在云计算时代,数据库中间件可以以多租户的形式给一个或多个应用提供服务,每个应用访问的可能是一个独立或是共享的物理库,常见的如阿里云数据库服务器RDS。
关键概念
分片表(t_node就是属于分片表,数据按照规则被分到dn1,dn2两个分片节点上):
如上所示的配置就配置了两个不同的逻辑库,逻辑库的概念和MysqL数据库中的Database的概念相同。
schema标签相关属性
datanode
此属性用于绑定逻辑库到某个具体的database上,如果定义了这个属性,那么这个逻辑库就不能工作在分库分表模式下了,也就是说对这个逻辑库的所有操作会直接作用到绑定的datanode上,这个schema就可以用作读写分离和主从切换。
sqlschema="false" sqlMaxLimit="100" datanode="dn1">
现在USERDB就绑定到dn1所配置的具体database上,可以直接访问这个database,此属性只能绑定到一个database上,不能绑定多个dn.
checksqlschema
当该值设置为true时,如果我们执行语句
**select * from TESTDB.travelrecord;**
则mycat会把语句修改为
**select * from travelrecord;**
即把schema的字符去掉,避免发送到后端数据库执行时报
**(ERROR 1146 (42S02):Table 'testdb.travelrecord' doesn't exist)。**
不过,即使设置该值为true,如果语句所带的是并非是schema指定的名字,例
**select * from db1.travelrecord;**
那么mycat并不会删除db1这个字段,如果没有定义该库的话,则会报错。
sqlMaxLimit
当该值设置为某个数值时,每条执行的sql,如果没有加上limit语句,mycat会自动加上。
注意的是,如果运行的schema为非拆分库,那么该属性不会生效,需要手动添加limit语句。
table标签
Table标签定义了MYCAT中的逻辑表,所有需要拆分的表都要在这个标签中定义
属性名 | 值 | 数量限制 |
---|---|---|
name | string | (1) |
name定义逻辑表的表名,这个名字就如同我们在数据库中执行create table命令指的名字一样,同一个schema标签中定义的名字必须唯一。
datanode属性
定义此逻辑表所属的datanode,该属性的值需要和datanode标签中name属性的值相互对应,如果需要定义的dn过多可以使用如下的方法减少配置
<table></table>
atanode name="multipleDn" dataHost="localhost1" database="db$0-99">
atanode name="multipleDn2" dataHost="localhost1" database="db$0-99">
rule属性
此属性用于指定逻辑表要使用的规则名字,规则名字在rule.xml中定义,必须与tableRule标签中name属性值一一对应。
该属性用于指定表是否绑定分片规则,如果配置为true,但没有配置具体rule的话,程序会报错。
primaryKey属性
该逻辑表对应真实表的主键,例:分片的规则是使用非主键进行分片的,那么在使用主键查询的时候,就会发送查询语句到所有配置的DN上,如果使用该属性配置真实表的主键,那么mycat会缓存主键与具体DN的信息,那么再次使用非主键进行查询的时候就不会进行广播式的查询,就会直接发送语句给具体的DN.
type属性
该属性定义了逻辑表的类型,目前逻辑表只有全局表和普通表两种类型
全局表 global
普通表 不指定该值为global的所有表
autoIncrement属性
MysqL对非自增长主键,使用last_insert_id()是不会返回结果的,只会返回0.所以只有定义了自增长主键的表才可以用last_insert_id()返回主键值。
mycat目前提供了自增长主键功能,但是如果对应的MysqL节点上数据表,没有定义auto_increment,那么在mycat层调用last_insert_id()也是不会返回结果的。
由于insert操作的时候没有带入分片键,mycat会先取下这个表对应的全局序列,然后赋值给分片键,这样才能正常的插入到数据库中,最后使用last_insert_id()才会返回插入的分片键值。使用此功能,最好配合使用数据库模式的全局序列。
needAddLimit属性
此属性默认true,指定表是否需要自动的在每个语句后面加上limit限制,由于使用了分库分表,数据量有时会特别巨大,这时候执行查询语句,如果恰巧又忘记了加上数量限制的话,那么查询所有数据,会比较费时,所以mycat就自动为我们加上limit 100.
childTable标签
childTable用于定义ER分片的子表,通过标签上的属性与父表进行关联
数量限制 | |
---|---|
定义子表的表名
joinKey属性
插入子表的时候会使用这个列的值查找父表存储的数据节点
parentKey属性
此属性指定的值一般为与父表建立关联关系的列名,程序首先获取joinkey的值,再通过parentKey属性指定的列名产生查询语句,通过执行该语句得到父表存储在哪个分片上,从而确定子表存储的位置。
parmaryKey属性
同table标签所描述的
needAddLimit属性
同table标签所描述的
datanode标签
atanode name="dn1" dataHost="1ch3307" database="db1">
datanode标签定义了mycat中的数据节点,也就是我们通常说的数据分片,一个datanode标签就是一个独立的数据分片。
例子中所表述的意思为:使用名字为lch3307数据库实例上的db1物理数据库,这就组成一个数据分片,最后,我们使用名字dn1标识这个分片。
数量限制 | |
---|---|
定义数据节点的名字,这个名字需要是唯一的,我们需要在table标签上应用这个名字,来建立表与分片对应的关系。
dataHost属性
该属性用于定义该分片属于那个数据库实例的,属性值是引用dataHost标签上定义的name属性
database属性
该属性用于定义该分片属于哪个具体数据库实例上的具体库,因为这里使用两个纬度来定义分片,就是实例+具体的库,因为每个库上建立的表和结构是一样的,所以这样做就可以轻松的对表进行水平拆分。
dataHost标签
MysqL" dbDriver="native">
select user()
dataHost相关属性
数量限制 | |
---|---|
maxCon属性
指定每个读写实例连接池的最大连接,也就是说,标签内嵌套的writeHost、readHost标签都会使用这个属性的值来实例化出连接池的最大连接数。
minCon属性
指定每个读写实例连接池的最小连接,初始化连接池的大小
balance属性
负载均衡类型,目前的取值有3种
balance="0" 所有读操作都发送到当前可用的writeHost。
balance="1" 所有计操作都随机的发送到readHost。
balance="2" 所有读操作都随机的在writeHost、readHost上分发。
writeType属性
负载均衡类型,目前的取值有3种
writeType="0" 所有写操作都发送到可用的writeHost
writeType="1" 所有写操作都随机的发送到readHost.
writeType="2" 所有写操作都随机的在writeHost、readHost上分发
dbType属性
指定后端连接的数据库类型,目前支持二进制的MysqL协议,还有其他使用JDBC连接其他的数据库.
dbDriver属性
指定连接后端数据库使用的Driver,目前可选native和JDBC.使用native的话,因这个值执行的是二进制的MysqL协议。
heartbeat标签
这个标签内指明用于和后端数据库进行心跳检查的语句,例如MysqL可以使用select user(),oracle可以用select 1 from dual等
此标签还有一个connectionInitsql属性..
writeHost、readHost标签
这两个标签都指定后端数据库的相关配置给mycat,用于实例化后端连接池,唯一不同的是,writeHost指定写实例,readHost指定读实例。
在一个dataHost内可以定义多个writeHost和readHost,但是如果writeHost指定的后端数据库宕机,那么这个writeHost绑定的所有readHost都将不可用。另一方面,由于这个writeHost宕机系统会自动检测到,并切换到备用的writeHost上去。
数量限制 | |
---|---|
用于标识不同实例,一般writeHost我们使用*M1,readHost我们用*S1
url属性
native 格式 ip:port
jdbc 格式 jdbc:MysqL://ip:端口
user、password属性
存储实例的用户名和密码
server.xml
server.xml几乎保存了所有mycat需要的系统配置信息,其在代码内直接映射类为SystemConfig类。
user标签
定义登录mycat的用户和权限,property标签则是具体声明的属性值,schemas控制用户可访问的schema,同时访问多个用逗号隔开。
system标签
由于mycat最初是使用FoundationDB的sql解析器,而后才添加的Druid的解析器,所以这个属性用来指定默认的解析器,目前可用的取值有:druidparser和fdbparser。
processors属性
这个属性主要用于指定系统可用的线程数,默认值为Runtime.getRuntime().availableProcessors()方法返回的值,主要影响processorBufferPool、processorBufferLocalPercent、processorExecutor属性。NIOProcessor的个数也是由这个属性定义的,所以调优的时候可以适当的调高此属性。
processorBufferChunk属性
此属性指定每次分配Socket Direct Buffer的大小,默认是4096个字节,这个属性也影响buffer pool的长度。
processorBufferPool属性
此属性指定bufferPool计算比例值,由于每次执行NIO读、写操作都需要使用到buffer,系统初始化的时候会建立一定长度的buffer池来加快读、写的效率,减少建立buffer的时间。
Mycat中有两个主要的buffer池
BufferPool
ThreadLocalPool
BufferPool由ThreadLocalPool组合而成,每次从BufferPool中获取buffer都会优先获取ThreadLocalPool中的buffer,未命中之后才会去获取BufferPool中的buffer.也就是说ThreadLocalPool是作为BufferPool的二级缓存,每个线程内部自己使用的。
当然,这其中还有一些限制条件需要线程的名字是由$_开头,然而BufferPool上的buffer则是每个NIOProcessor都共享的。
默认此属性的值为:默认bufferChunkSize(4096)*processors属性*1000
BufferPool的总长度=bufferPool/bufferChunk
若bufferPool不是bufferChunk的整数倍,则总长度为前面计算得出的商+1
假设系统线程数为4,其他都为属性的默认值 ,则
bufferPool = 4096*4*1000
BufferPool的总长度: 4000 = bufferPool/4096s
processorBufferLocalPercent属性
前面提到了ThreadLocalPool,这个属性就是用来控制分配这个pool的大小用的,但其也并不是一个准确的值,也是一个比例值,默认为100.
线程缓存百分比=bufferLocalPercent/processors属性
例如,系统可以同时运行4个线程,使用默认值,则根据公式每个线程的百分比为25,最后根据这个百分比来计算出具体的ThreadLocalPool的长度公式如下
ThreadLocalPool的长度=线程缓存百分比BufferPool长度/100
假设BufferPool的长度为4000,其他保持默认值
那么最后每个线程建立的ThreadLocalPool的长度为:1000 = 254000/100
processorExecutor属性
此属性主要用于指定NIOProcessor上共享的businessExecutor固定线程池大小。mycat在需要处理一些异步逻辑的时候会把任务提交到这个线程池中,新版中这个连接池的使用频率不是很大,可以设置一个较小的值。
sequnceHandlerType属性
指定使用MYCAT全局序列的类型,0为本地文件方式,1为数据库方式,默认是本地文件方式,主要用于测试使用。
TCP连接的相关属性
StandardSocketOptions.SO_RCVBUF
StandardSocketOptions.SO_SNDBUF
StandardSocketOptions.TCP_NODELAY
上面的三个属性,分别由
frontSocketSoRcvbuf 默认值 1024*1024
frontSocketSoSndbuf 默认值 4*1024*1024
frontSocketNoDelay 默认值 1
backSocketSoRcvbuf 默认值 410241024
backSocketSoSndbuf 默认值 1024*1024
backSocketNoDelay 默认值 1
各自设置前后端TCP连接参数,MYCAT在每次建立前、后端连接的时候都会使用这些参数初始化连接,可以按系统要求适当调整这些buffer的大小。
packetHeaderSize:指定MysqL协议中的报文头长度,默认4
maxPacketSize:指定MysqL协议可以携带的数据最大长度,默认16M
idleTimeout:指定连接的空闲超时时间,某连接在发起空闲检查下,发现距离上次使用超过了空闲时间,那么这个连接会被回收,就是被直接的关闭掉,默认30分钟
charset:连接的初始化字符集 默认utf8
txIsolation:前端连接的初始化事务隔离级别,只在初始化的时候使用,后续会根据客户端传递过来的属性对后端数据库连接进行同步,默认为REPEATED_READ.
sqlExecuteTimeout:sql执行超时的时间,mycat会检查连接上最后一次执行sql的时间,若超过这个时间则会直接关闭这连接,默认时间为300秒
周期间隔相关属性
mycat中有几个周期性的任务来异步处理一些需要的工作,这些属性在系统调优的过程中是必不可少的。
processorCheckPeriod:清理NIOProcessor上前后端空闲、超时和关闭连接的间隔时间,默认是1秒
datanodeIdleCheckPeriod:对后端连接进行空闲、超时检查的时间间隔,默认是60秒
datanodeHeartbeatPeriod:对后端所有读、写库发起心跳的间隔时间,默认是10秒
服务相关属性
服务属性主要会影响外部系统对mycat的感知
bindIp:mycat服务监听的IP地址,默认值为0.0.0.0
serverPort:定义mycat的使用端口,默认值为8066
managerPort:定义mycat的管理端口,默认值为9066
rule.xml
rule.xml里定义了我们对表进行拆分所涉及到的规则定义,我们可以灵活的对表使用不同的分片算法,或者对表使用相同的算法但具体的参数不同。此文件里面主要有tableRule和function这两个标签。
tableRule标签
此标签定义表规则
id
func1
name属性指定唯一的名字,用于标识不同的表规则
内嵌的rule标签则指定对物理表中的哪一列进行拆分和使用什么路由算法
columns内指定要拆分的列名字
algorithm使用function标签中的name属性,连接表规则和具体路由算法,当然,多个表规则可以连接到同一个路由算法上。
function标签
name指定算法的名字
class指定路由算法具体的类名字
property为具体算法需要用到的一些属性
MyCAT的JOIN
在分布式环境中,跨分片的表关联是最复杂,最难解决的一个问题
inner join 内连接
left join 左连接
right join 右连接
cross join 交叉连接 得到的结果是两个表的乘积,即笛卡尔积
full join 产生所有记录
性能建议:
尽量避免使用left join和right join 而是用inner join.
在使用inner join时,on会优先执行,where条件在最后执行,因此在使用过程中,条件尽可能的在on语句中判断,减少where的执行
少用子查询,而用join
Mycat目前版本支持跨分片的join,主要实现的方式有四种.
全局表
ER分片
catletT(人工智能)
ShareJoin
全局表
将字典或符合字典表特性的一些表定义为全局表,从另外一个方面,很好的解决了数据join的难题。
全局表具有如下特性
配置
全局表配置比较简单,不用写Rule规则