server.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://org.opencloudb/">
<system>
<property name="defaultSqlParser">druidparser</property>
<property name="useCompression">1</property>
<property name="processorBufferPool">204800000</property> #BufferPool的大小
<property name="processorBufferChunk">40960</property> #每一个Buffer块的大小,processorBufferPool/processorBufferChun可以得到buffer块的数量
<property name="maxStringLiteralLength">65535</property>
<property name="sequnceHandlerType">0</property>
<property name="backSocketNoDelay">1</property>
<property name="frontSocketNoDelay">1</property>
<property name="mutiNodeLimitType">1</property>
<property name="mutiNodePatchSize">100</property>
<property name="processors">32</property> #可用线程数,实际上由于现在的多核CPU和超线程技术 酌情调高4-8倍
<property name="processorExecutor">32</property> #线程池是用来做异步处理逻辑的时候用的,对并发能力的影响相对较小
<property name="serverPort">3306</property> #定义访问端口
<property name="managerPort">9066</property> #定义管理端口
<property name="idleTimeout">300000</property> #超时时间
<property name="bindIp">0.0.0.0</property> #安全绑定IP
<property name="frontWriteQueueSize">4096</property>
<property name="processors">32</property>
</system>
<user name="root"> #访问账号
<property name="password">123456</property> #访问密码
<property name="schemas">chshop</property> #库文件名
</user>
<user name="root_read"> #只读账号
<property name="password">654321</property>
<property name="schemas">chshop</property>
<property name="readOnly">true</property> #只读模式
</user>
<quarantine>
<whitehost>
<host host="127.0.0.1" user="root"></host> #白名单
<host host="172.16.1.45" user="root"></host> #白名单允许那些IP能通过root访问
</whitehost>
<blacklist check="false"></blacklist> #黑名单
</quarantine>
</mycat:server>
server调整总结:
processors+processorExecutor会影响到MyCAT可用的线程数,虽然调高点会比较好,但是调的太高会导致频繁的上下文切换和软中断,在实际调整中,用top观察sys和si的百分比,如果服务器/虚拟机并没有什么不干净的后台程序和其他的服务在运行,sys在10%-15%之内,si在5%之内是比较理想的状态;
processorBufferPool+processorBufferChunk影响的server缓存,保持processorBufferChunk大小合理的情况下,增加buffer块的数量才是关键;
cacheservice配置-是SQL的缓存服务
#used for mycat cache service conf
factory.encache=org.opencloudb.cache.impl.EnchachePooFactory
#key is pool name ,value is type,max size, expire seconds
pool.SQLRouteCache=encache,10000,1800 #sql路由缓存,通过缓存SQL语句的路由信息,下次查询,不用再路由了,直接从缓存中获取路由信息,然后发到各个节点执行;
pool.ER_SQL2PARENTID=encache,1000,1800 #ER关系的缓存目前只是在Insert语句中才使用缓存,子表插入数据的时候根据joinKey值,判断父表所在分片,从而定位子表分片,分片信息put缓存layedpool.TableID2DataNodeCache=encache,10000,18000#表主键ID的路由缓存,为每一个表建一个缓存池,命名为TableID2DataNodeCache.TESTDB_表名,缓存key是id的值,value是节点名
layedpool.TableID2DataNodeCache.TESTDB_ORDERS=50000,18000
Cacheservice调整总结
SQLRouteCache的大小对具体的QPS有比较大的影响 参考: pool.SQLRouteCache=encache,1500000,60
rule.xml 配置
hash-int---在这一条切分规则的下面,有一个mapfile,这代表着,这个切分规则是根据partition-hash-int的内容来决定的,那么看一下这个文本文件
很简单的内容,这代表着切分使用的基准列里面,值为10000的时候,放在第一个DN里面(dn1),值为10010的时候,放在第二个DN里面(dn2) 可以看一下实际效果
看一下MyCAT的Debug日志,这两条语句被分配到了dn1和dn2上面,数据库里面也插入了相对应的数据
那么~问题来了(挖掘机滚粗~),如果插入的数据中,基准列的取值不是这个文件里面写明的值,会是什么效果?
直截了当的报错了~
好了,hash-int的这种切分规则,大体上可以理解为枚举分区,会比较适合于取值固定的场合,比如说性别(0,1),省份(固定值,短时间不会收复日本省吧~),渠道商 or 各种平台的ID
而且,用逗号分隔可以把多个值放在一个分区里面,所以可以根据实际的数据量/流量/访问量来综合制定切分策略;
缺点:毕竟不是全能战士
range-long ----仔细一看的话,和hash-int是比较像的,也是由特定的文件来决定切分策略,所以还是去看一下文件的内容
从文件内容可以看出,这是一种范围切分的方式,制定基准列的取值范围,然后把这一范围的所有数据都放到一个DN上面,这种方式和hash-int基本一致,就不截图了(懒癌晚期,时间不够了!)
这种切分策略,个人感觉在业务数据库里面的使用场景会少一些,因为这种切分方式需要预定好整体的数量,这就决定了那种无限增长的数据不能用这个,毕竟要改动这个切分策略会很麻烦
真要用起来,感觉也就对自增主键用,然后按照一定的数量来均匀切分,比如那种一天固定X条数据的业务(温度采集?数据采集?之类的情况),然后提前建好多个DN(库)。
当然,也存在一种潜在的问题,如果在短时间发生海量的顺序插入操作,而每一个DN(分库)设定的数量比较高(比如说一个DN设定的放1000W条数据),那么在这个时候,会出现某一个DN(分库)IO压力非常高,而其他几个DN(分库)完全没有IO操作,就会出现类似于DB中常见的热块/热盘的现象,而MySQL经常用自增主键,所以使得MySQL的表出现大量“顺序”插入的机会会多很多。
mod-long--从mod来看这应该是一种取余数的方法,来看一下具体配置的信息
count=4,这是代表着总共把数据切分成四份,一般是和具体的DN数量对应,从而达到把数据均匀的分布在四个DN上(当然,count<dn数量也没什么问题) 看一下实际的效果
看一下MyCAT的Debug日志,看看MyCAT是如何处理的
采用这种取余数的方式时,这四条数据分别插入了四个DN(库),而且可以看到,顺序插入时,数据是被均匀的分散在多个DN(库)上面
相比较于上面的range的方法,这种切分策略会更好的分散数据库写的压力,但是问题也很明显,一旦出现了范围查询,就需要MyCAT去合并结果,当数据量偏高的时候,这种跨库查询+合并结果消耗的时间有可能会增加很多,尤其是还出现了order by的时候。
所以这种切分策略会比较适合于单点查询的情景,比如说.....我也不知道......真的不知道,也许在银行,查询个人账户信息的时候,一些和用户信息的表可以做好冗余,然后利用这种方式来提供更为高效的查询(毕竟银行的用户数量多,恩恩~)
partition-by-long---处于range-long和mod-long之间的一个略微折中的划分策略,具体切分形势依照如下描述:
以1024为一个单位,每个DN存放partitionLength数量的数据,且,partitionCount x partitionLength=1024
看起来有点难以理解,形象点描述的话,以partitionCount(4) x partitionLength(256)为例,sid%1024=0-255的放在DN1,256-511的放在DN2,以此类推
试着以128为偏移值插入了八条数据,直接看MyCAT的日志
可以看到,八条数据均匀的分布在这四个DN里面~
值得一提的是,这种切分策略也支持非均匀分布~实在是测不动了,盗图两张~
这两张图基本上也说明白了这种非均匀分布的划分策略,重点还是在2x256+1x512=1024上面~
这种划分策略在range-long和mod-long之间取了一个折中点,同时,也还算是比较灵活,可以根据不同的情况进行非均匀划分,实际上能应用的场景会稍微多一点吧,或者说,不少场景都能用一用,相对减少了跨DN的情形,又把数据比较均匀的切分开来了,单点查询也不会太慢。
可以说,MyCAT的分库分表的重点,基本全部在这个rule里面体现了,表要不要分,表的数据怎么切分,都是需要根据实际业务来决定,充分根据业务的特点去决定最合适的划分策略
------------------------------------------------------------------------------------------
schema.xml 配置
- <?xml version=\"1.0\"?>
- <!DOCTYPE mycat:schema SYSTEM \"schema.dtd\">
- <mycat:schema xmlns:mycat=\"http://org.opencloudb/\">
- <schema name=\"mycat\" checkSQLschema=\"false\" sqlMaxLimit=\"100\">
- <!-- auto sharding by id (long) -->
- <table name=\"students\" dataNode=\"dn1,dn2,dn3,dn4\" rule=\"rule1\" />
- <table name=\"log_test\" dataNode=\"dn1,dn2,dn3,dn4\" rule=\"rule2\" />
- <!-- global table is auto cloned to all defined data nodes ,so can join
- with any table whose sharding node is in the same data node -->
- <!--<table name=\"company\" primaryKey=\"ID\" type=\"global\" dataNode=\"dn1,dn2,dn3\" />
- <table name=\"goods\" primaryKey=\"ID\" type=\"global\" dataNode=\"dn1,dn2\" />
- -->
- <table name=\"item_test\" primaryKey=\"ID\" type=\"global\" dataNode=\"dn1,dn2,dn3,dn4\" />
- <!-- random sharding using mod sharind rule -->
- <!-- <table name=\"hotnews\" primaryKey=\"ID\" dataNode=\"dn1,dn2,dn3\"
- rule=\"mod-long\" /> -->
- <!--
- <table name=\"worker\" primaryKey=\"ID\" dataNode=\"jdbc_dn1,jdbc_dn2,jdbc_dn3\" rule=\"mod-long\" />
- -->
- <!-- <table name=\"employee\" primaryKey=\"ID\" dataNode=\"dn1,dn2\"
- rule=\"sharding-by-intfile\" />
- <table name=\"customer\" primaryKey=\"ID\" dataNode=\"dn1,dn2\"
- rule=\"sharding-by-intfile\">
- <childTable name=\"orders\" primaryKey=\"ID\" joinKey=\"customer_id\"
- parentKey=\"id\">
- <childTable name=\"order_items\" joinKey=\"order_id\"
- parentKey=\"id\" />
- <ildTable>
- <childTable name=\"customer_addr\" primaryKey=\"ID\" joinKey=\"customer_id\"
- parentKey=\"id\" /> -->
- </schema>
- <!-- <dataNode name=\"dn\" dataHost=\"localhost\" database=\"test\" /> -->
- <dataNode name=\"dn1\" dataHost=\"localhost\" database=\"test1\" />
- <dataNode name=\"dn2\" dataHost=\"localhost\" database=\"test2\" />
- <dataNode name=\"dn3\" dataHost=\"localhost\" database=\"test3\" />
- <dataNode name=\"dn4\" dataHost=\"localhost\" database=\"test4\" />
- <!--
- <dataNode name=\"jdbc_dn1\" dataHost=\"jdbchost\" database=\"db1\" />
- <dataNode name=\"jdbc_dn2\" dataHost=\"jdbchost\" database=\"db2\" />
- <dataNode name=\"jdbc_dn3\" dataHost=\"jdbchost\" database=\"db3\" />
- -->
- <dataHost name=\"localhost\" maxCon=\"100\" minCon=\"10\" balance=\"1\"
- writeType=\"1\" dbType=\"mysql\" dbDriver=\"native\">
- <heartbeat>select user()<beat>
- <!-- can have multi write hosts -->
- <writeHost host=\"localhost\" url=\"localhost:3306\" user=\"root\" password=\"wangwenan\">
- <!-- can have multi read hosts -->
- <readHost host=\"hostS1\" url=\"localhost:3307\" user=\"root\" password=\"wangwenan\"/>
- </writeHost>
- <writeHost host=\"localhost1\" url=\"localhost:3308\" user=\"root\" password=\"wangwenan\">
- <!-- can have multi read hosts -->
- <readHost host=\"hostS11\" url=\"localhost:3309\" user=\"root\" password=\"wangwenan\"/>
- </writeHost>
- </dataHost>
- <!-- <writeHost host=\"hostM2\" url=\"localhost:3316\" user=\"root\" password=\"123456\"/> -->
- <!--
- <dataHost name=\"jdbchost\" maxCon=\"1000\" minCon=\"1\" balance=\"0\" writeType=\"0\" dbType=\"mongodb\" dbDriver=\"jdbc\">
- <heartbeat>select user()<beat>
- <writeHost host=\"hostM\" url=\"mongodb://192.168.0.99/test\" user=\"admin\" password=\"123456\" ></writeHost>
- </dataHost>
- -->
- <!--
- <dataHost name=\"jdbchost\" maxCon=\"1000\" minCon=\"10\" balance=\"0\"
- dbType=\"mysql\" dbDriver=\"jdbc\">
- <heartbeat>select user()<beat>
- <writeHost host=\"hostM1\" url=\"jdbc:mysql://localhost:3306\"
- user=\"root\" password=\"123456\">
- </writeHost>
- </dataHost>
- -->
- </mycat:schema>
第一行参数<schema name="mycat" checkSQLschema="false" sqlMaxLimit="100"/>
在这一行参数里面,schema name定义了可以在MyCAT前端显示的逻辑数据库的名字,
checkSQLschema这个参数为False的时候,表明MyCAT会自动忽略掉表名前的数据库名,比如说mydatabase1.test1,会被当做test1;
sqlMaxLimit指定了SQL语句返回的行数限制;
如截图,这个limit会让MyCAT在分发SQL语句的时候,自动加上一个limit,限制从分库获得的结果的行数,另外,截图右上角可以看到,MyCAT本身也是有缓存的;
那么,如果我们执行的语句要返回较多的数据行,在不修改这个limit的情况下,MyCAT会怎么做?
可以从截图看到,MyCAT完全就没搭理前端的实际需求,老老实实返回100条数据,所以如果实际应用里面需要返回大量数据,可能就得手动改逻辑了
MyCAT的1.4版本里面,用户的Limit参数会覆盖掉默认的MyCAT设置
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
<table name="students" dataNode="dn1,dn2,dn3,dn4" rule="rule1" />
这一行代表在MyCAT前端会显示哪些表名,类似几行都代表一样的意思,这里强调的是表,而MyCAT并不会在配置文件里面定义表结构
如果在前端使用show create table ,MyCAT会显示正常的表结构信息,观察Debug日志,
可以看到,MyCAT把命令分发给了dn1代表的数据库,然后把dn1的查询结果返回给了前端
可以判断,类似的数据库级别的一些查询指令,有可能是单独分发给某个节点,然后再把某个节点的信息返回给前端;
dataNode的意义很简单,这个逻辑表的数据存储在后端的哪几个数据库里面
rule代表的是这个逻辑表students的具体切分策略,目前MyCAT只支持按照某一个特殊列,遵循一些特殊的规则来切分,如取模,枚举等,具体的留给之后细说
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
<table name="item_test" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3,dn4" />
这一行代表的是全局表,这意味着,item_test这张表会在四个dataNode里面都保存有完整的数据副本,那么查询的时候还会分发到所有的数据库么?
结果如截图,MyCAT依然是规规矩矩的返回了100条数据(╮(╯_╰)╭),而针对全局表的查询,只会分发到某一个节点上
配置的primaryKey没发现作用在哪里,姑且忽略吧,以后发现了再补上
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
childtable我在测试中并没有实际用起来不过在MyCAT的设计文档里面有提到,childtable是一种依赖于父表的结构,
这意味着,childtable的joinkey会按照父表的parentKey的策略一起切分,当父表与子表进行连接,且连接条件是childtable.joinKey=parenttable.parentKey时,不会进行跨库的连接.
PS:具体测试以后再补
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
dataNode的参数在之前的篇章介绍过,这里直接跳过~
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
dataHost配置的是实际的后端数据库集群,大部分参数简单易懂,这里就不一个个介绍了,只介绍比较重要的两个参数,writeType和balance.
writeType和balance是用来控制后端集群的读写分离的关键参数,这里我用了双主双从的集群配置
这里的测试过程比较麻烦,所以直接贴结论:
1.balance=0时,读操作都在localhost上(localhost失败时,后端直接失败)
2.balance=1时,读操作会随机分散在localhost1和两个readhost上面(localhost失败时,写操作会在localhost1,如果localhost1再失败,则无法进行写操作)
3.balance=2时,写操作会在localhost上,读操作会随机分散在localhost1,localhost1和两个readhost上面(同上)
4.writeType=0时,写操作会在localhost上,如果localhost失败,会自动切换到localhost1,localhost恢复以后并不会切换回localhost进行写操作
5.writeType=1时,写操作会随机分布在localhost和localhost1上,单点失败并不会影响集群的写操作,但是后端的从库会无法从挂掉的主库获取更新,会在读数据的时候出现数据不一致
举例:localhost失败了,写操作会在localhost1上面进行,localhost1的主从正常运行,但是localhost的从库无法从localhost获取更新,localhost的从库于其他库出现数据不一致
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
实际上,MyCAT本身的读写分离是基于后端集群的同步来实现的,而MyCAT本身则提供语句的分发功能,当然,那个sqlLimit的限制也使得MyCAT会对前端应用层的逻辑造成一些影响
由schema到table的配置,则显示出MyCAT本身的逻辑结构里面,就包含了分库分表的这种特性(可以指定不同的表存在于不同的数据库中,而不必分到全部数据库)