• 《Mycat分布式数据库架构》之数据切分实战


    前文回顾:
    《Mycat分布式数据库架构》之原理及架构
    《Mycat分布式数据库架构》之搭建详解
    《Mycat分布式数据库架构》之配置详解
    《Mycat分布式数据库架构》之数据切分和读写分离
    《Mycat分布式数据库架构》之故障切换



    1、引言

    本文将建立一个人事管理的数据库,使用水平拆分来做实际演示,并且后期解决分片后的一些问题,比如分片导致的跨库join等问题,以达到一个进阶实战的效果 。


    2、前期准备

    2.1 系统环境

    我的虚拟机配置如下。
    系统:CentOS 7 64位
    内存:3GB
    硬盘:80GB


    2.2 数据库集群

    数据库我部署了两个数据节点,环境说明如下所示:

    ip角色数据库
    192.168.157.129Mycat
    192.168.157.129Oraclehuman
    192.168.157.130Oraclehuman

    这里我在两台服务器上安装部署了oracle数据库,并在其中一台服务器部署了mycat。

    为了方便后期切分示例演示,需要先了解数据库表结构,首先在linux中先为数据库创建用户,sqlplus命令如下。

    CONNECT sys/pwd @human_res AS SYSDBA;
    
    • 1
    CREATE USER human IDENTIFIED BY human DEFAULT TABLESPACE USERS;
    GRANT CONNECT,RESOURCE,CREATE VIEW TO human;
    
    • 1
    • 2

    创建一个人事管理系统数据库来进行实战演示,创建表语句如下
    REGIONS表:

    CREATE TABLE REGIONS(
    REGION_ID NUMBER PRIMARY KEY,
    REGION_NAME VARCHAR2(25)
    )
    TABLESPACE USERS;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    COUNTRIES表:

    CREATE TABLE COUNTRIES(
    COUNTRY_ID CHAR(2) PRIMARY KEY,
    COUNTRY_NAME VARCHAR2(40),
    REGION_ID NUMBER REFERENCES REGIONS(REGION_ID)
    )
    TABLESPACE USERS;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    LOCATIONS表:

    CREATE TABLE LOCATIONS(
    LOCATION_ID NUMBER PRIMARY KEY,
    STREET_ADDRESS VARCHAR2(40),
    POSTTAL_CODE VARCHAR2(12),
    CITY VARCHAR2(30) NOT NULL,
    STATE_PROVINCE VARCHAR2(25),
    COUNTY_ID CHAR(2) REFERENCES COUNTRIES(COUNTRY_ID)
    )
    TABLESPACE USERS;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    DEPARTMENTS表:

    CREATE TABLE DEPARTMENTS(
    DEPARTMENT_ID NUMBER(4) PRIMARY KEY,
    DEPARTMENT_NAME VARCHAR2(30) NOT NULL,
    MANAGER_ID NUMBER(6),
    LOCATION_ID NUMBER(4) REFERENCES LOCATIONS(LOCATION_ID)
    )
    TABLESPACE USERS;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    JOBS表:

    CREATE TABLE JOBS(
    JOB_ID VARCHAR2(10) PRIMARY KEY,
    JOB_TITLE VARCHAR2(35) NOT NULL,
    MIN_SALARY NUMBER(6),
    MAX_SALARY NUMBER(6)
    )
    TABLESPACE USERS;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    EMPLOYEES表:

    CREATE TABLE EMPLOYEES(
    EMPLOYEE_ID NUMBER(6) PRIMARY KEY,
    FIRST_NAME VARCHAR2(20),
    LAST_NAME VARCHAR2(25) NOT NULL,
    EMAIL VARCHAR2(25) NOT NULL UNIQUE,
    PHONE_NUMBER VARCHAR2(20),
    HIRE_DATE DATE NOT NULL,
    JOB_ID VARCHAR2(10) NOT NULL REFERENCES JOBS(JOB_ID),
    SALARY NUMBER(8,2) CHECK (SALARY>0),
    COMMISSION_PCT NUMBER(2,2),
    MANAGER_ID NUMBER(6,0),
    DEPARTMENT_ID NUMBER(4,0) REFERENCES DEPARTMENTS(DEPARTMENT_ID)
    )
    TABLESPACE USERS;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    JOB_HISTORY表:

    CREATE TABLE JOB_HISTORY(
    EMPLOYEE_ID NUMBER(6) NOT NULL REFERENCES EMPLOYEES(EMPLOYEE_ID),
    START_DATE DATE NOT NULL,
    END_DATE DATE NOT NULL,
    JOB_ID VARCHAR2(10) NOT NULL REFERENCES JOBS(JOB_ID),
    DEPARTMENT_ID NUMBER(4) REFERENCES DEPARTMENTS(DEPARTMENT_ID),
    CONSTRAINT JHIST_DATE_INTERVAL CHECK (END_DATE>START_DATE),
    CONSTRAINT JHIST_EMP_ID_ST_DATE_PK PRIMARY KEY (EMPLOYEE_ID,START_DATE)
    )
    TABLESPACE USERS;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    SAL_GRADES表:

    CREATE TABLE SAL_GRADES(
    GRADE NUMBER PRIMARY KEY,
    MIN_SALARY NUMBER(8,2),
    MAX_SALARY NUMBER(8,2)
    )
    TABLESPACE USERS;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    USERS表:

    CREATE TABLE USERS(
    USER_ID NUMBER(2) PRIMARY KEY,
    USER_NAME CHAR(20),
    PASSWORD VARCHAR2(20) NOT NULL
    )
    TABLESPACE USERS;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    为了方便后面演示全局自增ID,这里将EMPLOYEES表的EMPLOYEE_ID设置为自增ID,如下,创建一个名为“EMPLOYEES_SEQ”的序列,起始值为100,步长为1,不缓存,不循环。

    CREATE SEQUENCE EMPLOYEES_SEQ
    START WITH 100
    INCREMENT BY 1
    NOCACHE
    NOCYCLE;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    接着创建触发器,如下:

    CREATE OR REPLACE TRIGGER EMPLOYEES_SEQ_TRG
    BEFORE INSERT ON EMPLOYEES
    FOR EACH ROW
    WHEN (NEW.EMPLOYEE_ID IS NULL)
    BEGIN
      SELECT EMPLOYEES_SEQ.NEXTVAL
      INTO :NEW.EMPLOYEE_ID
      FROM DUAL;
    END;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    我们可以使用navicat来连接mycat,这样就可以方便对数据进行操作,连接mycat的方式和连接mysql方式相同,点击连接,选择mysql,然后填写如下信息。

    在这里插入图片描述

    其中端口是在mycat的server.xml文件中配置的端口,默认8066;用户名密码也是在server.xml文件中配置的访问用户和密码,连接成功后即可看到配置的逻辑库和逻辑表,如下。

    在这里插入图片描述

    mycats 就是逻辑库,employees便是配置的逻辑表,目前对于应用程序来说和普通数据库没什么区别,无需关系底层物理数据库,只是当前逻辑数据库中只有一个表,其余表还需全部进行配置。


    3 注意事项

    3.1 分片原则
    • 能不切分尽量不要切分。
    • 选择合适的切分规则和分片键。
    • 尽量避免跨分片JOIN操作。

    3.2 如何选择分片键
    • 尽可能的比较均匀的分配到各个数据节点中。
    • 该业务字段是最频繁的或最重要的查询条件。

    4 数据切分实战

    4.1 配置访问用户及权限

    使用server.xml配置访问用户及权限,如下

    
    DOCTYPE mycat:server SYSTEM "server.dtd">
    <mycat:server xmlns:mycat="http://io.mycat/">
        <system>
        <property name="nonePasswordLogin">0property> 
        <property name="useHandshakeV10">1property>
        <property name="useSqlStat">0property>  
        <property name="useGlobleTableCheck">0property>  
            <property name="sqlExecuteTimeout">300property>  
            <property name="sequnceHandlerType">2property>
            <property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+property>
            <property name="processorBufferPoolType">0property>
            
            <property name="handleDistributedTransactions">0property>
            <property name="useOffHeapForMerge">0property>
            <property name="memoryPageSize">64kproperty>
            <property name="spillsFileBufferSize">1kproperty>
            <property name="useStreamOutput">0property>
            <property name="systemReserveMemorySize">384mproperty>
            
            <property name="useZKSwitch">falseproperty>
            
            
            
            
            
            <property name="strictTxIsolation">falseproperty>
            <property name="useZKSwitch">trueproperty>
        system>
    
        <user name="yxc" defaultAccount="true">
            <property name="password">yxc123456property>
            <property name="schemas">mycatsproperty>
        user>
    
        <user name="user">
            <property name="password">userproperty>
            <property name="schemas">mycatsproperty>
            <property name="readOnly">trueproperty>
        user>
    
    mycat:server>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    4.2 配置逻辑库及逻辑表

    使用schema.xml配置逻辑库及逻辑表

    
    DOCTYPE mycat:schema SYSTEM "schema.dtd">
    <mycat:schema xmlns:mycat="http://io.mycat/">
            <schema name="mycats" checkSQLschema="false" sqlMaxLimit="100">
               <table name="EMPLOYEES" primaryKey="EMPLOYEE_ID" dataNode="datanode01,datanode02" rule="EMPLOYEES"/>
            schema>
            <dataNode name="datanode01" dataHost="host01" database="human_res" />  
            <dataNode name="datanode02" dataHost="host02" database="hr" />  
            <dataHost name="host01" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="oracle" dbDriver="jdbc" switchType="1"  slaveThreshold="100">  
                    
                    <heartbeat>select 1 from dualheartbeat>  
                    <writeHost host="192.168.157.130" url="jdbc:oracle:thin:@192.168.157.130:1521:humanres" user="human" password="human">   
                    writeHost>
            dataHost> 
            <dataHost name="host02" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="oracle" dbDriver="jdbc" switchType="1"  slaveThreshold="100">  
                    
                    <heartbeat>select 1 from dualheartbeat>  
                    <writeHost host="192.168.157.129" url="jdbc:oracle:thin:@192.168.157.129:1521:hr" user="human" password="human">   
                    writeHost>
            dataHost> 
    mycat:schema>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    4.3 配置分片规则

    使用rule.xml 配置分片表的分片规则,根据业务可以灵活的对表使用不同的分片算法(目前已实现十余种不同的分片规则,对应所在源码包为:io.mycat.route.function),或者对表使用相同的算法但具体的参数不同。下面对常用算法分别进行演示。

    4.3.1 简单取模分片

    在这里插入图片描述

    简单取模分片-PartitionByMod算法具有以下特性:

    • 只能用于分片列为整数类型的表
    • 分片列 mod 分片基数
    • 类全名:io.mycat.route.function.PartitionByMod

    这里我使用EMPLOYEES表来进行分片演示,简单取模分片只能用于整数列,EMPLOYEE_ID已经为自增ID,所以我选择它的外键DEPARTMENT_ID作为分片列,如下,分片规则使用简单取模分片,片数为2。

    
    DOCTYPE mycat:rule SYSTEM "rule.dtd">
    <mycat:rule xmlns:mycat="http://io.mycat/">
        <tableRule name="EMPLOYEES">
            <rule>
                <columns>DEPARTMENT_IDcolumns>
                <algorithm>mod-longalgorithm>
            rule>
        tableRule>
    
        <function name="mod-long" class="io.mycat.route.function.PartitionByMod">
            
            <property name="count">2property>
        function>
    mycat:rule>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    接着向mycat逻辑库中添加数据,如下

    在这里插入图片描述

    分别查看两个数据节点中的数据,根据取模算法,刚才插入的数据中DEPARTMENT_ID值为10、12、14的取模结果为0,都将插入第一个节点。第一个节点192.168.157.130中数据如下所示

    在这里插入图片描述

    同理,11、13取模结果为1,都将插入第二个节点192.168.157.129中,如下所示。

    在这里插入图片描述

    对于EMPLOYEES这张表来说,它的主键EMPLOYEE_ID为自增ID,那这里我向mycat插入数据时,底层各节点物理数据库会各自对ID进行自增,所以会出现重复的情况,解决这个问题需要使用全局自增ID,在下文第5点中会进行讲解。


    4.3.2 哈希取模分片

    哈希取模算法就是计算出分片列,然后进行取模计算,从而进行分片存储,例如

    Hash('chao dev')%count
    
    • 1

    哈希取模-PartitionByHashMod算法具有以下特性:
    • 可以用于多种数据类型,如字符串,日期等
    • hash(分片列) mod分片基数
    • 类全名:io.mycat.route.function.PartitionByHashMod

    这里使用EMPLOYEES表来进行分片演示,选择LAST_NAME列来作为分片列,节点数同样为2。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mycat:rule SYSTEM "rule.dtd">
    <mycat:rule xmlns:mycat="http://io.mycat/">
        <tableRule name="EMPLOYEES">
            <rule>
                <columns>LAST_NAME</columns>
                <algorithm>mod-long</algorithm>
            </rule>
        </tableRule>
    
        <function name="mod-long" class="io.mycat.route.function.PartitionByHashMod">
            <!-- how many data nodes -->
            <property name="count">2</property>
        </function>
    </mycat:rule>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4.3.3 枚举分片

    枚举分片算法比较简单,就是指定哪些值存储什么数据节点,除此之外还有一个默认节点,没指定的值都将存储进入默认节点中,如下图所示。
    在这里插入图片描述

    枚举分片-PartitionByFileMap算法具有以下特性:
    • 可以根据可能的枚举值指定数据存储的位置
    • mycat/conf目录下增加MapFile配置枚举值同对应节点的对应关系
    • 类全名:io.mycat.route.function.PartitionByFileMap

    这里使用EMPLOYEES表来进行分片演示,DEPARTMENT_ID列来作为分片列。

    
    DOCTYPE mycat:rule SYSTEM "rule.dtd">
    <mycat:rule xmlns:mycat="http://io.mycat/">
        <tableRule name="EMPLOYEES">
            <rule>
                <columns>DEPARTMENT_IDcolumns>
                <algorithm>filemap-empalgorithm>
            rule>
        tableRule>
    
        <function name="filemap-emp" class="io.mycat.route.function.PartitionByFileMap">
            <property name="mapFile">partition-filemap-emp-init.txtproperty> 
            <property name="type">0property>
            
            <property name="defaultNode">0property>
        function>
    mycat:rule>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    4.3.4 字符串范围取模分片

    字符串范围取模分片是利用选取的字符串前N个字符来进行ASCII值来求和,然后根据求模基数计算出值,如下所示:
    在这里插入图片描述

    在这里插入图片描述
    字符串范围取模分片-PartitionByPrefixPattern算法具有以下特性:
    • 可以根据字符串的前N个字符确定数据存储的位置
    • mycat/conf目录下增加MapFile配置枚举值同对应节点的对应关系
    • 类全名:io.mycat.route.function.PartitionByPrefixPattern

    
    DOCTYPE mycat:rule SYSTEM "rule.dtd">
    <mycat:rule xmlns:mycat="http://io.mycat/">
        <tableRule name="EMPLOYEES">
            <rule>
                <columns>EMAILcolumns>
                <algorithm>sharding-by-prefix-patternalgorithm>
            rule>
        tableRule>
    
        <function name="sharding-by-prefix-pattern" class="io.mycat.route.function.PartitionByPrefixPattern">
            <property name="patternValue">128property> 
            <property name="prefixLength">0property><!-- 要取的字符串长度,2代表取前两个字符->
            
            <property name="mapFile">prefix-partition-pattern.txtproperty>
        function>
    mycat:rule>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17


    更多技术干货,请持续关注程序员大佬超。
    原创不易,转载请注明出处。

  • 相关阅读:
    自由职业者是一个趋势吗?
    黑马瑞吉外卖项目开发笔记
    蔚来杯“2022牛客暑期多校训练营10
    Java中的匿名内部类(看这篇就够了)
    代码随想录算法训练营第52天 | 300.最长递增子序列 674. 最长连续递增序列 718. 最长重复子数组
    3分钟登记流程3秒搞定,合合信息与腾讯云联合推出合规降本新方案
    条码二维码读取设备在医疗设备自助服务的重要性
    WEB3-众筹合约
    【Apollo学习笔记】——规划模块TASK之PIECEWISE_JERK_NONLINEAR_SPEED_OPTIMIZER(二)
    JAVA语法技术课第1课day01_Java基础语法&HelloWorld
  • 原文地址:https://blog.csdn.net/xch_yang/article/details/126556900