1.单机配置
mysql主从搭建
mkdir conf &mkdir data &mkdir log &cd conf#添加如下内容vi mymaster.cnf[mysqld]# [必须]服务器唯一ID,默认是1server-id=1#数据库默认字符集,主流字符集支持一些特殊表情符号(特殊表情符占用4个字节)character-set-server = utf8mb4#数据库字符集对应一些排序等规则,注意要和character-set-server对应collation-server = utf8mb4_general_ci#设置client连接mysql时的字符集,防止乱码init_connect='SET NAMES utf8mb4'#是否对sql语句大小写敏感,1表示不敏感lower_case_table_names = 1# [必须]启用二进制日志log-bin=mysql-bin#主从复制的格式(mixed,statement,row,默认格式是statement)binlog_format=mixed# 复制过滤:也就是指定哪个数据库不用同步(mysql库一般不同步)binlog-ignore-db=mysql# 设置需要同步的数据库 binlog_do_db = 数据库名;# 如果是多个同步库,就以此格式另写几行即可。# 如果不指明对某个具体库同步,表示同步所有库。除了binlog-ignore-db设置的忽略的库binlog_do_db = testdb #需要同步testdb数据库。# 确保binlog日志写入后与硬盘同步sync_binlog = 1#添加如下内容vi myslave.cnf[mysqld]# [必须]服务器唯一ID,默认是1,一般取IP最后一段server-id=2# log_slave_updates表示slave将复制事件写进自己的二进制日志log-slave-updates=1#开启二进制日志功能,以备Slave作为其它Slave的Master时使用log-bin=slave-binlog-bin-index=slave-bin.index#主从复制的格式(mixed,statement,row,默认格式是statement)binlog_format=mixed#relay_log配置中继日志relay_log=relay-bin#进制日志自动删除/过期的天数。默认值为0,表示不自动删除。expire_logs_days=14# 跳过所有的错误,继续执行复制操作#slave-skip-errors = all#防止改变数据read_only=1
#添加如下内容
vi docker-compose.yml
version: '3.3'
services:
mysql-master:
container_name: mysql-master
image: mysql:5.7
restart: always
ports:
- 3340:3306
volumes:
- ./log:/opt/log
- ./conf/mymaster.cnf:/etc/mysql/my.cnf
- ./data:/opt/data
environment:
MYSQL_ROOT_PASSWORD: "123456"
command: [
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_general_ci',
'--max_connections=3000'
]
networks:
- myweb
mysql-slave:
container_name: mysql-slave
image: mysql:5.7
restart: always
ports:
- 3341:3306
volumes:
- ./log:/opt/log
- ./conf/myslave.cnf:/etc/mysql/my.cnf
- ./data:/opt/data
environment:
MYSQL_ROOT_PASSWORD: "123456"
command: [
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_general_ci',
'--max_connections=3000'
]
networks:
- myweb
networks:
myweb:
driver: bridge
#启动服务
docker-compose up -d
#主从配置
docker exec -it mysql-master bash
mysql -uroot -p123456
show variables like '%server_id%';
show master status;
grant replication slave,replication client on *.* to 'slave'@'%' identified by "123456";
flush privileges;
docker exec -it mysql-slave bash
mysql -uroot -p123456
show variables like '%server_id%';
##注意此处ip是容器ip,不是宿主机ip
change master to master_host='172.22.0.2',master_user='slave',master_password='123456',master_port=3306,master_log_file='mysql-bin.000003', master_log_pos=610,master_connect_retry=30;
start slave;
show slave status \G;#两个YES成功
mycat搭建
#拉取镜像
docker pull fify/mycat
#添加如下内容
vi server.xml
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="serverPort">8066</property>
<!-- 1为开启实时统计、0为关闭 -->
<property name="useSqlStat">0</property>
<!-- 1为开启全加班一致性检测、0为关闭 -->
<property name="useGlobleTableCheck">0</property>
<property name="sequnceHandlerType">2</property>
<!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena-->
<property name="processorBufferPoolType">0</property>
<!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,但是记录分布式事务日志-->
<property name="handleDistributedTransactions">0</property>
<!-- off heap for merge/order/group/limit 1开启 0关闭 -->
<property name="useOffHeapForMerge">1</property>
<!-- 单位为m -->
<property name="memoryPageSize">1m</property>
<!-- 单位为k -->
<property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<!-- 单位为m -->
<property name="systemReserveMemorySize">384m</property>
</system>
<user name="root">
<property name="password">123456</property>
<property name="schemas">TESTDB</property>
</user>
<user name="user">
<property name="password">123456</property>
<property name="schemas">TESTDB</property>
<property name="readOnly">true</property>
</user>
</mycat:server>
#添加如下内容
vi schema.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"> </schema>
<!-- dataNode -->
<!--database属性设置你要连接的数据库-->
<dataNode name="dn1" dataHost="dh1" database="testdb" />
<dataHost name="dh1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<!-- 心跳语句 -->
<heartbeat>select user()</heartbeat>
<!-- 写实例 -->
<writeHost host="hostM1" url="192.168.99.101:3340" user="root" password="123456">
<!-- 读实例 -->
<readHost host="hostS1" url="192.168.99.101:3341" user="root" password="123456" />
</writeHost>
</dataHost>
</mycat:schema>
#启动服务
docker run --name mycat -p 8666:8066 -p 9666:9066 \
-v /home/docker/mycat/server.xml:/usr/local/mycat/conf/server.xml -v /home/docker/mycat/schema.xml:/usr/local/mycat/conf/schema.xml \
-d fify/mycat
测试
主从


mycat



手动修改从库数据,从mycat重新读取,发现默认走的从库。
代码配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
server:
port: 80
mybatis:
mapper-locations: classpath:mappers/*Mapper.xml
type-aliases-package: com.lymn.mycat
spring:
datasource:
url: jdbc:mysql://192.168.99.101:8666/TESTDB
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
@Data
public class User {
int id;
String name;
}
@Mapper
public interface UserMapper {
List<User> findList();
int insert(User user);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lymn.mycat.UserMapper">
<select id="findList" resultType="User">
select * from t_user
</select>
<insert id="insert" parameterType="User">
insert into t_user(ID,NAME)
values (#{id},#{name})
</insert>
</mapper>
@RestController@RequestMapping("/user")public class UserController { @Autowired private UserMapper userMapper; @RequestMapping("/list") public List<User> list() { return userMapper.findList(); } @RequestMapping("/add") public Integer add(User user) { return userMapper.insert(user); }}
http://127.0.0.1/user/add?id=1&name=jack
http://127.0.0.1/user/add?id=2&name=jerry
主从同步失效,目前数据是写入主库,读取数据是从库读。


2.集群配置

搭建
server.xml、mymatser.cnf myslave.conf#如单机配置,主从配置也如上面描述#添加如下内容vi schema.xml<?xml version="1.0" encoding="UTF-8" ?><!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" rule="auto-sharding-long" /> </schema> <!-- dataNode --> <!--database属性设置你要连接的数据库--> <dataNode name="dn1" dataHost="dh1" database="testdb" /> <dataNode name="dn2" dataHost="dh2" database="test" /> <dataHost name="dh1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <!-- 心跳语句--> <heartbeat>select user()</heartbeat> <!-- 写实例--> <writeHost host="hostM1" url="172.23.0.2:3306" user="root" password="123456"> <!-- 读实例 --> <readHost host="hostS1" url="192.168.99.101:3341" user="root" password="123456" /> </writeHost> </dataHost> <dataHost name="dh2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <!-- 心跳语句 --> <heartbeat>select user()</heartbeat> <!-- 写实例 --> <writeHost host="hostM2" url="172.23.0.6:3306" user="root" password="123456"> </writeHost> </dataHost></mycat:schema>
vi autopartition-long.txt
0-500M=0
500M-1500M=1
#添加如下内容
vi docker-compose.yml
version: '3.3'
services:
mycat-01:
container_name: mycat-01
image: fify/mycat
volumes:
- /home/docker/clustermycat/server.xml:/usr/local/mycat/conf/server.xml
- /home/docker/clustermycat/schema.xml:/usr/local/mycat/conf/schema.xml
- /home/docker/mycat/autopartition-long.txt:/usr/local/mycat/conf/autopartition-long.txt
ports:
- 8066:8066
- 9066:9066
networks:
- myweb
mycat-02:
container_name: mycat-02
image: fify/mycat
volumes:
- /home/docker/clustermycat/server.xml:/usr/local/mycat/conf/server.xml
- /home/docker/clustermycat/schema.xml:/usr/local/mycat/conf/schema.xml
- /home/docker/mycat/autopartition-long.txt:/usr/local/mycat/conf/autopartition-long.txt
ports:
- 8067:8066
- 9067:9066
networks:
- myweb
mysql-01:
container_name: mysql-01
image: mysql:5.7
restart: always
ports:
- 3340:3306
volumes:
- ./log:/opt/log
- ./conf/mymaster.cnf:/etc/mysql/my.cnf
- ./data:/opt/data
environment:
MYSQL_ROOT_PASSWORD: "123456"
command: [
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_general_ci',
'--max_connections=3000'
]
networks:
- myweb
mysql-02:
container_name: mysql-02
image: mysql:5.7
restart: always
ports:
- 3341:3306
volumes:
- ./log:/opt/log
- ./conf/myslave.cnf:/etc/mysql/my.cnf
- ./data:/opt/data
environment:
MYSQL_ROOT_PASSWORD: "123456"
command: [
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_general_ci',
'--max_connections=3000'
]
networks:
- myweb
mysql-03:
container_name: mysql-03
image: mysql:5.7
restart: always
ports:
- 3342:3306
environment:
MYSQL_ROOT_PASSWORD: "123456"
networks:
- myweb
networks:
myweb:
driver: bridge
#启动服务
docker-compose up -d
问题
Caused by: io.mycat.config.util.ConfigException: Illegal table conf : table [ TRAVELRECORD ] rule function [ rang-long ] partition size : 3 > table datanode size : 2, please make sure table datanode size = function partition size。
mycat默认配置的autopartition-long.txt里面自动帮我们配置了三个节点的数据,所以自己根据分片节点重新配置。

测试





高可用
| 服务名 | docker-ip地址 | docker-keepalived-vip-ip |
|---|---|---|
| mycat-01 | 172.23.0.10 | |
| mycat-02 | 172.23.0.11 | |
| mysql-01 | 172.23.0.2 | |
| mysql-02 | 172.23.0.4 | |
| mysql-03 | 172.23.0.6 | |
| haproxy-01 | 172.23.0.3(自动分配 未指定ip,此案例是172.23.0.3) | 172.23.0.15 |
| haproxy-02 | 172.23.0.5(自动分配 未指定ip,此案例是172.23.0.5) |
vi docker-compose.yml #修改集群配置
version: '3.3'
services:
mycat-01:
container_name: mycat-01
image: fify/mycat
volumes:
- /home/docker/clustermycat/server.xml:/usr/local/mycat/conf/server.xml
- /home/docker/clustermycat/schema.xml:/usr/local/mycat/conf/schema.xml
- /home/docker/mycat/autopartition-long.txt:/usr/local/mycat/conf/autopartition-long.txt
ports:
- 8066:8066
- 9066:9066
networks:
myweb:
ipv4_address: 172.23.0.10
mycat-02:
container_name: mycat-02
image: fify/mycat
volumes:
- /home/docker/clustermycat/server.xml:/usr/local/mycat/conf/server.xml
- /home/docker/clustermycat/schema.xml:/usr/local/mycat/conf/schema.xml
- /home/docker/mycat/autopartition-long.txt:/usr/local/mycat/conf/autopartition-long.txt
ports:
- 8067:8066
- 9067:9066
networks:
myweb:
ipv4_address: 172.23.0.11
mysql-01:
container_name: mysql-01
image: mysql:5.7
restart: always
ports:
- 3340:3306
volumes:
- ./log:/opt/log
- ./conf/mymaster.cnf:/etc/mysql/my.cnf
- ./data:/opt/data
environment:
MYSQL_ROOT_PASSWORD: "123456"
command: [
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_general_ci',
'--max_connections=3000'
]
networks:
myweb:
ipv4_address: 172.23.0.2
mysql-02:
container_name: mysql-02
image: mysql:5.7
restart: always
ports:
- 3341:3306
volumes:
- ./log:/opt/log
- ./conf/myslave.cnf:/etc/mysql/my.cnf
- ./data:/opt/data
environment:
MYSQL_ROOT_PASSWORD: "123456"
command: [
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_general_ci',
'--max_connections=3000'
]
networks:
myweb:
ipv4_address: 172.23.0.4
mysql-03:
container_name: mysql-03
image: mysql:5.7
restart: always
ports:
- 3342:3306
environment:
MYSQL_ROOT_PASSWORD: "123456"
networks:
myweb:
ipv4_address: 172.23.0.6
networks:
myweb:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.23.0.0/24
#启动服务
docker-compose up -d
docker pull haproxy:1.7
vi haproxy.cfg #haproxy配置
global
#工作目录
chroot /usr/local/etc/haproxy
#日志文件,使用rsyslog服务中local5日志设备(/var/log/local5),等级info
log 127.0.0.1 local5 info
#守护进程运行
daemon
defaults
log 127.0.0.1 local0 err #[err warning info debug]
mode http #默认的模式mode { tcp|http|health },tcp是4层,http是7层,health只会返回OK
retries 2 #两次连接失败就认为是服务器不可用,也可以通过后面设置
option redispatch #当serverId对应的服务器挂掉后,强制定向到其他健康的服务器
option abortonclose #当服务器负载很高的时候,自动结束掉当前队列处理比较久的链接
option dontlognull #日志中不记录负载均衡的心跳检测记录
maxconn 4096 #默认的最大连接数
timeout connect 5000ms #连接超时
timeout client 30000ms #客户端超时
timeout server 30000ms #服务器超时
#timeout check 2000 #=心跳检测超时
######## 监控界面配置 #################
listen admin_stats
#监控界面的访问的IP和端口
bind 0.0.0.0:8888
#访问协议
mode http
#URI相对地址
stats uri /dbs
#统计报告格式
stats realm Global\ statistics
#登陆帐户信息
stats auth admin:admin
########frontend配置##############
#mycat负载均衡
listen proxy-mycat
#访问的IP和端口
bind 0.0.0.0:9000
#网络协议
mode tcp
#负载均衡算法(轮询算法)
#轮询算法:roundrobin
#权重算法:static-rr
#最少连接算法:leastconn
#请求源IP算法:source
balance roundrobin
# 这里是容器中的IP地址,由于配置的是轮询roundrobin,weight 权重其实没有生效
server mycat-01 172.23.0.10:8066 check weight 1 maxconn 2000
server mycat-02 172.23.0.11:8066 check weight 1 maxconn 2000
# 使用keepalive检测死链
option tcpka
#启动服务
docker run -d -p 4001:8888 -p 9000:8066 -v /home/docker/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg --name haproxy01 --privileged --net=clustermycat_myweb haproxy:1.7
docker run -d -p 4002:8888 -p 9001:8066 -v /home/docker/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg --name haproxy02 --privileged --net=clustermycat_myweb haproxy:1.7
#同样的操作haproxy01、haproxy02docker exec -it haproxy01 /bin/bashhaproxy -f /usr/local/etc/haproxy/haproxy.cfghttp://宿主机IP:4001/dbs用户名:admin密码:admin#同样的操作haproxy01、haproxy02,安装keepalivedapt-get updateapt-get install keepalivedapt-get install net-toolsapt-get install iputils-pingvi keepalived.conf #配置keepalivedvrrp_instance VI_1 { #定义节点属性 state MASTER #定义虚拟网卡 interface eth0 #定义组vriid virtual_router_id 100 #定义权重 priority 100 #定义心跳检测时间1秒 advert_int 1 #定义组用户密码 authentication { auth_type PASS auth_pass 123456 } #定义docker内ip地址,必须要在和haproxy同一个网段 virtual_ipaddress { 172.23.0.15 }}service keepalived start #启动keepalivedip a#查看有无172.23.0.15

问题
ERROR: for c1a39a9747ad_mycat-02 Cannot start service mycat-02: Invalid address 172.18.0.11: It does not belong to any of this network’s subnets
ifconfig 查看已经存在的网段
测试
正常情况


停掉mycat-01


在mysql服务器进行测试连接


再停掉mycat-02

只停掉haproxy01




3.常用分片规则
字段求模
schema.xml
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mycat:schema SYSTEM "schema.dtd"><mycat:schema xmlns:mycat="http://io.mycat/"> <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"> <table name="orders" dataNode="dn1,dn2" rule="mod_rule" ></table> </schema> <!-- dataNode --> <!--database属性设置你要连接的数据库--> <dataNode name="dn1" dataHost="dh1" database="testdb" /> <dataNode name="dn2" dataHost="dh2" database="testdb" /> <dataHost name="dh1" 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.99.101:3340" user="root" password="123456"> </writeHost> </dataHost> <dataHost name="dh2" 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.99.101:3341" user="root" password="123456"> </writeHost> </dataHost></mycat:schema>
rule.xml
<!--在 rule 配置文件里新增分片规则 mod_rule,并指定规则适用字段为 customer_id,选择分片算法 mod-long(对字段求模运算)对两个节点求模,根据求模的结果分片--><tableRule name="mod_rule"><rule><columns>customer_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">2</property></function>
docker rm mycatdocker run --name mycat -p 8666:8066 -p 9666:9066 \-v /home/docker/mycat/server.xml:/usr/local/mycat/conf/server.xml \-v /home/docker/mycat/schema.xml:/usr/local/mycat/conf/schema.xml \-v /home/docker/mycat/rule.xml:/usr/local/mycat/conf/rule.xml \-d fify/mycat
CREATE TABLE orders(id INT AUTO_INCREMENT,order_type INT,customer_id INT,amount DECIMAL(10,2),PRIMARY KEY(id));INSERT INTO orders(id,order_type,customer_id,amount) VALUES (1,101,100,100100);INSERT INTO orders(id,order_type,customer_id,amount) VALUES(2,101,100,100300);INSERT INTO orders(id,order_type,customer_id,amount) VALUES(3,101,101,120000);INSERT INTO orders(id,order_type,customer_id,amount) VALUES(4,101,101,103000);INSERT INTO orders(id,order_type,customer_id,amount) VALUES(5,102,101,100400);INSERT INTO orders(id,order_type,customer_id,amount) VALUES(6,102,100,100020);

分片枚举
通过在配置文件中配置可能的枚举 id,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照省份或区县来做保存,而全国省份区县固定的,这类业务使用本条规则。
schema.xml
<table name="orders_ware_info" dataNode="dn1,dn2" rule="sharding_by_intfile" ></table>
rule.xml
<!-- columns:分片字段, algorithm:分片函数mapFile: 标识配置文件名称, type: 0为int型、 非0为StringdefaultNode: 默认节点:小于 0 表示不设置默认节点,大于等于 0 表示设置默认节点。设置默认节点如果碰到不识别的枚举值,就让它路由到默认节点,如不设置不识别就报错--><tableRule name="sharding_by_intfile"><rule><columns>areacode</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><property name="type">1</property><property name="defaultNode">0</property></function>
partition-hash-int.txt
110=0120=1
docker rm mycatdocker run --name mycat -p 8666:8066 -p 9666:9066 \-v /home/docker/mycat/server.xml:/usr/local/mycat/conf/server.xml \-v /home/docker/mycat/schema.xml:/usr/local/mycat/conf/schema.xml \-v /home/docker/mycat/rule.xml:/usr/local/mycat/conf/rule.xml \-v /home/docker/mycat/partition-hash-int.txt:/usr/local/mycat/conf/partition-hash-int.txt \-d fify/mycat
CREATE TABLE orders_ware_info
(
`id` INT AUTO_INCREMENT COMMENT '编号',
`order_id` INT COMMENT '订单编号',
`address` VARCHAR(200) COMMENT '地址',
`areacode` VARCHAR(20) COMMENT '区域编号',
PRIMARY KEY(id)
);
INSERT INTO orders_ware_info(id, order_id,address,areacode) VALUES (1,1,'北京','110');
INSERT INTO orders_ware_info(id, order_id,address,areacode) VALUES (2,2,'天津','120');
INSERT INTO orders_ware_info(id, order_id,address,areacode) VALUES (3,3,'北京','110');
INSERT INTO orders_ware_info(id, order_id,address,areacode) VALUES (4,4,'上海','130');


如果去掉0

范围约定
此分片适用于提前规划好分片字段某个范围属于哪个分片。
schema.xml
<table name="payment_info" dataNode="dn1,dn2" rule="auto_sharding_long" ></table>
rule.xml
<!-- columns:分片字段, algorithm:分片函数
mapFile: 标识配置文件名称, type: 0为int型、 非0为String
defaultNode: 默认节点:小于 0 表示不设置默认节点,大于等于 0 表示设置默认节点。设置默认节点如果碰到不识别的值,就让它路由到默认节点,如不设置不识别就报错
-->
<tableRule name="auto_sharding_long">
<rule>
<columns>order_id</columns>
<algorithm>rang-long</algorithm>
</rule>
</tableRule>
<function name="rang-long"
class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">autopartition-long.txt</property>
<property name="defaultNode">0</property>
</function>
autopartition-long.txt
0-102=0
103-200=1
docker rm mycat
docker run --name mycat -p 8666:8066 -p 9666:9066 \
-v /home/docker/mycat/server.xml:/usr/local/mycat/conf/server.xml \
-v /home/docker/mycat/schema.xml:/usr/local/mycat/conf/schema.xml \
-v /home/docker/mycat/rule.xml:/usr/local/mycat/conf/rule.xml \
-v /home/docker/mycat/autopartition-long.txt:/usr/local/mycat/conf/autopartition-long.txt \
-d fify/mycat
CREATE TABLE payment_info
(
`id` INT AUTO_INCREMENT COMMENT '编号',
`order_id` INT COMMENT '订单编号',
`payment_status` INT COMMENT '支付状态',
PRIMARY KEY(id)
);
INSERT INTO payment_info (id,order_id,payment_status) VALUES (1,101,0);
INSERT INTO payment_info (id,order_id,payment_status) VALUES (2,102,1);
INSERT INTO payment_info (id,order_id ,payment_status) VALUES (3,103,0);
INSERT INTO payment_info (id,order_id,payment_status) VALUES (4,104,1);
INSERT INTO payment_info (id,order_id,payment_status) VALUES (5,2000,1);

按日期(天)分片
此规则为按天分片。 设定时间格式、范围
schema.xml
<table name="login_info" dataNode="dn1,dn2" rule="sharding_by_date" ></table>
rule.xml
<!-- columns:分片字段, algorithm:分片函数
#dateFormat :日期格式
#sBeginDate :开始日期
#sEndDate:结束日期,则代表数据达到了这个日期的分片后循环从开始分片插入
#sPartionDay :分区天数,即默认从开始日期算起,分隔 2 天一个分区
-->
<tableRule name="sharding_by_date">
<rule>
<columns>login_date</columns>
<algorithm>shardingByDate</algorithm>
</rule>
</tableRule>
<function name="shardingByDate" class="io.mycat.route.function.PartitionByDate">
<property name="dateFormat">yyyy-MM-dd</property>
<property name="sBeginDate">2019-01-01</property>
<property name="sEndDate">2019-01-04</property>
<property name="sPartionDay">2</property>
</function>
CREATE TABLE login_info
(
`id` INT AUTO_INCREMENT COMMENT '编号',
`user_id` INT COMMENT '用户编号',
`login_date` DATE COMMENT '登录日期',
PRIMARY KEY(id)
);
INSERT INTO login_info(id,user_id,login_date) VALUES (1,101,'2019-01-01');
INSERT INTO login_info(id,user_id,login_date) VALUES (2,102,'2019-01-02');
INSERT INTO login_info(id,user_id,login_date) VALUES (3,103,'2019-01-03');
INSERT INTO login_info(id,user_id,login_date) VALUES (4,104,'2019-01-04');
INSERT INTO login_info(id,user_id,login_date) VALUES (5,103,'2019-01-05');
INSERT INTO login_info(id,user_id,login_date) VALUES (6,104,'2019-01-06');
INSERT INTO login_info(id,user_id,login_date) VALUES (7,104,'2019-01-07');
INSERT INTO login_info(id,user_id,login_date) VALUES (8,104,'2019-01-09');
INSERT INTO login_info(id,user_id,login_date) VALUES (9,104,'2019-01-11');

4.分片join与全局序列
分片join
通过全局表+基于 E-R 关系的分片策略, Mycat 可以满足 80%以上的企业应用开发。

ER 表
子表的记录与所关联的父表记录存放在同一个数据分片上。
schema.xml
<table name="orders" dataNode="dn1,dn2" rule="mod_rule" >
<childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
</table>
CREATE TABLE orders_detail(
id INT AUTO_INCREMENT,
detail VARCHAR(2000),
order_id INT,
PRIMARY KEY(id)
);
INSERT INTO orders_detail(id,detail,order_id) VALUES(1,'detail1',1);
INSERT INTO orders_detail(id,detail,order_id) VALUES(2,'detail1',2);
INSERT INTO orders_detail(id,detail,order_id) VALUES(3,'detail1',3);
INSERT INTO orders_detail(id,detail,order_id) VALUES(4,'detail1',4);
INSERT INTO orders_detail(id,detail,order_id) VALUES(5,'detail1',5);
INSERT INTO orders_detail(id,detail,order_id) VALUES(6,'detail1',6);
SELECT o.*,od.detail FROM orders o INNER JOIN orders_detail od ON o.id=od.order_id;

全局表
当业务表因为规模而进行分片以后,业务表与这些附属的字典表之间的关联,就成了比较棘手的问题。考虑到字典表变动不频繁、数据量总体变化不大,鉴于此Mycat 定义了一种特殊的表,称之为“全局表”。全局表具有以下特性:
- 全局表的插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性
- 全局表的查询操作,只从一个节点获取
- 全局表可以跟任何一个表进行 JOIN 操作
schema.xml
<table name="dict_order_type" dataNode="dn1,dn2" type="global" ></table>
CREATE TABLE dict_order_type(id INT AUTO_INCREMENT,order_type VARCHAR(200),PRIMARY KEY(id));INSERT INTO dict_order_type(id,order_type) VALUES(101,'type1');INSERT INTO dict_order_type(id,order_type) VALUES(102,'type2');

全局序列
在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一。为此, Mycat 提供了全局 sequence,并且提供了包含本地配置和数据库配置等多种实现方式。
本地文件
Mycat 将 sequence 配置到文件中,当使用到 sequence 中的配置后, Mycat 会更下classpath 中的 sequence_conf.properties 文件中 sequence 当前的值。
优点: 本地加载,读取速度较快
缺点: 抗风险能力差, Mycat 所在主机宕机后,无法读取本地文件。
server.xml
<property name="sequnceHandlerType">0</property>

INSERT INTO orders(id,amount,customer_id,order_type) VALUES(NEXT VALUE FOR MYCATSEQ_ORDER,1000,98,102);
INSERT INTO orders(id,amount,customer_id,order_type) VALUES(NEXT VALUE FOR MYCATSEQ_ORDER,1000,99,102);



数据库方式
利用数据库一个表 来进行计数累加。并不是每次生成序列都读写数据库,Mycat 会预加载一部分号段到 Mycat 的内存中,这样大部分读写序列都是在内存中完成的。如果内存中的号段用完了 Mycat 会再向数据库要一次。
Mycat 意外崩溃重启后,向数据库申请新的号段,原有号段会弃用。
#在 dn1 上创建全局序列表
CREATE TABLE MYCAT_SEQUENCE (NAME VARCHAR(50) NOT NULL,current_value INT NOT
NULL,increment INT NOT NULL DEFAULT 100, PRIMARY KEY(NAME)) ENGINE=INNODB;
#创建全局序列所需函数
DELIMITER $$
CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS VARCHAR(64)
DETERMINISTIC
BEGIN
DECLARE retval VARCHAR(64);
SET retval="-999999999,null";
SELECT CONCAT(CAST(current_value AS CHAR),",",CAST(increment AS CHAR)) INTO retval FROM
MYCAT_SEQUENCE WHERE NAME = seq_name;
RETURN retval;
END $$
DELIMITER ;
DELIMITER $$
CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50),VALUE INTEGER) RETURNS
VARCHAR(64)
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = VALUE
WHERE NAME = seq_name;
RETURN mycat_seq_currval(seq_name);
END $$
DELIMITER ;
DELIMITER $$
CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS VARCHAR(64)
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = current_value + increment WHERE NAME = seq_name;
RETURN mycat_seq_currval(seq_name);
END $$
DELIMITER ;
#初始化序列表记录
INSERT INTO MYCAT_SEQUENCE(NAME,current_value,increment) VALUES ('ORDERS', 400000,
100);
sequence_db_conf.properties
GLOBAL=dn1
COMPANY=dn1
CUSTOMER=dn1
#ORDERS这个序列在dn1这个节点上
ORDERS=dn1
server.xml
<!--#全局序列类型: 0-本地文件, 1-数据库方式, 2-时间戳方式。-->
<property name="sequnceHandlerType">1</property>
#插入多次insert into orders(id,amount,customer_id,order_type) values(next value for MYCATSEQ_ORDERS,1000,101,102);#重启mycat后INSERT INTO orders(id,amount,customer_id,order_type) VALUES(NEXT VALUE FOR MYCATSEQ_ORDERS,1000,100,102);



时间戳方式
全局序列ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加) 换算成十进制为 18 位数的long 类型,每毫秒可以并发 12 位二进制的累加。
优点: 配置简单
缺点: 18 位 ID 过长
server.xml
<property name="sequnceHandlerType">0</property>



报错:Out of range value for column ‘id’ at row 1
调整id长度
分布式 ZK ID 生成器
自主生成全局序列
在 java 项目里自己生成全局序列,如下:
根据业务逻辑组合
可以利用 redis 的单线程原子性 incr 来生成序列,但自主生成需要单独在工程中用 java 代码实现, 还是推荐使用 Mycat 自带全局序列。
5.Mycat 安全设置
权限配置
user 标签权限控制
<user name="mycat"> <property name="password">123456</property> <property name="schemas">TESTDB</property></user><user name="user"> <property name="password">user</property> <property name="schemas">TESTDB</property> <property name="readOnly">true</property></user>

privileges 标签权限控制
在 user 标签下的 privileges 标签可以对逻辑库(schema)、表(table)进行精细化的 DML 权限控制。
privileges 标签下的 check 属性,如为 true 开启权限检查,为 false 不开启,默认为 false。
<user name="mycat"> <property name="password">123456</property> <property name="schemas">TESTDB</property> <!-- 表级 DML 权限设置 --> <privileges check="true"> <schema name="TESTDB" dml="1111" > <!--配置orders表没有增删改查权限--> <table name="orders" dml="0000"></table> </schema> </privileges></user>
| DML 权限 | 增加(insert) | 更新(update) | 查询(select) | 删除(select) |
|---|---|---|---|---|
| 0000 | 禁止 | 禁止 | 禁止 | 禁止 |
| 0010 | 禁止 | 禁止 | 可以 | 禁止 |
| 1110 | 可以 | 可以 | 可以 | 禁止 |
| 1111 | 可以 | 可以 | 可以 | 可以 |
SQL 拦截
firewall 标签用来定义防火墙; firewall 下 whitehost 标签用来定义 IP 白名单 , blacklist 用来定义SQL 黑名单。
白名单
可以通过设置白名单, 实现某主机某用户可以访问 Mycat,而其他主机用户禁止访问。
<!--#设置白名单#server.xml配置文件firewall标签#配置只有192.168.140.128主机可以通过mycat用户访问--><firewall> <whitehost> <host host="192.168.99.1101" user="mycat"/> </whitehost></firewall>
黑名单
可以通过设置黑名单, 实现 Mycat 对具体 SQL 操作的拦截, 如增删改查等操作的拦截。
<!--
#设置黑名单
#server.xml配置文件firewall标签
#配置禁止mycat用户进行删除操作
-->
<firewall>
<whitehost>
<host host="192.168.140.128" user="mycat"/>
</whitehost>
<blacklist check="true">
<property name="deleteAllow">false</property>
</blacklist>
</firewall>
| 配置项 | 缺省值 | 描述 |
|---|---|---|
| selelctAllow | true | 是否允许执行 SELECT 语句 |
| deleteAllow | true | 是否允许执行 DELETE 语句 |
| updateAllow | true | 是否允许执行 UPDATE 语句 |
| insertAllow | true | 是否允许执行 INSERT 语句 |
| createTableAllow | true | 是否允许创建表 |
| setAllow | true | 是否允许使用 SET 语法 |
| alterTableAllow | true | 是否允许执行 Alter Table 语句 |
| dropTableAllow | true | 是否允许修改表 |
| commitAllow | true | 是否允许执行 commit 操作 |
| rollbackAllow | true | 是否允许执行 roll back 操作 |
6.Mycat 监控工具
Mycat-web 是 Mycat 可视化运维的管理和监控平台。 Mycat-web 引入了 ZooKeeper 作为配置中心,可以管理多个节点。Mycat-web 主要管理和监控 Mycat 的流量、连接、活动线程和内存等,具备 IP 白名单、邮件告警等模块,还可以统计 SQL 并分析慢 SQL 和高频 SQL 等。为优化 SQL 提供依据。

ZooKeeper 安装
mkdir conf &mkdir data
cd conf
#添加zoo.cfg如下内容
vi zoo.cfg
# 心跳检查的时间 2秒 每个tickTime时间会发送一个心跳,时间单位为毫秒。
tickTime=2000
# 初始化时连接到服务器端的间隔次数,总时间5*2=10秒
initLimit=5
# ZK Leader 和follower 之间通讯的次数,总时间2*2=4秒
syncLimit=2
autopurge.snapRetainCount=3
autopurge.purgeInterval=0
maxClientCnxns=60
standaloneEnabled=true
admin.enableServer=true
# 数据目录
dataDir=/data
# 日志目录
dataLogDir=/datalog
# 客户端端口
clientPort=2181
# 对应`myid`文件数据1 2888:集群内机器通讯使用(Leader监听此端口) 3888:选举leader使用
server.1=www.zhengqingya.com:2888:3888
cd data
#添加myid如下内容
vi myid
1
#启动zookeeper
docker run -d -p 2181:2181 --name zookeeper --restart always \
-v /home/docker/zookeeper/data/myid:/data/myid \
-v /home/docker/zookeeper/conf/zoo.cfg:/conf/zoo.cfg zookeeper
客户端工具:
下载地址:[https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip](https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip)
解压,进入build目录,运行java -jar zookeeper-dev-ZooInspector.jar

报错:EndOfStreamException: Unable to read additional data from client, it probably closed the socket: address = /192.168.99.1:59530, session = 0x0

Mycat-web 安装
docker run --name mycat-web -d -p 8082:8082 --restart=always coolflame/mycat-web
配置mycat、mysql;开启server.xml文件 ,修改参数1 ;重启mycat、mycat-web




