• MySQL高级-读写分离-分库分表


    主从复制

    MySQL主从复制原理

    强一致性:主机在写的时候,主机从机枷锁直到主从机的数据一样,解锁
    在这里插入图片描述
    基本原理:

    slave会从master读取binlog来进行数据同步

    具体步骤:

    • step1:master将数据改变记录到二进制日志(binary log)中。
      • 二进制日志由配置文件log-bin参数指定
      • 这些记录过程叫做二进制日志事件(binary log events)
    • step2: slave将master的binary log events拷贝到它的中继日志(relay log)中。
      • slave的I/O线程去请求主库 的binlog,并将得到的binlog日志写到relay log(中继日志) 文件中
      • master会生成一个 log dump 线程,用来给slave的I/O线程线程传输binlog
    • step3:slave重做中继日志中的事件,将改变反映到自己的数据中。
      • slave的SQL线程,读取relay log日志,并解析成具体操作,从而实现主从操作一致,最终数据一致。

    由此可见主从复制过程需要网络传输或大量的IO操作,这些操作会导致数据同步的延时

    复制的基本原则:

    • 每个master可以有多个salve
    • 每个slave只有一个master
    • 每个slave只能有一个唯一的服务器ID

    一主一从常见配置

    准备两台服务器 可以克隆一台

    主机: 192.168.200.129
    从机: 192.168.200.130
    修改ip 地址:vim /etc/sysconfig/network-scripts/ifcfg-ens33
    重启网络服务 :service network restart
    注意一点:两台服务器中的 server-uuId 可能一致!
    vim /var/lib/mysql/auto.cnf

    配置/etc/my.cnf

    			先配置主机:
    			# 服务器唯一id
    			server-id=1
    			# 启用二进制日志,日志名是mysql-bin
    			log-bin=mysql-bin
    			# 设置不需要复制的数据库
    			binlog-ignore-db=mysql
    			binlog-ignore-db=infomation_schema
    			# 设置需要复制的数据库
    			binlog-do-db=mytestdb
    			# 设置logbin格式
    			binlog_format=STATEMENT
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在主机上创建用户!

    			-- 创建slave用户
    			CREATE USER 'dudu_slave'@'%';
    			-- 设置密码
    			ALTER USER 'dudu_slave'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
    			-- 授权
    			GRANT REPLICATION SLAVE ON *.* TO 'dudu_slave'@'%';
    			-- 刷新权限
    			FLUSH PRIVILEGES;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    修改从机配置文件:
    			vim /etc/my.cnf
    			# 服务器唯一id
    			server-id=2
    			# 启用中继日志
    			relay-log=mysql-relay
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    下面的MASTER_LOG_FILE和MASTER_LOG_POS用到了上图标红的数据

    重启mysql-slave 服务器!登录客户端,配置 当前读取台主机
    			CHANGE MASTER TO MASTER_HOST='192.168.6.100', 
    			MASTER_USER='dudu_slave',MASTER_PASSWORD='123456', MASTER_PORT=3306,
    			MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=157; 
    
    • 1
    • 2
    • 3
    • 4
    启动主从,查看状态:
    			start slave;
    			show slave status \G;
    			
    			<!--这两个必须为yes-->
    			Slave_IO_Running: Yes
                Slave_SQL_Running: Yes
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    测试:
    			CREATE DATABASE mytestdb;
    			USE mytestdb;
    			CREATE TABLE mytbl(id INT,NAME VARCHAR(16));
    			INSERT INTO mytbl VALUES(1, 'zhang3');
    			INSERT INTO mytbl VALUES(2, @@hostname);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    注意点:
    			如果出现了错误:
    			-- 在从机上执行。功能说明:停止I/O 线程和SQL线程的操作。
    			stop slave; 
    
    			-- 在从机上执行。功能说明:用于删除SLAVE数据库的relaylog日志文件,并重新启用新的relaylog文件。
    			reset slave;
    
    			-- 在主机上执行。功能说明:删除所有的binglog日志文件,并将日志索引文件清空,重新开始所有新的日志文件。
    			-- 用于第一次进行搭建主从库时,进行主库binlog初始化工作;
    			reset master;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    MyCat简介

    可以放在主机、从机、也可以单独一个服务器存放.

    读写分离和分库分表具体的实现方式一般有两种:中间件封装程序代码封装

    MyCat就是一个数据库中间件。

    安装MyCat

    把mycat压缩包上传到/opt下

    cd /opt
    tar -zxvf Mycat-server-1.6.7.6-release-20220524173810-linux.tar.gz
    
    • 1
    • 2

    配置文件打开MyCat目录结构如下
    在这里插入图片描述

    • bin:二进制执行文件
    • conf:配置文件目录
    • lib:依赖
    • logs:日志

    配置MyCat

    核心的配置有3个conf目录

    • server.xml:定义用户以及系统相关变量,如端口等
    • schemal.xml:定义逻辑库、表、分片节点等内容
    • rule.xml:定义分片规则

    server配置server.xml中配置了MyCat作为虚拟数据库的基本信息
    在这里插入图片描述
    在这里插入图片描述

    MyCat实现读写分离

    MyCat数据分片

    分片分析

    注意:分库分表必须是干净的库和表(不能有数据)

    分片原则

    1. 能不切分尽量不要切分。数据量不是很大的库或者表,尽量不要分片。单表行数 500W ,或者数据超过2G,才考虑分库分表!
    2. 尽量按照功能模块分库,避免跨库join。

    数据准备

    #  创建数据库  从机192.168.6.100
     create database orders;
    
    #客户表  rows:20万 
    CREATE TABLE `customer`(
        id INT AUTO_INCREMENT,
        NAME VARCHAR(200),
        PRIMARY KEY(id)
    );
     
    #  创建数据库  主机192.168.6.99
     create database orders;
     
    #订单表   rows:600万
    CREATE TABLE `orders`(
        id INT AUTO_INCREMENT,
        order_type INT,
        customer_id INT,
        amount DECIMAL(10,2),
        PRIMARY KEY(id)  
    ); 
     
     
    #订单详细表     rows:600万
    CREATE TABLE `orders_detail`(
        id INT AUTO_INCREMENT,
        detail VARCHAR(2000),
        order_id INT,
        PRIMARY KEY(id)
    );
     
     
    #订单状态字典表   rows:20
    CREATE TABLE `dict_order_type`(
        id INT AUTO_INCREMENT,
        order_type VARCHAR(200),
        PRIMARY KEY(id)
    );
    
    • 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

    问题:以上四个表如何分库?

    答案:客户表分在一个数据库,另外三张都需要关联查询,分在另外一个数据库。

    配置schema.xml实现MyCat分库:

    <?xml version="1.0"?>
    <!DOCTYPE mycat:schema SYSTEM "schema.dtd"><mycat:schema xmlns:mycat="http://io.mycat/">
    
    	<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1" dataNode="dn1">
    
    			<table name="customer" dataNode="dn2"/>		
    	</schema>
    	
    	<dataNode name="dn1" dataHost="host1" database="orders" />
    	<dataNode name="dn2" dataHost="host2" database="orders" />
    	
    	
    	
    	<dataHost name="host2" maxCon="1000" minCon="10" balance="0"
    			  writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
    		<heartbeat>select user()</heartbeat>
    		
    		<writeHost host="hostM2" url="jdbc:mysql://192.168.6.100:3306" user="root"
    				   password="123456">
    				   
    				 
    		</writeHost>
    		
    	</dataHost>
    	
    	
    	<dataHost name="host1" maxCon="1000" minCon="10" balance="0"
    			  writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
    		<heartbeat>select user()</heartbeat>
    		
    		<writeHost host="hostM1" url="jdbc:mysql://192.168.6.99:3306" user="root"
    				   password="123456">
    				   
    				 
    		</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
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    创建表

    主节点 :dn1 192.168.99 主机创建 orders 数据库 与 三张表

    从节点 :dn2 192.168.6.100 从机创建 orders 数据库 与 一张客户表

    查看分库实现

    在这里插入图片描述

    分表

    先停止mycat服务:

    ./mycat stop
    
    • 1

    首先在rule.xml中配置分表规则

    <tableRule name="order_rule">
        <rule>
            <columns>customer_id</columns>
            <algorithm>mod-long</algorithm>
        </rule>
    </tableRule>
    -- 修改原有配置文件的数据  3---2
    <function name="mod-long" class="io.mycat.route.function.PartitionByMod">
        <!-- how many data nodes -->
        <property name="count">2</property>
    </function>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    完整的代码:

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- - - Licensed under the Apache License, Version 2.0 (the "License"); 
    	- you may not use this file except in compliance with the License. - You 
    	may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 
    	- - Unless required by applicable law or agreed to in writing, software - 
    	distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT 
    	WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the 
    	License for the specific language governing permissions and - limitations 
    	under the License. -->
    <!DOCTYPE mycat:rule SYSTEM "rule.dtd">
    <mycat:rule xmlns:mycat="http://io.mycat/">
    
    	<tableRule name="order_rule">
    		<rule>
    			<!-- 新建的分表规则 -->
    			<columns>customer_id</columns>
    			<algorithm>mod-long</algorithm>
    		</rule>
    	</tableRule>
    	
    	
    	
    	
    	<tableRule name="rule1">
    		<rule>
    			<columns>id</columns>
    			<algorithm>func1</algorithm>
    		</rule>
    	</tableRule>
    
    	<tableRule name="sharding-by-date">
    		<rule>
    			<columns>createTime</columns>
    			<algorithm>partbyday</algorithm>
    		</rule>
    	</tableRule>
    
    	<tableRule name="rule2">
    		<rule>
    			<columns>user_id</columns>
    			<algorithm>func1</algorithm>
    		</rule>
    	</tableRule>
    
    	<tableRule name="sharding-by-intfile">
    		<rule>
    			<columns>sharding_id</columns>
    			<algorithm>hash-int</algorithm>
    		</rule>
    	</tableRule>
    	<tableRule name="auto-sharding-long">
    		<rule>
    			<columns>id</columns>
    			<algorithm>rang-long</algorithm>
    		</rule>
    	</tableRule>
    	<tableRule name="mod-long">
    		<rule>
    			<columns>id</columns>
    			<algorithm>mod-long</algorithm>
    		</rule>
    	</tableRule>
    	<tableRule name="sharding-by-murmur">
    		<rule>
    			<columns>id</columns>
    			<algorithm>murmur</algorithm>
    		</rule>
    	</tableRule>
    	<tableRule name="crc32slot">
    		<rule>
    			<columns>id</columns>
    			<algorithm>crc32slot</algorithm>
    		</rule>
    	</tableRule>
    	<tableRule name="sharding-by-month">
    		<rule>
    			<columns>create_time</columns>
    			<algorithm>partbymonth</algorithm>
    		</rule>
    	</tableRule>
    	<tableRule name="latest-month-calldate">
    		<rule>
    			<columns>calldate</columns>
    			<algorithm>latestMonth</algorithm>
    		</rule>
    	</tableRule>
    
    	<tableRule name="auto-sharding-rang-mod">
    		<rule>
    			<columns>id</columns>
    			<algorithm>rang-mod</algorithm>
    		</rule>
    	</tableRule>
    
    	<tableRule name="jch">
    		<rule>
    			<columns>id</columns>
    			<algorithm>jump-consistent-hash</algorithm>
    		</rule>
    	</tableRule>
    
    	<function name="murmur"
    			  class="io.mycat.route.function.PartitionByMurmurHash">
    		<property name="seed">0</property><!-- 默认是0 -->
    		<property name="count">2</property><!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
    		<property name="virtualBucketTimes">160</property><!-- 一个实际的数据库节点被映射为这么多虚拟节点,默认是160倍,也就是虚拟节点数是物理节点数的160倍 -->
    		<!-- weightMapFile 节点的权重,没有指定权重的节点默认是1。以properties文件的格式填写,以从0开始到count-1的整数值也就是节点索引为key,以节点权重值为值。所有权重值必须是正整数,否则以1代替 -->
    		<!-- /etc/mycat/bucketMapPath
    			用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的murmur hash值与物理节点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西 -->
    	</function>
    
    	<function name="crc32slot"
    			  class="io.mycat.route.function.PartitionByCRC32PreSlot">
    		<property name="count">2</property><!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
    	</function>
    	<function name="hash-int"
    			  class="io.mycat.route.function.PartitionByFileMap">
    		<property name="mapFile">partition-hash-int.txt</property>
    	</function>
    	<function name="rang-long"
    			  class="io.mycat.route.function.AutoPartitionByLong">
    		<property name="mapFile">autopartition-long.txt</property>
    	</function>
    	<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
    		<!-- how many data nodes 0 ,1 -->
    		<property name="count">2</property>
    	</function>
    
    	<function name="func1" class="io.mycat.route.function.PartitionByLong">
    		<property name="partitionCount">8</property>
    		<property name="partitionLength">128</property>
    	</function>
    	<function name="latestMonth"
    			  class="io.mycat.route.function.LatestMonthPartion">
    		<property name="splitOneDay">24</property>
    	</function>
    	<function name="partbymonth"
    			  class="io.mycat.route.function.PartitionByMonth">
    		<property name="dateFormat">yyyy-MM-dd</property>
    		<property name="sBeginDate">2015-01-01</property>
    	</function>
    
    
    	<function name="partbyday"
    			  class="io.mycat.route.function.PartitionByDate">
    		<property name="dateFormat">yyyy-MM-dd</property>
    		<property name="sNaturalDay">0</property>
    		<property name="sBeginDate">2014-01-01</property>
    		<property name="sEndDate">2014-01-31</property>
    		<property name="sPartionDay">10</property>
    	</function>
    
    	<function name="rang-mod" class="io.mycat.route.function.PartitionByRangeMod">
    		<property name="mapFile">partition-range-mod.txt</property>
    	</function>
    
    	<function name="jump-consistent-hash" class="io.mycat.route.function.PartitionByJumpConsistentHash">
    		<property name="totalBuckets">3</property>
    	</function>
    </mycat:rule>
    
    • 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
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160

    在schema.xml中配置使用分表规则

    <schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1" dataNode="dn1">
    		<table name="customer" dataNode="dn2"/>
    		<table name="orders" dataNode="dn1,dn2"  rule="order_rule" ></table>
    	</schema>
    
    • 1
    • 2
    • 3
    • 4

    完整代码:

    <?xml version="1.0"?>
    <!DOCTYPE mycat:schema SYSTEM "schema.dtd"><mycat:schema xmlns:mycat="http://io.mycat/">
    
    	<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1" dataNode="dn1">
    
    			<table name="customer" dataNode="dn2"/>
    			
    			<!-- 分表 -->
    			<table name="orders" dataNode="dn1,dn2"  rule="order_rule" ></table>
    			<!--全局表-->
    			<!-- 
    -->
    </schema> <dataNode name="dn1" dataHost="host1" database="orders" /> <dataNode name="dn2" dataHost="host2" database="orders" /> <dataHost name="host2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <writeHost host="hostM2" url="jdbc:mysql://192.168.6.100:3306" user="root" password="123456"> </writeHost> </dataHost> <dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <writeHost host="hostM1" url="jdbc:mysql://192.168.6.99:3306" user="root" password="123456"> </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
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    手动在数据节点dn2-从机上建orders表

    -- 执行orders的建表语句
    #订单表   rows:600万
    CREATE TABLE `orders`(
        id INT AUTO_INCREMENT,
        order_type INT,
        customer_id INT,
        amount DECIMAL(10,2),
        PRIMARY KEY(id)  
    ); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    最后启动mycat

    在mycat的安装目录

    ./mycat console
    
    • 1

    往MyCat中插入几条数据:

    这里指的是windows本地连接的Mycat

    -- 在mycat里向orders表插入数据,INSERT时字段不能省略
    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    测试分表实现

    在这里插入图片描述

    跨库JOIN

    修改schema配置文件

    <table name="orders" dataNode="dn1,dn2"  rule="order_rule" fetchStoreNodeByJdbc="true">
        <childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
    </table>
    
    • 1
    • 2
    • 3

    在数据节点dn2从机上建orders_detail表

    -- 执行orders_detail的建表语句
     
    #订单详细表     rows:600万
    CREATE TABLE `orders_detail`(
        id INT AUTO_INCREMENT,
        detail VARCHAR(2000),
        order_id INT,
        PRIMARY KEY(id)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    访问Mycat向orders_detail表插入数据

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    测试:在mycat、dn1、dn2中运行两个表join语句

    在这里插入图片描述

    全局表–使用了

    在分片的情况下,当业务表因为规模而进行分片以后,业务表与这些附属的字典表之间的关联,就成了比较棘手的问题,考虑到字典表具有以下几个特性:

    (1)变动不频繁

    (2)数据量总体变化不大

    (3)数据规模不大,很少有超过数十万条记录

    鉴于此,Mycat 定义了一种特殊的表,称之为“全局表”,全局表具有以下特性:

    (1)全局表的插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性

    (2)全局表的查询操作,只从一个节点获取

    (3)全局表可以跟任何一个表进行 JOIN 操作

    修改schema配置文件

    <table name="dict_order_type" dataNode="dn1,dn2" type="global" ></table>
    
    • 1

    在dn2从机创建dict_order_type表

    CREATE TABLE `dict_order_type`(
        id INT AUTO_INCREMENT,
        order_type VARCHAR(200),
        PRIMARY KEY(id)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5

    访问Mycat向dict_order_type表插入数据

    INSERT INTO dict_order_type(id,order_type) VALUES(101,'type1');
    INSERT INTO dict_order_type(id,order_type) VALUES(102,'type2');
    
    • 1
    • 2

    测试

    在这里插入图片描述

    全局序列

    在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一。

    为此,Mycat 提供了全局 sequence,并且提供了包含本地配置和数据库配置等多种实现方式。

    本地文件

    此方式 Mycat 将 sequence 配置到文件中,当使用到 sequence 中的配置后,Mycat 会更新classpath 中的 sequence_conf.properties 文件中 sequence 当前的值。

    优点:本地加载,读取速度较快

    缺点:抗风险能力差,Mycat所在主机宕机后,无法读取本地文件。

    时间戳方式

    全局序列ID = 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加) 换算成十进制为 18 位数的 long 类型,每毫秒可以并发 12 位二进制的累加。

    在这里插入图片描述

    第一位:固定为0 二进制里面第一个bit如果是1,表示负数,我们需要生产的数据都是正数,所以第一位要给 0

    41bit: 时间戳

    数值取值范围 2^41 -1

    10 bit:

    前5位可以为机房id, 后5位可以代表机器id。 也可以根据公司的实际情况自由定制。

    12 bit: 自增序列

    同一毫秒内,同一机器可以产生2^12-1 = 4096 个不同的id。

    优点:配置简单

    缺点:18位ID过长

    自主生成全局序列

    可在java项目里自己生成全局序列,如下:

    根据业务逻辑组合

    可以利用 redis的单线程原子性 incr来生成序列

    但,自主生成需要单独在工程中用java代码实现,引入了分布式项目的复杂性。

    数据库方式

    利用数据库的一个表来进行计数累加。

    在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;
    -- 查询当前序列
    SELECT * FROM MYCAT_SEQUENCE;
    -- 删除全局序列表
    TRUNCATE TABLE MYCAT_SEQUENCE;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    创建全局序列所需函数
    官方提供

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

    在dn1主机节点上初始化序列表记录

    INSERT INTO MYCAT_SEQUENCE(NAME,current_value,increment) VALUES ('ORDERS', 400000,100);
    
    • 1

    修改MyCat配置
    在这里插入图片描述
    验证全局序列
    登录MyCat,插入数据

    INSERT INTO orders(id,amount,customer_id,order_type) VALUES(NEXT VALUE FOR MYCATSEQ_ORDERS,1000,101,102);
    
    • 1

    查询数据

    SELECT * FROM orders;
    
    • 1

    重启Mycat后,再次插入数据,再查询(模拟Mycat备机上线)

    • 并不是每次生成序列都读写数据库,这样效率太低。
    • Mycat会预加载一部分号段到Mycat的内存中,这样大部分读写序列都是在内存中完成的。
    • 如果内存中的号段用完了 Mycat会再向数据库要一次。
    • 问:如果Mycat崩溃了 ,内存中的序列岂不是都没了?
    • 是的。如果是这样,那么Mycat启动后会向数据库申请新的号段,原有号段会弃用。
    • 也就是说如果Mycat重启,那么损失是当前的号段没用完的号码,但是不会因此出现主键重复
  • 相关阅读:
    基于 dynamic-datasource 实现 DB 多数据源及事物控制、读写分离、负载均衡解决方案
    【开发】视频监控平台EasyCVR分组批量绑定/取消通道功能的后端代码设计逻辑介绍
    P3871 [TJOI2010]中位数
    Laravel 下实现 Google 2fa 验证
    UTM 坐标系
    【NLP】使用递归神经网络对序列数据进行建模 (Pytorch)
    Python中list的操作4-4
    ViewModel
    Qt QPushButton详解
    LabVIEW使用源代码控制
  • 原文地址:https://blog.csdn.net/qq_40824748/article/details/126125899