需求
建设数据表TB_TEST
中数据量非常大,我们对TB_TEST
表进行数据分片,分为三个数据节点
环境准备
在Mycat环境搭建中,我们已经配置好了数据库,三台MySQL数据库实例分别为
192.168.0.55:3306
192.168.0.55:3307
192.168.0.55:3308
分别在这三个实例中创建数据库db1
create database db1;
配置schema.xml
schema.xml 作为MyCat中重要的配置文件之一,管理着MyCat的逻辑库、逻辑表以及对应的分片规则、DataNode以及DataSource。弄懂这些配置,是正确使用MyCat的前提
属性 | 含义 |
---|---|
schema | 标签用于定义MyCat实例中的逻辑库 |
table | 标签定义了MyCat中的逻辑表, rule用于指定分片规则,auto-sharding-long的分片规则是按ID值的范围进行分片 1-5000000 为第1片 5000001-10000000 为第2片… 具体设置我们会在第四节中讲解。 |
dataNode | 标签定义了MyCat中的数据节点,也就是我们通常说所的数据分片。 |
dataHost | 标签在mycat逻辑库中也是作为最底层的标签存在,直接定义了具体的数据库实例、读写分离配置和心跳语句。 |
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TEST_DB" checkSQLschema="true" sqlMaxLimit="100">
<table name="tb_test" dataNode="dn1,dn2,dn3" rule="auto-sharding-long"></table>
</schema>
<dataNode name="dn1" dataHost="host1" database="db1" />
<dataNode name="dn2" dataHost="host2" database="db1" />
<dataNode name="dn3" dataHost="host3" database="db1" />
<dataHost name="host1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="192.168.0.55:3306" user="root" password="123"></writeHost>
</dataHost>
<dataHost name="host2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="192.168.0.55:3307" user="root" password="123"></writeHost>
</dataHost>
<dataHost name="host3" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="192.168.0.55:3308" user="root" password="123"></writeHost>
</dataHost>
</mycat:schema>
配置server.xml
server.xml几乎保存了所有mycat需要的系统配置信息。最常用的是在此配置用户名、密码及权限。在system中添加UTF-8字符集设置,否则存储中文会出现问号
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="charset">utf8</property>
<property name="nonePasswordLogin">0</property>
<property name="ignoreUnknownCommand">0</property>
<property name="useHandshakeV10">1</property>
<property name="removeGraveAccent">1</property>
<property name="useSqlStat">0</property>
<property name="useGlobleTableCheck">0</property>
<property name="sqlExecuteTimeout">300</property>
<property name="sequnceHandlerType">1</property>
<property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>
<property name="subqueryRelationshipCheck">false</property>
<property name="sequenceHanlderClass">io.mycat.route.sequence.handler.HttpIncrSequenceHandler</property>
<property name="processorBufferPoolType">0</property>
<property name="handleDistributedTransactions">0</property>
<property name="useOffHeapForMerge">0</property>
<property name="memoryPageSize">64k</property>
<property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<property name="systemReserveMemorySize">384m</property>
<property name="useZKSwitch">false</property>
<property name="strictTxIsolation">false</property>
<property name="useZKSwitch">true</property>
<property name="parallExecute">0</property>
</system>
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">TEST_DB</property>
</user>
<user name="test">
<property name="password">123456</property>
<property name="schemas">TEST_DB</property>
</user>
</mycat:server>
在这里我们主要配置了system的字符集<property name="charset">utf8</property>
以及两个用户
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">TEST_DB</property>
</user>
<user name="test">
<property name="password">123456</property>
<property name="schemas">TEST_DB</property>
</user>
启动mycat
cd /usr/local/mycat/bin
./mycat start
访问mycat
mysql -h 192.168.0.55 -P 8066 -u root -p
- 查看库信息
show databases;
2. 选择库
use TEST_DB;
- 查看表信息
show tables;
这里显示的表是逻辑表,实际的数据库中并不存在这张表,可以使用select
进行查询验证
select * from tb_test;
返回一个错误信息,提示我们这张表并不存在
- 创建表
create table tb_test(id bigint(20) not null,title varchar(100) not null,primary key(id))engine=innodb default charset=utf8;
执行完成之后,我们分别连接三个分片数据库,可以看到三个数据库中的db1
库中都创建了tb_test
表
- 插入数据
insert into tb_test(id,title) values(1,'title1');
insert into tb_test(id,title) values(2,'title1');
insert into tb_test(id,title) values(3,'title1');
插入成功后,我们分别连接三个分片实例,发现上面插入的3条数据都存在了端口为3306的节点上,3307和3308节点并没有数据,出现这样的结果,是由于我们在schema.xml
中配置了分片规则auto-sharding-long
,这个分片规则声明在rule.xml
中,我们查看rule.xml
<tableRule name="auto-sharding-long">
<rule>
<columns>id</columns>
<algorithm>rang-long</algorithm>
</rule>
</tableRule>
这里显示对表中的id
列采用rang-long
的算法进行分片,而rand-long
的声明是
<function name="rang-long"
class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">autopartition-long.txt</property>
</function>
这里有一个规则匹配文件autopartition-long.txt
,其中的内容为
# range start-end ,data node index
# K=1000,M=10000.
0-500M=0
500M-1000M=1
1000M-1500M=2
根据文件的内容,我们可以看出当id为0-500M
时,数据存放在0
分片节点,500M-1000M
数据存放在1
分片节点,1000M-1500M
数据存放在2
分片节点
下面我们通过插入指定的id进行测试
insert into tb_test(id,title) values(5000001,'title5000001');
结果这条数据存储到了3307
节点上,而id为10000001
时
insert into tb_test(id,title) values(10000001,'title10000001');
数据存储到了3308
节点上,
当id值为15000001
时
insert into tb_test(id,title) values(15000001,'title15000001');
mycat
会提示错误信息
找不到符合分片算法的分片节点,当数据量到大了这个程度时,表示我们需要对分片节点进行扩展了。
原理介绍
mycat在运行过程中,拦截了用户发送的SQL语句,对SQL语句进行了特定的分析,如分片分析,路由分析,读写分离分析,缓存分析等,然后将SQL语句发送到后端真实的数据库,并将返回结果做适当的处理,最终再返回给用户
如图所示,user
表分为dn1,dn2,dn3三个分片节点,分片字段为status
,stats='0'
存储在0分片节点,status='1'
存储在1分片节点,status='2'
存储在2分片节点。
Mycat收到一条客户端发送的SQL语句后,首先解析SQL语句涉及的表,查看此表的定义,如果该表存在分片规则,则获取SQL语句中的分片字段的值,并匹配分片函数,得到该SQL语句对应的分片列表,然后将SQL语句发送到相应的分片去执行,最后处理所有分片返回的数据,并返回给客户端;
如发送SQL为select * from user where status='0'
,解析查到status='0'
,匹配分片函数,语句发送到0分片节点中去执行,如果发送的语句为select * from user where status in ('0','1')
,则SQL语句会发送到dn1,dn2两个分片节点上去执行,然后将结果合并后返回个给客户端。