文章目录
数据分片
数据分片的目的
在分布式存储系统中,数据需要分散存储在多台设备上,数据分片就是用来确定数据在多台存储设备上分布的技术
- 分布均匀: 即每台设备上的数据量要尽可能相近
- 负载均衡:即每台设备上的请求量要尽可能相近
- 扩缩容时产生的数据迁移尽可能少
数据库分割方式
- 水平分割 (横向切分)
- 按照表中指定字段的分片规则,将表记录按行切分,分散存储到多个数据库中
- 垂直分割(纵向切分)
- 将单个数据库的多个表按业务类型分类,分散存储到不同的数据库
不是切割表字段
- 将单个数据库的多个表按业务类型分类,分散存储到不同的数据库
mycat
mycat概述
mycat是基于Java的分布式数据库系统中间件,为高并发环境的分布式存储提供解决方案
- 适合数据大量写入的存储需求
- 支持Mysql、Oracle、Sqlserver、Mongdb等
- 提供数据读写分离服务
- 提供数据切片服务
- 基于Cobar进行研发的开源软件
mycat分片规则
- 枚举法 sharding-by-intfile
- 给字段赋值时,值只能在规定的范围内选择 根据给定的字段值分片数据存储到哪个节点
- 固定分片 rule1
- 范围约定 auto-sharding-long
- 求模法 mod-long
- 给字段取模 分配到对应的节点服务器
- 日期列分区法 sharding-by-date
- 通配取模 sharding-by-pattern
- ASCLL码求模通配 sharding-by-prefixpattern
- 编程指定 sharding-by-substring
- 字符串拆分hash解析 sharding-by-stringhash
- 一致性hash sharding-by-murmur
mycat工作原理
收到一个SQL命令时
- 解析SQL命令涉及到的表
- 然后看对表的配置,如果有分片规则,则获取SQL命令里分片字段的值,并匹配分片函数,获得分片列表
- 然后将SQL命令发往对应的数据库服务器去执行
- 最后收集和处理所有分片结果数据,并返回到客户端0
分片案例
前置准备
主机名 | 角色 | 数据库 | IP地址 |
---|---|---|---|
user | 客户端 | 无 | 192.168.4.50 |
db2 | 分片服务器 | 无 | 192.168.4.51 |
db3 | 数据库服务器 | db1 | 192.168.4.53 |
db4 | 数据库服务器 | db2 | 192.168.4.54 |
db5 | 数据库服务器 | db3 | 192.168.4.55 |
过程
#4.51 配置分片服务器
yum -y install java-1.8.0-openjdk #mycat基于Java
which java;java -version #确认安装和版本
/usr/bin/java
openjdk version "1.8.0_161" #版本对的
OpenJDK Runtime Environment (build 1.8.0_161-b14)
OpenJDK 64-Bit Server VM (build 25.161-b14, mixed mode)
tar -zxvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz -C /usr/local/ #解压mycat源码包
ls /usr/local/mycat/
bin catlet conf lib logs version.txt
#程序 复杂查询 配置文件 包 日志
mycat的server.xml rule.xml schema.xml log4j2.xml 四种配置文件的简述
- server.xml
- 配置系统参数
- 配置用户访问权限
- 配置SQL防火墙以及SQL拦截功能
- rule.xml
- 配置水平分片的分片规则
- 配置分片规则对应的分配函数
- schema.xml
- 配置逻辑库及逻辑表
- 配置逻辑表所存储的数据节点
- 配置数据节点所对应的物理数据库服务器的信息
- log4j2.xml
- 配置日志展示的格式
- 配置日志级别
- 等等
#4.51
#配置mycat配置文件
vim /usr/local/mycat/conf/server.xml #设置连接账号 测试不用修改啥
========================================
....
<user name="root"> #连接mycat服务时使用的用户名
<property name="password">123456</property> #用户连接mycat用户时使用的密码
<property name="schemas">TESTDB</property> #逻辑库名
</user>
<user name="user">
<property name="password">user</property>
<property name="schemas">TESTDB</property>
<property name="readOnly">true</property> #只读权限,连接mycat服务后只有读记录的权限,不写这一行则是可读可写
</user>
....
===================================================
vim /usr/local/mycat/conf/rule.xml #看看就好 没啥改的
#去除本次测试无关配置
cd /usr/local/mycat/conf/
wc -l schema.xml
77 schema.xml
sed -i '56,77d' /usr/local/mycat/conf/schema.xml #先删除下面的 先删除上面的话 行号会变
sed -i '39,42d' /usr/local/mycat/conf/schema.xml
sed -i '16,18d' /usr/local/mycat/conf/schema.xml
wc -l schema.xml
48 schema.xml #删除后为48
修改schema.xml
<!--4.51分片服务器操作-->
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">
<!--定义逻辑表属性 在哪个节点 使用上面分片规则-->
<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="mysqla" database="db1" />
<dataNode name="dn2" dataHost="mysqlb" database="db2" />
<dataNode name="dn3" dataHost="mysqlc" database="db3" />
<!--节点信息-->
<dataHost name="mysqla" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<!--心跳-->
<heartbeat>select user()</heartbeat>
<!--可读可写 如果内嵌readHost 就是定义读写分离-->
<writeHost host="hostM1" url="192.168.4.53:3306" user="wangxiaoming"
password="123456">
<!-- <readHost host="hostS2" url="192.168.1.200:3306" user="root" password="xxx" /> -->
</writeHost>
</dataHost>
<dataHost name="mysqlb" 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="wangxiaoming"
password="123456">
<!-- <readHost host="hostS2" url="192.168.1.200:3306" user="root" password="xxx" /> -->
</writeHost>
</dataHost>
<dataHost name="mysqlc" 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="wangxiaoming"
password="123456">
<!-- <readHost host="hostS2" url="192.168.1.200:3306" user="root" password="xxx" /> -->
</writeHost>
</dataHost>
</mycat:schema>
-
小结
- Mycat逻辑表table的九个属性介绍
- name:对应实际的表名
- dataNode:逻辑表所在的分片,该属性值需要和dataNode标签的name属性对应
- rule:逻辑表使用的分片规则名称,规则在conf/rule.xml中配置,该属性值必须与tableRule标签中的name属性对应
- ruleRequired:是否绑定分片规则,如果为true的话,就一定要配置true
- primaryKey:逻辑表对应真实表的主键
- type:逻辑表类型,分为全局表和普通表
- autoincrement:是否启用自增主键,对应Mysql自增主键,默认时禁用
- subTable:分表
- needAddLimit:是否允许自动添加schema标签中设置的limit,默认为true
- dataHost标签的balance属性
- 值为0:不开启读写分离机制,所有读操作都发送到当前可用的writehost上
- 值为1:全部的readhost与standby writehost参与select语句的负载均衡 都有
- 值为2:所有读操作都随机的在writehost、readhost上分发
- 值为3:所有读请求随机的分发到writehost对应的readhost执行 writehost不负担读压力,1.4版本及其之后才行
配置数据库服务器 授权
#4.53~55执行 create database db1; #53~55分别创建对应的数据库db1~db3 schema.xml对应的 grant all on *.* to wangxiaoming@'%' identified by '123456'; #授权 三台都弄 #可能会密码太简单无法设置 set global validate_password_policy=0; #密码策略为0,只检查密码长度 set global validate_password_length=6; #最短为6
测试授权用户是否成功
#4.51分片服务器测试 which mysql || yum -y install mariadb #看看有没有mysql命令 mysql -h192.168.4.53 -uwangxiaoming -p123456 #测试53~55
启动服务
#4.51启动服务 配置文件上面写好了 /usr/local/mycat/bin/mycat start ss -nutlp | grep :8066 #查看服务端口是否成功启动 tcp LISTEN 0 100 :::8066 :::* users:(("java",pid=108491,fd=76)) ps -C java #查看进程 PID TTY TIME CMD 108491 ? 00:00:02 java #有了
测试配置
#4.50测试 mysql -h192.168.4.51 -P8066 -uroot -p123456 #客户端访问分片服务器4.51:8066端口 #这个root账户是分片服务器的server.xml里面配置的 不是mysql的 mysql> show databases; #查看下 发现有逻辑数据库TESTDB了 +----------+ | DATABASE | +----------+ | TESTDB | +----------+ 1 row in set (0.01 sec) mysql> use TESTDB Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> show tables; #查看下 发现有逻辑表了 +------------------+ | Tables in TESTDB | +------------------+ | company | | customer | | customer_addr | | employee | | goods | | hotnews | | orders | | order_items | | travelrecord | +------------------+ 9 rows in set (0.00 sec)
- 这个root账户是分片服务器的server.xml里面配置的 不是mysql的
- 逻辑数据库在server.xml中定义
- 逻辑表在schema.xml中定义
- Mycat逻辑表table的九个属性介绍
分片规则案例
延续上面案例
枚举法 sharding-by-intfile
vim /usr/local/mycat/conf/rule.xml
==================================
<tableRule name="sharding-by-intfile">
<rule>
<columns>sharding_id</columns> #数据分片字段名
<algorithm>hash-int</algorithm> #使用的函数名
</rule>
</tableRule>
<function name="hash-int" #算法名
class="io.mycat.route.function.PartitionByFileMap"> #支持类型
<property name="mapFile">partition-hash-int.txt</property> #函数调用的配置文件
</function>
===================================
#枚举法
#修改partition-hash-int.txt
find / -type f -name partition-hash-int.txt #找到位置
vim /usr/local/mycat/conf/partition-hash-int.txt
=============================================
10000=0 #sharding_id字段值为10000时,数据存储在节点dn1
10010=1 #sharding_id字段值为10010时,数据存储在节点dn2
10020=2 #sharding_id字段值为10020时,数据存储在节点dn3
=======================================================
/usr/local/mycat/bin/mycat stop
/usr/local/mycat/bin/mycat start #重启服务
#测试 客户端4.50登录分片服务器 存入sharding_id值为上面三种的记录,再去三个节点服务器分别查看验证
本例子说明了:分片规则sharding-by-intfile定义在rule.xml 使用了hash-int算法,值列表文件为partition-hash-int.txt 里面左边值为字段值,右边是存储在哪个对应的服务器
求模法 mod-long
vim /usr/local/mycat/conf/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">
<property name="count">3</property> #指定求模数字为3
</function>
=================================================
/usr/local/mycat/bin/mycat stop
/usr/local/mycat/bin/mycat start #重启服务
#测试 客户端4.50登录分片服务器 存入id值取余0~2的记录,再去三个节点服务器分别查看验证
分片建表案例
创建表数据 延续上面
#4.50
mysql -h192.168.4.51 -uroot -p123456 -P8066
use TESTDB
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
show tables;
+------------------+
| Tables in TESTDB |
+------------------+
| company |
| customer |
| customer_addr |
| employee |
| goods |
| hotnews |
| orders |
| order_items |
| travelrecord |
+------------------+
9 rows in set (0.00 sec)
desc employee
ERROR 1146 (42S02): Table 'db1.employee' doesn't exist #不存在 重复多次 随机访问db1~3 上面设置了balance=0
#创建employee表 其中字段值要符合分片规则(schema.xml里面的table标签内容)
create table employee(ID int primary key auto_increment, sharding_id int,
name char(10), sex enum('boy','girl'));
desc employee #查看
+-------------+--------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------------+------+-----+---------+----------------+
| ID | int(11) | NO | PRI | NULL | auto_increment |
| sharding_id | int(11) | YES | | NULL | |
| name | char(10) | YES | | NULL | |
| sex | enum('boy','girl') | YES | | NULL | |
+-------------+--------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
#表有了
#查看下53~55上面是否有
mysql -uroot -p -e 'desc db1.employee' #查看对应数据库里面是否有employee表
+-------------+--------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------------+------+-----+---------+----------------+
| ID | int(11) | NO | PRI | NULL | auto_increment |
| sharding_id | int(11) | YES | | NULL | |
| name | char(10) | YES | | NULL | |
| sex | enum('boy','girl') | YES | | NULL | |
+-------------+--------------------+------+-----+---------+----------------+
#4.50插入记录 字段名不能省略 因为不是真实数据库 必须值对应字段名 上面看流程
insert into employee(sharding_id,name,sex) values(10000,'xiaoming',1);
insert into employee(sharding_id,name,sex) values(10010,'lansi',1);
insert into employee(sharding_id,name,sex) values(10020,'liuli',2);
Query OK, 3 rows affected (0.00 sec)
#没有指定9999字段值 所以报错了
insert into employee(sharding_id,name,sex) values(9999,'chunli',2);
ERROR 1064 (HY000): can't find any valid datanode :EMPLOYEE -> SHARDING_ID -> 9999
#分别取三个节点数据库查询 因为sharding_id分片规则指定字段是枚举法
#53
select * from employee;
+----+-------------+----------+------+
| ID | sharding_id | name | sex |
+----+-------------+----------+------+
| 1 | 10000 | xiaoming | boy |
+----+-------------+----------+------+
#54
select * from employee;
+----+-------------+-------+------+
| ID | sharding_id | name | sex |
+----+-------------+-------+------+
| 1 | 10010 | lansi | boy |
+----+-------------+-------+------+
#55
select * from employee;
+----+-------------+-------+------+
| ID | sharding_id | name | sex |
+----+-------------+-------+------+
| 1 | 10020 | liuli | girl |
+----+-------------+-------+------+
#枚举分布式存储
mod-long建表
#4.50
mysql -h192.168.4.51 -P8066 -uroot -p123456
use TESTDB;
create table hotnews(id int, title char(30), worker char(10), comment char(100));
#4.53~55查询表结构
desc hotnews
+---------+-----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-----------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| title | char(30) | YES | | NULL | |
| worker | char(10) | YES | | NULL | |
| comment | char(100) | YES | | NULL | |
+---------+-----------+------+-----+---------+-------+
#4.50插入数据 字段名不能省略 因为不是真实数据库 必须值对应字段名 上面看流程
insert into hotnews(id,title,worker,comment) values(7,'a','a','aaa');
insert into hotnews(id,title,worker,comment) values(11,'a','a','aaa');
insert into hotnews(id,title,worker,comment) values(9,'a','a','aaa');
#4.53~55验证查询
#4.53
select * from hotnews;
+------+-------+--------+---------+
| id | title | worker | comment |
+------+-------+--------+---------+
| 9 | a | a | aaa | # 9 % 3 = 0
+------+-------+--------+---------+
#4.54
select * from hotnews;
+------+-------+--------+---------+
| id | title | worker | comment |
+------+-------+--------+---------+
| 7 | a | a | aaa | # 7 % 3 = 1
+------+-------+--------+---------+
#4.55
select * from hotnews;
+------+-------+--------+---------+
| id | title | worker | comment |
+------+-------+--------+---------+
| 11 | a | a | aaa | # 11 % 3 = 2
+------+-------+--------+---------+