1.单机配置

mysql主从搭建

  1. mkdir conf &
  2. mkdir data &
  3. mkdir log &
  4. cd conf
  5. #添加如下内容
  6. vi mymaster.cnf
  7. [mysqld]
  8. # [必须]服务器唯一ID,默认是1
  9. server-id=1
  10. #数据库默认字符集,主流字符集支持一些特殊表情符号(特殊表情符占用4个字节)
  11. character-set-server = utf8mb4
  12. #数据库字符集对应一些排序等规则,注意要和character-set-server对应
  13. collation-server = utf8mb4_general_ci
  14. #设置client连接mysql时的字符集,防止乱码
  15. init_connect='SET NAMES utf8mb4'
  16. #是否对sql语句大小写敏感,1表示不敏感
  17. lower_case_table_names = 1
  18. # [必须]启用二进制日志
  19. log-bin=mysql-bin
  20. #主从复制的格式(mixed,statement,row,默认格式是statement)
  21. binlog_format=mixed
  22. # 复制过滤:也就是指定哪个数据库不用同步(mysql库一般不同步)
  23. binlog-ignore-db=mysql
  24. # 设置需要同步的数据库 binlog_do_db = 数据库名;
  25. # 如果是多个同步库,就以此格式另写几行即可。
  26. # 如果不指明对某个具体库同步,表示同步所有库。除了binlog-ignore-db设置的忽略的库
  27. binlog_do_db = testdb #需要同步testdb数据库。
  28. # 确保binlog日志写入后与硬盘同步
  29. sync_binlog = 1
  30. #添加如下内容
  31. vi myslave.cnf
  32. [mysqld]
  33. # [必须]服务器唯一ID,默认是1,一般取IP最后一段
  34. server-id=2
  35. # log_slave_updates表示slave将复制事件写进自己的二进制日志
  36. log-slave-updates=1
  37. #开启二进制日志功能,以备Slave作为其它Slave的Master时使用
  38. log-bin=slave-bin
  39. log-bin-index=slave-bin.index
  40. #主从复制的格式(mixed,statement,row,默认格式是statement)
  41. binlog_format=mixed
  42. #relay_log配置中继日志
  43. relay_log=relay-bin
  44. #进制日志自动删除/过期的天数。默认值为0,表示不自动删除。
  45. expire_logs_days=14
  46. # 跳过所有的错误,继续执行复制操作
  47. #slave-skip-errors = all
  48. #防止改变数据
  49. 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实践 - 图1

mycat实践 - 图2

mycat

mycat实践 - 图3

mycat实践 - 图4

mycat实践 - 图5

手动修改从库数据,从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

主从同步失效,目前数据是写入主库,读取数据是从库读。

mycat实践 - 图6

mycat实践 - 图7

2.集群配置

mycat实践 - 图8

搭建

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里面自动帮我们配置了三个节点的数据,所以自己根据分片节点重新配置。

mycat实践 - 图9

测试

mycat实践 - 图10

mycat实践 - 图11

mycat实践 - 图12

mycat实践 - 图13

mycat实践 - 图14

高可用

服务名 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

mycat实践 - 图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实践 - 图16

mycat实践 - 图17

停掉mycat-01

mycat实践 - 图18

mycat实践 - 图19

在mysql服务器进行测试连接

mycat实践 - 图20

mycat实践 - 图21

再停掉mycat-02

mycat实践 - 图22

只停掉haproxy01

mycat实践 - 图23

mycat实践 - 图24

mycat实践 - 图25

mycat实践 - 图26

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);

mycat实践 - 图27

分片枚举

通过在配置文件中配置可能的枚举 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');

mycat实践 - 图28

mycat实践 - 图29

如果去掉0

mycat实践 - 图30

范围约定

此分片适用于提前规划好分片字段某个范围属于哪个分片。

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);

mycat实践 - 图31

按日期(天)分片

此规则为按天分片。 设定时间格式、范围

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');

mycat实践 - 图32

4.分片join与全局序列

分片join

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

mycat实践 - 图33

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实践 - 图34

全局表

当业务表因为规模而进行分片以后,业务表与这些附属的字典表之间的关联,就成了比较棘手的问题。考虑到字典表变动不频繁、数据量总体变化不大,鉴于此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实践 - 图35

全局序列

在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一。为此, Mycat 提供了全局 sequence,并且提供了包含本地配置和数据库配置等多种实现方式。

本地文件
Mycat 将 sequence 配置到文件中,当使用到 sequence 中的配置后, Mycat 会更下classpath 中的 sequence_conf.properties 文件中 sequence 当前的值。
优点: 本地加载,读取速度较快
缺点: 抗风险能力差, Mycat 所在主机宕机后,无法读取本地文件。

server.xml

<property name="sequnceHandlerType">0</property>

mycat实践 - 图36

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实践 - 图37

mycat实践 - 图38

mycat实践 - 图39

数据库方式

利用数据库一个表 来进行计数累加。并不是每次生成序列都读写数据库,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);

mycat实践 - 图40

mycat实践 - 图41

mycat实践 - 图42

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

server.xml

<property name="sequnceHandlerType">0</property>

mycat实践 - 图43

mycat实践 - 图44

mycat实践 - 图45

报错: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>

mycat实践 - 图46

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 提供依据。

mycat实践 - 图47

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

mycat实践 - 图48

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

mycat实践 - 图49

Mycat-web 安装

docker run --name mycat-web -d -p 8082:8082 --restart=always coolflame/mycat-web

配置mycat、mysql;开启server.xml文件 ,修改参数1 ;重启mycat、mycat-web

mycat实践 - 图50

mycat实践 - 图51

mycat实践 - 图52

mycat实践 - 图53