数据分片概述
- 把数据存储在不同的数据库服务器里
- 适合数据存储比较多的业务场景
- 存在瓶颈和单点故障问题
1)分库/分表
- 将存放在一台数据库服务器中的数据,按照
特定方式
进行拆分,分散存放到多台
数据库服务器中,以达到分散单台服务器负载的效果
2)水平分割
- 横向切分:按照表中指定字段的分片规则,将表记录按
行
进行切分,分散存储到多个数据库中
3)垂直分割
- 纵向切分:将单个数据库的
多个表
按业务类型分类,分散存储到不同的数据库
MyCAT
MyCAT是基于JAVA的分布式数据库系统中间件
,为高并发
环境的分布式存储提供解决方案
- 适合数据大量写入的存储需求
- 支持MySQL/Oracle/Splserver/Mongodb等
- 提供数据分片服务
- 提供读写分离服务
- 基于阿里Cobar进行研发的开源软件
分片规则:
- 枚举法 sharding-by-intfile
- 固定分片 rule1
- 范围约定 auto-sharding-long
- 求模法 mod-long
- 日期列分区法 sharding-by-date
- 统配求模 sharding-by-pattern
- ASCII码求模通配 sharding-by-prefixpattern
- 编程指定 sharding-by-substring
- 字符串拆分hash解析 sharding-by-stringhash
- 一致性hash sharding-by-murmur
工作过程:
- 解析SQL命令涉及到的表
- 查看对表的配置,如果有分片规则,则获取SQL命令里分片字段的值,并匹配分片函数,获得分片列表
- 将SQL命令发往对应的数据库服务器去执行
- 收集和处理所有分片结果数据,并返回到客户端
部署MyCAT
1)部署软件运行环境
yum -y install java-1.8.0-openjdk.x86_64
2)安装服务软件
tar -xvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz #官网下载
mv mycat /usr/local/
目录结构:
名称 | 作用 |
---|---|
bin/ | mycat命令 |
catlet/ | 扩展功能 |
conf/ | 配置文件 |
lib/ | mycat使用的jar包 |
logs/ | mycat启动日志和运行日志 |
wrapper.log | mycat服务启动日志 |
mycat.log | 记录SQL脚本执行后的报错内容 |
conf目录文件:
文件 | 作用 |
---|---|
server.xml | 设置连接账号及逻辑库 |
schema.xml | 配置数据分片存储的表 |
rule.xml | 分片规则 |
其他文件 | 分片规则配置文件 |
3)修改配置文件
- 配置用户:
server.xml
vim /usr/local/mycat/conf/server.xml <!--根据需要配置-->
<user name="root"> <!--用户名-->
<property name="password">123456</property> <!--密码-->
<property name="schemas">TESTDB</property> <!--逻辑库名称-->
</user>
- 配置数据分片:
schema.xml
配置字段 | 作用 |
---|---|
<schema>…</schema> | 定义分片信息 |
<table>…</table> | 定义表 |
name | 逻辑库名或逻辑表名 |
dataNode | 指定数据节点 |
rule | 指定使用的分片规则 |
type=global | 数据不分片存储 |
sed -i '56,77d' /usr/local/mycat/conf/schema.xml
sed -i '39,42d' /usr/local/mycat/conf/schema.xml #删除多余的注释,可以不删
vim /usr/local/mycat/conf/schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
<!-- auto sharding by id (long) -->
<table name="travelrecord" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<!-- 设置表的数据存储节点,使用哪个分片规则 -->
<!-- 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,dn3" />
<!-- random sharding using mod sharind rule -->
<table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3"
rule="mod-long" />
<!-- <table name="dual" primaryKey="ID" dataNode="dnx,dnoracle2" type="global"
needAddLimit="false"/> <table name="worker" primaryKey="ID" dataNode="jdbc_dn1,jdbc_dn2,jdbc_dn3"
rule="mod-long" /> -->
<table name="employee" primaryKey="ID" dataNode="dn1,dn2,dn3"
rule="sharding-by-intfile" />
<table name="customer" primaryKey="ID" dataNode="dn1,dn2,dn3"
rule="sharding-by-intfile">
<childTable name="orders" primaryKey="ID" joinKey="customer_id"
parentKey="id">
<childTable name="order_items" joinKey="order_id"
parentKey="id" />
</childTable>
<childTable name="customer_addr" primaryKey="ID" joinKey="customer_id"
parentKey="id" />
</table>
<!-- <table name="oc_call" primaryKey="ID" dataNode="dn1$0-743" rule="latest-month-calldate"
/> -->
</schema>
<!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"
/> -->
<dataNode name="dn1" dataHost="mysql1" database="db1" />
<!-- 一个dataNode定义一个节点 -->
<!-- 节点名,节点主机名,存储数据的库 -->
<dataNode name="dn2" dataHost="mysql2" database="db2" />
<dataNode name="dn3" dataHost="mysql3" database="db3" />
<!-- 使用dataHost定义节点ip -->
<dataHost name="mysql1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="hostM1" url="192.168.4.53:3306" user="root"
password="123qqq...A">
<!-- 设置写主机名,ip,端口,用来连接数据库的用户,密码 -->
<!-- can have multi read hosts -->
<!-- <readHost host="hostS2" url="192.168.1.200:3306" user="root" password="xxx" /> -->
<!-- 不需要读写分离,注释掉readHost -->
</writeHost>
</dataHost>
<dataHost name="mysql2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM2" url="192.168.4.54:3306" user="root"
password="123qqq...A">
</writeHost>
</dataHost>
<dataHost name="mysql3" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM3" url="192.168.4.55:3306" user="root"
password="123qqq...A">
</writeHost>
</dataHost>
</mycat:schema>
4)配置数据库服务器
创建存储数据的库,授权用户,需要和schema.xml
文件中的配置对应!
[root@mysql1] mysql -uroot -pPASSWORD
mysql> create database db1;
mysql> grant all on *.* to root@'%' identified by '123qqq...A';
[root@mysql2] mysql -uroot -pPASSWORD
mysql> create database db2;
mysql> grant all on *.* to root@'%' identified by '123qqq...A';
[root@mysql3] mysql -uroot -pPASSWORD
mysql> create database db3;
mysql> grant all on *.* to root@'%' identified by '123qqq...A';
5)启动MyCAT服务
默认端口:8066
[root@mycat] /usr/local/mycat/bin/mycat --help #查看选项
[root@mycat] /usr/local/mycat/bin/mycat start #启动
Mycat-server is running (1553). #正常启动的输出信息
6)排错
启动失败:查找wrapper.log日志
数据读写错误:mycat.log日志
测试配置
存储数据:
建表:建表时表需要有使用分片规则的分片字段
[root@clinet] mysql -hMyCAT服务器ip -P8066 -uUSER -pPASSWORD
mysql> show databases; #逻辑库,server.xml中定义
+----------+
| DATABASE |
+----------+
| TESTDB |
+----------+
1 row in set (0.00 sec)
mysql> use TESTDB
mysql> show tables; #此处显示的为表名,在配置文件中定义了分片规则,需要create(创建字段),create时需要根据分片规则添加一个字段
+------------------+
| Tables in TESTDB |
+------------------+
| company |
| customer |
| customer_addr |
| employee |
| goods |
| hotnews |
| orders |
| order_items |
| travelrecord |
+------------------+
9 rows in set (0.00 sec)
常用分片规则
1)sharding-by-intfile
- 枚举法:字段值必须在规则文件定义的值里选择
以employee为例:
[root@mycat ~] cd /usr/local/mycat/conf
[root@mycat conf] vim schema.xml
<table name="employee" primaryKey="ID" dataNode="dn1,dn2,dn3"
rule="sharding-by-intfile" />
#该表的分片规则为sharding-by-intfile
[root@mycat conf] vim rule.xml #查找需要的字段名
<tableRule name="sharding-by-intfile">
<rule>
<columns>sharding_id</columns> #字段名
<algorithm>hash-int</algorithm> #使用的算法,根据这个算法名在function词条中找到存放取值的文件
</rule>
</tableRule>
...
<function name="hash-int"
class="io.mycat.route.function.PartitionByFileMap"> #算法程序
<property name="mapFile">partition-hash-int.txt</property> #记录取值范围的文件
</function>
[root@mycat conf] vim partition-hash-int.txt
10000=0 #等号左边为sharding_id字段的值,右边数字对应数据节点0 -> dn1
10010=1 #1 -> dn2
10020=2 #添加此行,对应dn3
[root@mycat conf] ../bin/mycat restart #重启服务应用配置文件
[root@clinet ~] mysql -hMyCAT服务器ip -P8066 -uUSER -pPASSWORD
mysql> create table employee (
-> ID int primary key auto_increment,
-> sharding_id int, #根据查到的结果创建
-> name varchar(15),
-> sex enum('man','woman'),
-> address varchar(50)
-> );
mysql> insert into employee(sharding_id,name,sex,address)
-> values(10000,"natasha","woman","usa"); #插入数据必须指定字段,不可省略
mysql> insert into employee(sharding_id,name,sex,address)
-> values(10020,"sarah","woman","usa"); #通过sharding_id字段的值指定该条信息存到哪个存储节点
mysql> insert into employee(sharding_id,name,sex,address)
-> values(10010,"bob","man","usa");
2)mod-long
- 求模法:根据字段值与设定的数字求模结果存储数据
以hotnews为例:
[root@mycat ~] cd /usr/local/mycat/conf
[root@mycat conf] vim schema.xml
<table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3"
rule="mod-long" />
#分片规则为mod-long
[root@mycat conf] vim rule.xml #查找需要的字段名
<tableRule name="mod-long">
<rule>
<columns>id</columns> #分片字段名,可以自定义
<algorithm>mod-long</algorithm> #算法
</rule>
</tableRule>
...
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">3</property> #有几个数据节点这个值设置为几
</function>
[root@mycat conf] ../bin/mycat restart #重启服务应用配置文件
[root@clinet ~] mysql -hMyCAT服务器ip -P8066 -uUSER -pPASSWORD
mysql> create table hotnews (
-> id int primary key auto_increment,
-> title varchar(50),
-> author char(10)
-> );
mysql> insert into hotnews(title,author)
-> values("NEWS","DZH");
附:自增长无法实现
方法1:多节点自增字段的处理
[root@mycat conf] vim server.xml
16 <property name="sequnceHandlerType">0</property>
#数值为0表示从文件配置里获取自增字段,数值为1代表从数据库获取,数值为2代表从本地时间戳获取
方法2:
建表时自增长字段的字符类型设置为bigint
,原因为默认自增字段的值从本地时间戳获取,值的大小超出了int
的范围
添加新库新表
[root@mycat conf] vim server.xml <!-- 添加新库 -->
<user name="root">
<property name="password">123456</property>
<property name="schemas">TESTDB,GAMEDB</property>
<!-- 写入新库名,以逗号分隔 -->
</user>
[root@mycat conf] vim schema.xml <!-- 该文件内的表名不能够重复,即使不属于同一个库 -->
<schema name="GAMEDB" checkSQLschema="false" sqlMaxLimit="100">
<!-- 添加新schema标签 -->
<table name="gamedb_user" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3"
rule="sharding-by-intfile" />
<table name="gamedb_pwd" primaryKey="ID" autoIncrement="true" type="global" dataNode="dn1,dn2,dn3" />
<!-- type=global表示不使用分片,把数据复制保存到每一个数据节点 -->
</schema>
[root@mycat conf] ../bin/mycat restart
读写分离
] vim server.xml
<user name="wordpress">
<property name="password">wordpress</property>
<property name="schemas">wordpress</property>
] vim schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="wordpress" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
</schema>
<dataNode name="dn1" dataHost="cluster1" database="db1" />
<dataHost name="cluster1" maxCon="1000" minCon="10" balance="3"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="192.168.1.170:3306" user="root"
password="123qqq...A">
<readHost host="hostS1" url="192.168.1.173:3306" user="root" password="123qqq...A" />
<readHost host="hostS2" url="192.168.1.174:3306" user="root" password="123qqq...A" />
<readHost host="hostS3" url="192.168.1.175:3306" user="root" password="123qqq...A" />
</writeHost>
<writeHost host="hostM2" url="192.168.1.171:3306" user="root"
password="123qqq...A">
</writeHost>
<writeHost host="hostM3" url="192.168.1.172:3306" user="root"
password="123qqq...A">
</writeHost>
<!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
</dataHost>
</mycat:schema>
sqlMaxLimit配置默认查询数量
database为真实数据库名
balance=“0”, 不开启读写分离机制,所有读操作都发送到当前可用的writeHost 上。
balance=“1”,全部的 readHost 与 stand by writeHost 参与 select 语句的负载均衡,简单的说,当双主双从模式(M1 ->S1 , M2->S2,并且 M1 与 M2 互为主备),正常情况下, M2,S1,S2 都参与 select 语句的负载均衡。
balance=“2”,所有读操作都随机的在 writeHost、 readhost 上分发。
balance=“3”, 所有读请求随机的分发到 wiriterHost 对应的 readhost 执行,writerHost 不负担读压力,注意 balance=3 只在 1.4 及其以后版本有, 1.3 没有。
writeType=“0”, 所有写操作发送到配置的第一个 writeHost,第一个挂了切到还生存的第二个writeHost,重新启动后已切换后的为准,切换记录在配置文件中:dnindex.properties .
writeType=“1”,所有写操作都随机的发送到配置的 writeHost。
writeType=“2”,没实现。
-1 表示不自动切换
1 默认值,自动切换
2 基于MySQL 主从同步的状态决定是否切换