分库分表技术MyCat

  传统数据库无法满足快速查询和插入数据的要求,数据量级呈指数增长。使用NoSQL数据库, 通过降低数据的安全性,减少对事务的支持,减少对复杂查询的支持,来获取性能上的提升;搭建MySQL数据库集群,为了提高查询性能, 将一个数据库的数据分散到不同的数据库中存储, 通过这种数据库拆分的方法来解决数据库的性能问题

1. 分库分表

  当单机存储容量遇到瓶颈,数据库连接数的处理能力达到上限。如单表数据量超过1000万或100G的时候,需要进行分库分表。

  通过某种特定的条件,将存放在同一个数据库中的数据分散存放到多个数据库(主机)上面,以达到分散单台设备负载的效果

  • 垂直分库:按照不同的业务将表进行分类,存放到不同的数据库中
  • 垂直分表:将一个表按照字段分成多张表,每个表存放部分信息
  • 水平分库:将同一个表中的数据(行)按照某种条件拆分到多个数据库中,每个数据库中的表的字段一致
  • 水平分表:按照某种规则把一张表的数据切分到多张表里,这些表在同一个数据库中。

垂直分库分表可以解决业务层面的耦合,能对不同业务的数据进行分级管理、维护、监控、扩展等,在高并发条件下,可以提高访问性能,但没有解决单表的数据量过大的问题。

水平分库分表按照一定规则对表的数据进行拆分,用多个表存放单表的数据

2. MyCat中间件

  • MyCat 是目前最流行的基于 java 语言编写的数据库中间件,是一个实现了 MySQL 协议的服务器
  • 前端用户可以把它看作是一个数据库代理,用 MySQL 客户端工具和命令行访问,而其后端可以用 MySQL原生协议与多个 MySQL 服务器通信,也可以用 JDBC 协议与大多数主流数据库服务器通信。
  • 其核心功能是分库分表和读写分离,即将一个大表水平分割为 N 个小表,存储在后端 MySQL 服务器里或者其他数据库里
  • 默认的 Mycat 端口是 8066 而非 MySQL 的 3306,因此需要在连接字符串上增加端口信息
  • 我们可以像使用MySQL一样使用MyCat,Mycat 可以管理若干 MySQL 数据库,同时实现数据的存储和操作
  • mycat支持的数据库:oracle,mysql,sql server,mongoDB

2.1 分片策略

  • MyCat支持两种切分模式
    • 一种是按照不同的表(或者Schema)来切分到不同的数据库(主机)之上,这种切可以称之为数据的垂直(纵向)切分
    • 一种则是根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种切分称之为数据的水平(横向)切分
  • 逻辑库(schema):对数据进行分片处理之后,从原有的一个库,被切分为多个分片数据库,所有的分片数据库集群构成了整个完整的数据库存储。Mycat在操作时,使用逻辑库来代表这个完整的数据库集群,便于对整个集群操作
  • 逻辑表(table) :可以是数据切分后,分布在一个或多个分片库中,也可以不做数据切分,不分片,只有一个表构成
    • 分片表:是指那些原有的很大数据的表,需要切分到多个数据库的表,这样,每个分片都有一部分数据,所有分片构成了完整的数据
    • 非分片表:不需要进行数据切分的表。
  • 分片节点(dataNode):数据切分后,一个大表被分到不同的分片数据库上面,每个表分片所在的数据库就是分片节点(dataNode)
  • 节点主机(dataHost):数据切分后,每个分片节点不一定都会独占一台机器,同一机器上面可以有多个分片数据库, 这样一个或多个分片节点所在的机器就是节点主机,为了规避单节点主机并发数限制, 尽量将读写压力高的分片节点均衡的放在不同的节点主机dataHost
  • 分片规则:按照某种业务规则把数据分到 某个分片的规则就是分片规则,数据切分选择合适的分片规则非常重要,将极大的避免后续数据处理的难度

分库分表技术MyCat

 

 

 

2.2 mycat 下载安装

  • 安装环境:
    •  jdk: 要求jdk必须是1.7 及以上版本
    • MySQL: 推荐mysql5.5 版本以上
    • MyCat: Mycat的官方网站:http://www.mycat.org.cn/
  • 步骤
    • 安装三台虚拟机
    分库分表技术MyCat
    • 选择两台虚拟机安装MySQL数据库服务器,保证版本一致
    server01 192.168.52.10
    server02 192.168.52.11
    • 创建数据库:192.168.52.10 创建 lagou1 数据库;192.168.52.11 创建 lagou2 和 lagou3 数据库
    • 下载MyCat ==》上传MyCat 到 server03 服务器 ,并解压
    • 启动
    启动命令:./mycat start
    停止命令:./mycat stop
    重启命令:./mycat restart
    查看状态:./mycat status
    带控制台的启动:./mycat console

2.3 mycat 核心配置

  • schema.xml配置:Schema.xml作为MyCat中重要的配置文件之一,管理着MyCat的逻辑库、表、分片规则、DataNode以及DataSource
    • schema标签
      <!-- 逻辑库 -->
      <schema name="lagou" checkSQLschema="true" sqlMaxLimit="100" >
      </schema>
      属性名 数量限制 说明
      dataNode 任意String (0..1) 分片节点
      sqlMaxLimit Integer (1) 查询返回的记录数限制limit
      checkSQLschema Boolean (1) 执行SQL时,是否去掉表所属的库名
    • table标签:定义了 Mycat 中的逻辑表,所有需要拆分的表都需要在这个标签中定义
      <schema name="lagou" checkSQLschema="true" sqlMaxLimit="100" >
      <table name="pay_order" dataNode="dn1,dn2,dn3" rule="auto-sharding-long"
      primaryKey="id" autoIncrement="true" ></table>
      </schema>
      属性 数量限制 说明
      name String (1) 逻辑表名
      dataNode String (1..*) 分片节点
      rule String (0..1) 分片规则
      ruleRequired Boolean (0..1) 是否强制绑定分片规则
      primaryKey String (1) 主键
      type String (0..1) 逻辑表类型,全局表、普通表
      autoIncrement Boolean (0..1) 自增长主键
      subTables String (1) 分表
      needAddLimit Boolean (0..1) 是否为查询SQL自动加limit限制
    • DataNode 标签:定义了 MyCat 中的分片节点,也就是我们通常说所的数据分片
      • name: 定义节点的名字,这个名字需要是唯一的,我们需要在 table 标签上应用这个名字,来建立表与分片对应的关系。
      • dataHost : 用于定义该分片属于哪个分片主机,属性值是引用 dataHost 标签上定义的 name 属性。
      • database: 用于定义该分片节点属于哪个具体的库。
    <dataNode name="dn1" dataHost="localhost1" database="lagou1" />
    <dataNode name="dn2" dataHost="localhost2" database="lagou2" />
    <dataNode name="dn3" dataHost="localhost2" database="lagou3" />
    • datahost标签:在 Mycat 逻辑库中也是作为最底层的标签存在,直接定义了具体的数据库实例、读写分离配置和心跳语句
    <!-- 节点主机 -->
    <dataHost name="localhost1" 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.52.10:3306" user="root"
    password="QiDian@666">
    </writeHost>
    </dataHost>
    属性 数量限制 说明
    name String (1) 节点主机名
    maxCon Integer (1) 最大连接数
    minCon Integer (1) 最小连接数
    balance Integer (1) 读操作负载均衡类型
    writeType Integer (1) 写操作负载均衡类型
    dbType String (1) 数据库类型
    dbDriver String (1) 数据库驱动
    switchType String (1) 主从切换类型
    • heartbeat标签:指明用于和后端数据库进行心跳检查的语句,MySQL 可以使用 select user()、Oracle 可以 使用 select 1 from dual 等
    <heartbeat>select user()</heartbeat>
    • writehost和readhost标签
      • writeHost和readHost标签都指定后端数据库的相关配置给 mycat,用于实例化后端连接池。
      • writeHost 指定写实例 , readHost 指定读实例. 在一个 dataHost 内可以定义多个 writeHost 和readHost
      <dataHost name="localhost1" 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.52.10:3306" user="root"
      password="QiDian@666">
      </writeHost>
      </dataHost>
    属性 数量限制 说明
    host String (1) 主机名
    url String (1) 连接字符串
    password String (1) 密码
    user String (1) 用户名
    weight String (1) 权重
    usingDecrypt String (1) 是否对密码加密,默认0
  • server.xml配置:保存了所有 mycat 需要的系统配置信息
    • user标签:定义登录 mycat 的用户和权限
    <user name="root" defaultAccount="true">
    <property name="password">123456</property>
    <property name="schemas">lagou</property>
    <property name="defaultSchema">lagou</property>
    </user>
    • 连接mycat
    #启动查看mycat状态
    ./mycat start
    ./mycat status
    #连接mycat
    mysql -uroot -p123456 -h127.0.0.1 -P8066
  • rule.xml 配置:定义了分片算法和函数参数
    • tableRule标签
      • name:指定唯一的名字,用于标识不同的表规则
      • rule: 指定对物理表中的哪一列进行拆分和使用什么路由算法
      • columns:指定要拆分的列名字
      • algorithm:使用 function 标签中的 name 属性,连接表规则和具体路由算法
    <mycat:rule xmlns:mycat="http://io.mycat/">
    <tableRule name="sharding-by-intfile">
    <rule>
    <columns>sharding_id</columns>
    <algorithm>hash-int</algorithm>
    </rule>
    </tableRule>
    </mycat:rule>
    • function标签
      • name:指定算法的名字
      • class:制定路由算法具体的类名字
      • property: 为具体算法需要用到的一些属性
        <function name="hash-int" class="io.mycat.route.function.PartitionByFileMap">
      <property name="mapFile">partition-hash-int.txt</property>
      </function>
  • 常用的分配规则

    • 自动分片:根据指定的列的范围进行分片.默认从0节点开始
    <tableRule name="auto-sharding-long">
    <rule>
    <columns>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>
    </function>
    
    autopartition-long.txt文件:
    0-200000=0
    200000-400000=1
    0-200000范围分配给节点0
    200000-400000范围分配给节点1
    • 枚举分片:把数据分类存储, 这种方法适用于取值固定的场合,例如性别和省份
      <!-- 枚举分片 -->
      <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>
      • mapFile 中是自定义的分片策略文件,需要自己编写
      • partition-hash-int.txt文件内容如下
      beijing=0
      wuhan=1
      shanghai=2
    • 取模分片:根据配置中的count值进行分片,将数据分成配置的count份,然后将数据均匀的分布在各个节点上
    <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>

2.4 mycat 分库分表

  • 分配规则设置(水平分库)
  • 在rule.xml配置, 自动分片,每个datanode中保存一定数量的数据。根据id进行分片

 

<!-- schema 逻辑库 -->
<schema name="lagou" checkSQLschema="true" sqlMaxLimit="100" >
<table name="pay_order" dataNode="dn1,dn2,dn3" rule="auto-sharding-long"
primaryKey="id" autoIncrement="true" >
</table>
</schema>
================================================================================
=========
<!-- 自动分片 -->
<tableRule name="auto-sharding-long">
<rule>
<columns>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>
</function>

 

  • autopartition-long.txt
# range start-end ,data node index
# K=1000,M=10000.
0-1k=0
1k-2k=1
2k-3k=2

Datanode1:1~1000
Datanode2:1000~2000
Datanode3:2000~3000

  • 连接mycat重启并测试
停止命令:./mycat stop
重启命令:./mycat restart
连接mycat并在mycat创建表,对应的MySQL节点也会创建表
DROP TABLE IF EXISTS pay_order;
CREATE TABLE pay_order (
id BIGINT(20) PRIMARY KEY,
user_id INT(11) NOT NULL ,
product_name VARCHAR(128) ,
amount DECIMAL(12,2)
);
插入数据,观察数据被插入到哪张表中
  • 解决数据插入乱码问题

  分库分表技术MyCat

  • 全局序列号实现数据自增,包含本地配置和数据库配置等多种实现方式
    • server.xml
    <system>
    <property name="sequnceHandlerType">0</property>
    </system>
    0 表示是表示使用本地文件方式。
    1 表示的是根据数据库来生成
    2 表示时间戳的方式 ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加)
    • 修改本地文件:当使用到 sequence 中的配置后,Mycat 会更新 classpath中的 sequence_conf.properties 文件中 sequence 当前的值

    PAY_ORDER.HISIDS=
    PAY_ORDER.MINID=101
    PAY_ORDER.MAXID=10000000
    PAY_ORDER.CURID=100

    其中 HISIDS 表示使用过的历史分段(一般无特殊需要可不配置),MINID 表示最小 ID 值,MAXID 表示最大ID 值,CURID 表示当前 ID 值,实现数据插入不用加上id

2.5 mycat 读写分离

  • 主从复制:通过搭建主从架构, 将数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善。
    • 用途:实时灾备,用于故障切换;读写分离,提供查询服务;备份,避免影响业务
    • 部署必要条件:主库开启binlog日志(设置log-bin参数);主从server-id不同;从库服务器能连通主库
    • 原理:Mysql 中有一种日志叫做 bin 日志(二进制日志)。这个日志会记录下所有修改了数据库的SQL语句(insert,update,delete,create/alter/drop table, grant 等等)。主从复制的原理其实就是把主服务器上的 bin 日志复制到从服务器上执行一遍,这样从服务器上的数据就和主服务器上的数据相同了

    分库分表技术MyCat  

 

  主库db的更新事件(update、insert、delete)被写到binlog==> 主库创建一个binlog dump thread,把binlog的内容发送到从库==> 从库启动并发起连接,连接到主库==> 从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log==>从库启动之后,创建一个SQL线程,从relay log里面读取内容,执行读取到的更新事件,将更新内
容写入到slave的db

 

  • 读写分离:主库处理事务性操作,从库处理select查询。数据库复制被用来把事务性查询导致的数据变更同步到从库,同时主库也可以select查询
  • 主从复制搭建
    • master中创建数据库和表
    -- 创建数据库
    CREATE DATABASE test CHARACTER SET utf8;
    -- 创建表
    CREATE TABLE users (
    id INT(11) PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(20) DEFAULT NULL,
    age INT(11) DEFAULT NULL
    );
    -- 插入数据
    INSERT INTO users VALUES(NULL,'user1',20);
    INSERT INTO users VALUES(NULL,'user2',21);
    INSERT INTO users VALUES(NULL,'user3',22);
    • 修改主数据库的配置文件my.cnf
    vim /etc/my.cnf
    # 忽略大小写
    lower_case_table_names=1
    #log-bin=mysql-bin 表示启用binlog功能,并制定二进制日志的存储目录,
    log-bin=mysql-bin
    #server-id=1 中的1可以任定义,只要是唯一的就行。
    server-id=1
    #binlog-do-db=test 是表示只备份test 数据库。
    binlog-do-db=test
    #binlog_ignore_db=mysql 表示忽略备份mysql。
    #不加binlog-do-db和binlog_ignore_db,那就表示备份全部数据库
    binlog_ignore_db=mysql
    • 重启MySQL
    service mysqld restart
    • 在主数据库上, 创建一个允许从数据库来访问的用户账号.
    -- 创建账号
    GRANT REPLICATION SLAVE ON *.* TO 'slave'@'192.168.52.11' IDENTIFIED BY 'Qwer@1234';
    • 停止主数据库的更新操作, 并且生成主数据库的备份
    -- 执行以下命令锁定数据库以防止写入数据。
    FLUSH TABLES WITH READ LOCK;
    • 导出数据库,恢复写操作
    unlock tables;
    • 将刚才主数据库备份的test.sql导入到从数据库 :在同步的时候要先锁表,让其不要有修改!等待主从数据追平,主从同步后在打开锁!
    • 修改从数据库的 my.cnf后重启
    server-id=2
    • 从数据库设置相关信息
    STOP SLAVE;
    CHANGE MASTER TO MASTER_HOST='192.168.52.10',
    MASTER_USER='slave',
    MASTER_PASSWORD='Qwer@1234',
    MASTER_PORT=3306,
    MASTER_LOG_FILE='mysql-bin.000001',
    MASTER_LOG_POS=0,
    MASTER_CONNECT_RETRY=10;
    • 修改auto.cnf中的UUID,保证唯一
    -- 编辑auto.cnf
    vim /var/lib/mysql/auto.cnf
    -- 修改UUID的值
    server-uuid=a402ac7f-c392-11ea-ad18-000c2980a208
    -- 重启
    service mysqld restart
    • 在从服务器上,启动slave 进程
    start slave;
    -- 查看状态
    SHOW SLAVE STATUS;
    -- 命令行下查看状态 执行
    SHOW SLAVE STATUS \G;

    分库分表技术MyCat

    • 在我们的主服务器做一些更新的操作,然后在从服务器查看是否已经更新
  • 对于MySQL来说,标准的读写分离是主从模式,一个写节点Master后面跟着多个读节点,读节点的数量取决于系统的压力,通常是1-3个读节点的配置。
  • mycat:
    • 在schema.xml文件中配置Mycat读写分离。使用前需要搭建MySQL主从架构,并实现主从复制,Mycat不负责数据同步问题
      • server.xml,修改用户访问的逻辑表为test
      <user name="root" defaultAccount="true">
      <property name="password">123456</property>
      <property name="schemas">test</property>
      <property name="defaultSchema">test</property>
      </user>
      • schema
      <schema name="test" checkSQLschema="true" sqlMaxLimit="100">
      <table name="users" dataNode="dn4" ruleRequired="false" primaryKey="id"
      autoIncrement="true" >
      </table>
      </schema>
      
      ####
      逻辑库 name="test"
      逻辑表 name="users"
      读写分离 不设置分片规则 ruleRequired=false
      分片节点 dataNode="dn4"
      • datanode
      <!-- 读写分离 -->
      <dataNode name="dn4" dataHost="localhost3" database="test" />
      • datahost
      <!-- 读写分离 -->
      <dataHost name="localhost3" maxCon="1000" minCon="10" balance="1" writeType="0"
      dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
      <heartbeat>select user()</heartbeat>
      <!-- 主 -->
      <writeHost host="M1" url="192.168.52.10:3306" user="root"
      password="QiDian@666">
      <!-- 从 -->
      <readHost host="S1" url="192.168.52.11:3306" user="root"
      password="QiDian@666"
      weight="1" />
      </writeHost>
      </dataHost>
      
      ####
      balance参数:
      0 :所有读操作都发送到当前可用的writeHost
      1 :所有读操作都随机发送到readHost和stand by writeHost 备份主机
      2 :所有读操作都随机发送到writeHost和readHost
      3 :所有读操作都随机发送到writeHost对应的readHost上,但是writeHost不负担读压力
      writeType参数:
      0 :所有写操作都发送到可用的writeHost
      1 :所有写操作都随机发送到readHost
      2 :所有写操作都随机发送到writeHost,readHost
    • 重启mycat
    • 结果
      • 插入一条数据, 观察否两个表都同时新增了,如果同时新增,证明插入的是主库的表
      • 在从库插入一条数据, 然后进行查询, 查询的是从库中的数据,证明查询操作在从库进行
上一篇:MySQL读写分离


下一篇:10592字,475行!带你搞懂 MyCat 读写分离!