1. 数据分片概念:
1.1. 分库分表
- 什么是分库分表:
- 将存放在一台数据库服务器中的数据,按照特定方式(指的是程序开发的算法)进行拆分,分散存放到多台数据库服务器中,以达到分散单台服务器负载的效果。
1.2. 水平分割
- 横向切分:
- 按照表中指定字段的切片规则,将表记录按行切分,分散存储到多个数据库中。
1.3. 垂直分割
- 纵向切分
- 将单个数据库的多个表按业务类型分类,分散存储到不同的数据库。
2. mycat软件介绍:
- mycat是基于Java的分布式数据库系统中间件,为高并发环境的分布式存储提供解决方案
- 适合数据大量写入的存储需求,不适合数据大量查询的请求。
- 支持MySQL、Oracle、SQL server、Mongodb等。
- 提供数据读写分离和数据分片服务。
- 基于阿里巴巴Cobar进行研发的开源软件
2.1. 分片规则
- mycat 支持提供10种分片规则
- 枚举法(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)
2.2. 工作流程
- 当mycat收到一个SQL命令时
- 解析SQL命令涉及到的表
- 然后看对表的配置,如果有分片规则,则获取SQL命令里分片字段的值,并匹配分片函数,获得分片列表
- 然后将SQL命令发往对应的分片服务器去执行
- 最后收集和处理所有分片结果数据,并返回到客户端
3. 配置数据分片服务
- 数据分片拓扑结构
- 注意部署这个架构至少需要三台数据库服务器
- IP规划
主机名 | 角色 | 数据库 | IP地址 |
---|---|---|---|
mysql10 | 客户端 | 无 | 192.168.2.10 |
mycat20 | 分片服务器 | 无 | 192.168.2.20 |
mysql30 | 数据库服务器 | db1 | 192.168.2.30 |
mysql40 | 数据库服务器 | db2 | 192.168.2.40 |
mysql50 | 数据库服务器 | db3 | 192.168.2.50 |
- 安装mysql这里就不说了,可以参考mysql安装。
- 修改所有服务器的主机名以IP规划的表格为准。
##修改所有主机的hosts文件
[root@mysql10 ~]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.2.10 mysql10
192.168.2.20 mycat20
192.168.2.30 mysql30
192.168.2.40 mysql40
192.168.2.50 mysql50
3.1. 安装mycat
##安装软件jdk,因为mycat软件需要jdk编译。
[root@mycat20 ~]# yum -y install java-1.8.0-openjdk
##查看java版本
[root@mycat20 ~]# java -version
openjdk version "1.8.0_362"
OpenJDK Runtime Environment (build 1.8.0_362-b08)
OpenJDK 64-Bit Server VM (build 25.362-b08, mixed mode)
##下载mycat二进制包
[root@mycat20 ~]# wget http://dl.mycat.org.cn/1.6-RELEASE/Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
##解压指定目录
[root@mycat20 ~]# tar -xf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz -C /usr/local/
##解压是否成功
[root@mycat20 ~]# cd /usr/local/
[root@mycat20 local]# ls mycat/
bin catlet conf lib logs version.txt
配置mycat环境变量:
[root@localhost ~]# echo "export PATH=/usr/local/mycat/bin:$PATH" >/etc/profile.d/mycat.sh
[root@localhost ~]# . /etc/profile.d/mycat.sh
[root@localhost ~]# echo $PATH
/usr/local/mycat/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
3.2. 目录结构
- ls /usr/local/mycat
- bin ----mycat命令
- catlet ----扩展功能
- conf ----配置文件
- lib ----mycat使用的jar包
- logs ----mycat启动日志和运行日志
- wrapper.log ----mycat服务启动日志
- mycat.log ----记录SQL脚本执行后报错内容
3.3. 修改主配置文件
- 重要配置文件说明
- server.xml ---设置连接账号及逻辑库
- schema.xml ---配置数据分片存储的表
- rule.xml ---分片规则
- 其他文件 ---分片规则配置文件
3.3.1. 创建连接用户(server.xml)
##备份server.xml文件
[root@mycat20 ~]# cd /usr/local/mycat/conf/
[root@mycat20 conf]# cp -r server.xml{,.bak}
##创建连接用户
.xml文件中<!-- -->代表注释。
[root@mycat20 conf]# vim server.xml
....
修改以下内容
<user name="root"> ---连接mycat服务用户名
<property name="password">1234</property> ---用户密码
<property name="schemas">mycatdb</property> ---逻辑库
<!-- 表级 DML 权限设置 -->
<!--
<privileges check="false">
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
-->
</user>
<user name="user">
<property name="password">1234</property>
<property name="schemas">mycatdb</property>
<property name="readOnly">true</property> ---只读权限
</user>
3.3.2. 定义分片的表(schema.xml)
##定义分片的表
<schema> ...... </schema> --定义分片信息,对什么表做分片
<table> ...... </table> --定义要分片的表
name --逻辑库名或逻辑表名
dataNode --指定数据节点名
rule --指定使用的分片规则
type=global --数据不做分片存储
##定义数据节点
<dataNode 选项=值,... .../> --定义数据节点
name --数据节点名
datahost --数据库服务器主机名
database --数据库名
##定义数据库服务器IP地址及端口
<datahost 选项=值,... ...>... ...</datahost> --服务器主机名
name --主机名(与datahost对应的主机名)
host --主机名(与IP地址对应的主机名)
url --数据库服务器IP地址及端口号
user --数据库服务器授权用户
password --授权用户密码
##备份schema.xml文件
[root@mycat20 conf]# cp -r schema.xml{,.bak}
[root@mycat20 conf]# vim schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="mycatdb" checkSQLschema="false" sqlMaxLimit="100">
<table name="travelrecord" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />
<table name="goods" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />
<table name="hotnews" dataNode="dn1,dn2,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>
</schema>
<dataNode name="dn1" dataHost="mysql30" database="db1" />
<dataNode name="dn2" dataHost="mysql40" database="db2" />
<dataNode name="dn3" dataHost="mysql50" database="db3" />
<dataHost name="mysql30" 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.2.30:3306" user="mycat"
password="1234">
</writeHost>
</dataHost>
<dataHost name="mysql40" 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.2.40:3306" user="mycat"
password="1234">
</writeHost>
</dataHost>
<dataHost name="mysql50" 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.2.50:3306" user="mycat"
password="1234">
</writeHost>
</dataHost>
</mycat:schema>
4. 配置数据库服务器
- 根据分片配置做相应的设置
- 添加授权用户
- 创建数据库
##创建授权用户
mysql> grant all on *.* to mycat@'%' identified by '1234'; ---在mysql30、mysql40、mysql50执行。
##创建数据库
mysql> create database db1 default chatset=utf8; ---在mysql30执行
mysql> create database db2 default charset=utf8; --在mysql40执行
mysql> create database db3 default charset=utf8; ---在mysql50执行
- 以上的操作是根据mycat配置文件schema.xml的内容
5. 启动mycat服务
[root@mycat20 conf]# mycat start
Starting Mycat-server...
[root@mycat20 conf]# netstat -nltp | grep 8066
tcp6 0 0 :::8066 :::* LISTEN 9719/java
6. 客户端连接分片服务器
[root@mysql10 ~]# mysql -uroot -p1234 -h192.168.2.20 -P8066
mysql> show databases;
+----------+
| DATABASE |
+----------+
| mycatdb | ---逻辑库
+----------+
mysql> use mycatdb
mysql> show tables;
+-------------------+
| Tables in mycatdb |
+-------------------+
| company | ---这些都是逻辑表
| customer |
| customer_addr |
| employee |
| goods |
| hotnews |
| orders |
| order_items |
| travelrecord |
+-------------------+
9 rows in set (0.00 sec)
-
登录分片服务器的用户名密码是图片上定义
-
分片服务器上的逻辑库和逻辑表是图片上定义的
7. 分片规则
7.1.枚举法(sharding-by-intfile)
- 字段值必须在规则文件定义的值里选择
[root@mycat20 conf]# vim schema.xml
<table name="employee" primaryKey="ID" dataNode="dn1,dn2,dn3"
rule="sharding-by-intfile" />
##可以看的出来employee这个表格必须有ID这个字段并且约束条件是主键分片规则是枚举法(sharding-by-intfile)
##查看枚举法的详细内容
[root@mycat20 conf]# vim rule.xml
<tableRule name="sharding-by-intfile">
<rule>
<columns>sharding_id</columns> --指定employee这个表格必须有sharding_id这个字段名
<algorithm>hash-int</algorithm> 算法
</rule>
....
##定义算法的
<function name="hash-int"
class="io.mycat.route.function.PartitionByFileMap"> ---算法
<property name="mapFile">partition-hash-int.txt</property> ---定义分片规则的值的配置文件
</function>
##查看sharding_id字段名的值范围
[root@mycat20 conf]# vim partition-hash-int.txt
10000=0 ---代表dn1
10010=1 ---代表dn2
10020=2 ---代表dn3
7.1.1. 重启mycat服务
[root@mycat20 conf]# mycat stop;mycat start
Stopping Mycat-server...
Stopped Mycat-server.
Starting Mycat-server...
7.1.2. 创建employee表
##登录分片服务器,创建employee表格
[root@mysql10 ~]# mysql -umycat -p1234 -h192.168.2.20 -P8066
mysql> create table employee(
-> ID int primary key auto_increment, ---schema.xml指定的字段名
-> sharding_id int not null, 分片规则指定字段
-> name char(20) not null,
-> sex enum('boy','girl') not null,
-> age int unsigned not null,
-> homedir char(50) not null);
Query OK, 0 rows affected (0.08 sec)
mysql> desc employee;
+-------------+--------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------------+------+-----+---------+----------------+
| ID | int(11) | NO | PRI | NULL | auto_increment |
| sharding_id | int(11) | NO | | NULL | |
| name | char(20) | NO | | NULL | |
| sex | enum('boy','girl') | NO | | NULL | |
| age | int(10) unsigned | NO | | NULL | |
| homedir | char(50) | NO | | NULL | |
+-------------+--------------------+------+-----+---------+----------------+
6 rows in set (0.09 sec)
##在mycat分片服务器上创建employee这个表格mysql30、mysql40、mysql50都应该有这个表格,这里就不显示出来了。
7.1.3. 验证枚举法
##往employee表格插入数据,sharding_id赋值为100010代表数据存储在mysql40上。
mysql> insert into employee(sharding_id,name,sex,age,homedir) values(10010,'bob','boy',29,'china');
##进入mysql40验证数据是否在。
mysql> select * from employee;
+----+-------------+------+-----+-----+-----------------------------------------+
| ID | sharding_id | name | sex | age | homedir |
+----+-------------+------+-----+-----+-----------------------------------------+
| 1 | 10010 | bob | boy | 29 | china |
+----+-------------+------+-----+-----+-----------------------------------------+
1 row in set (0.01 sec)
##还可以验证mysql30、mysql50的employee表格是否有这个数据,这里就不展示了,正确的应该这两台都没有。
7.2. 求模法(mod-long)
- 根据字段值与设定的数字求模结果存储数据,意思就是取余。
##查看hotnews表定义的内容:分片规则为求模法。
[root@mycat20 conf]# vim schema.xml
<table name="hotnews" dataNode="dn1,dn2,dn3"
rule="mod-long" />
##查看求模法定义的字段名以及算法范围
[root@mycat20 conf]# vim rule.xml
<tableRule name="mod-long">
39 <rule>
40 <columns>mod_id</columns>
41 <algorithm>mod-long</algorithm>
42 </rule>
43 </tableRule>
......
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
106 <!-- how many data nodes -->
107 <property name="count">3</property> --定义mod_id值除以3余数为0存储dn1,余数为1存储在dn2,余数为2存储在dn3。
108 </function>
7.2.1. 重启mycat服务
[root@mycat20 conf]# mycat stop; mycat start
Stopping Mycat-server...
Stopped Mycat-server.
Starting Mycat-server...
7.2.2. 创建hotnews表
mysql> create table hotnews( mod_id int not null, title char(20) not null, worker char(15) not null);
7.2.3. 验证求模法
##往hotnews表格插入数据
mysql> insert into hotnews(mod_id,title,worker) values(1,'system','bob');
##mod_id的值为1除以3余1所以数据存储在dn2(mysql40)
##在mysql40上查看数据
mysql> select * from hotnews;
+--------+--------+--------+
| mod_id | title | worker |
+--------+--------+--------+
| 1 | system | bob |
+--------+--------+--------+
1 row in set (0.00 sec)
##正确情况下mysql30和mysql50的hotnews表是没有数据的,这里不展示了。
8. 不分片存储(type=global)
##type=global意思是不用分片,存储数据在dn1、dn2、dn3上都存。
<table name="goods" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />
8.1. 创建表格goods并插入数据
mysql> create table goods(
-> ID int primary key auto_increment,
-> name char(15)not null,
-> sex enum('boy','girl')not null,
-> age int unsigned not null);
Query OK, 0 rows affected (0.02 sec)
##插入数据
mysql> insert into goods(name,sex,age) values('bob','boy',25),('andy','boy',19),('lucy','girl',18);
8.2.验证:
[root@mysql10 ~]# mysql -umycat -p1234 -h192.168.2.30 -e "select * from db1.goods"
mysql: [Warning] Using a password on the command line interface can be insecure.
+----+------+------+-----+
| ID | name | sex | age |
+----+------+------+-----+
| 1 | bob | boy | 25 |
| 2 | andy | boy | 19 |
| 3 | lucy | girl | 18 |
+----+------+------+-----+
[root@mysql10 ~]# mysql -umycat -p1234 -h192.168.2.40 -e "select * from db2.goods"
mysql: [Warning] Using a password on the command line interface can be insecure.
+----+------+------+-----+
| ID | name | sex | age |
+----+------+------+-----+
| 1 | bob | boy | 25 |
| 2 | andy | boy | 19 |
| 3 | lucy | girl | 18 |
+----+------+------+-----+
[root@mysql10 ~]# mysql -umycat -p1234 -h192.168.2.50 -e "select * from db3.goods"
mysql: [Warning] Using a password on the command line interface can be insecure.
+----+------+------+-----+
| ID | name | sex | age |
+----+------+------+-----+
| 1 | bob | boy | 25 |
| 2 | andy | boy | 19 |
| 3 | lucy | girl | 18 |
+----+------+------+-----+