MyCat分片 垂直拆分 概述
一种是按照不同的表(或者Schema)来切分到不同的数据库(主机)之上,这种切分可以称之为数据的垂直(纵向)切分。
案例场景
在业务系统中,有以下表结构,但是由于用户与订单每天都会产生大量的数据,单台服务器的数据存储及处理能力是有限的,可以对数据库表进行拆分,原有的数据库表:
准备工作
准备三台数据库实例
1 2 3 192.168.192.157 192.168.192.158 192.168.192.159
在三台数据库实例中建库建表,即将准备好的三个SQL脚本, 分别导入到三台MySQL实例中 。
首先上传文件:put ‘’ 上传文件路径 ‘’ ,如下:
之后登录MySQL数据库之后, 使用source命令导入 :
schema.xml的配置 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 <?xml version="1.0"?> <!DOCTYPE mycat :schema SYSTEM "schema.dtd" > <mycat:schema xmlns:mycat ="http://io.mycat/" > <schema name ="ITCAST_DB" checkSQLschema ="false" sqlMaxLimit ="100" > <table name ="tb_areas_city" dataNode ="dn1" primaryKey ="id" /> <table name ="tb_areas_provinces" dataNode ="dn1" primaryKey ="id" /> <table name ="tb_areas_region" dataNode ="dn1" primaryKey ="id" /> <table name ="tb_user" dataNode ="dn1" primaryKey ="id" /> <table name ="tb_user_address" dataNode ="dn1" primaryKey ="id" /> <table name ="tb_goods_base" dataNode ="dn2" primaryKey ="id" /> <table name ="tb_goods_desc" dataNode ="dn2" primaryKey ="goods_id" /> <table name ="tb_goods_item_cat" dataNode ="dn2" primaryKey ="id" /> <table name ="tb_order_item" dataNode ="dn3" primaryKey ="id" /> <table name ="tb_order_master" dataNode ="dn3" primaryKey ="order_id" /> <table name ="tb_order_pay_log" dataNode ="dn3" primaryKey ="out_trade_no" /> </schema > <dataNode name ="dn1" dataHost ="host1" database ="user_db" /> <dataNode name ="dn2" dataHost ="host2" database ="goods_db" /> <dataNode name ="dn3" dataHost ="host3" database ="order_db" /> <dataHost name ="host1" 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.192.157:3306" user ="root" password ="123456" > </writeHost > </dataHost > <dataHost name ="host2" maxCon ="1000" minCon ="10" balance ="0" writeType ="0" dbType ="mysql" dbDriver ="native" switchType ="1" slaveThreshold ="100" > <heartbeat > select user()</heartbeat > <writeHost host ="hostM2" url ="192.168.192.158:3306" user ="root" password ="123456" > </writeHost > </dataHost > <dataHost name ="host3" maxCon ="1000" minCon ="10" balance ="0" writeType ="0" dbType ="mysql" dbDriver ="native" switchType ="1" slaveThreshold ="100" > <heartbeat > select user()</heartbeat > <writeHost host ="hostM3" url ="192.168.192.159:3306" user ="root" password ="123456" > </writeHost > </dataHost > </mycat:schema >
server.xml的配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <user name ="root" defaultAccount ="true" > <property name ="password" > 123456</property > <property name ="schemas" > ITCAST_DB</property > </user > <user name ="test" > <property name ="password" > 123456</property > <property name ="schemas" > ITCAST_DB</property > </user > <user name ="user" > <property name ="password" > 123456</property > <property name ="schemas" > ITCAST_DB</property > <property name ="readOnly" > true</property > </user >
测试
查询数据(重新登录MyCat)
插入数据(直接操作MyCat执行插入)
1 insert into tb_user_address(id,user_id,province_id,city_id,town_id,mobile,address,contact,is_default,notes,create_date,alias) values (null ,'java00001' ,NULL ,NULL ,NULL ,'13900112222' ,'钟楼' ,'张三' ,'0' ,NULL ,NULL ,NULL )
1 insert into tb_order_item(id,item_id,goods_id,order_id,title,price,num,total_fee,pic_path,seller_id) values (null ,19 ,149187842867954 ,3 ,'3G 6' ,'1.00' ,5 ,'5.00' ,NULL ,'qiandu' )
测试跨分片的查询
1 2 SELECT order_id , payment ,receiver, province , city , area FROM tb_order_master o , tb_areas_provinces p , tb_areas_city c , tb_areas_region rWHERE o.receiver_province = p.provinceid AND o.receiver_city = c.cityid AND o.receiver_region = r.areaid ;
当运行上述的SQL语句时,MyCat会报错,原因是因为当前SQL语句涉及到跨域的join操作,要解决跨库操作就需要将区域表定义为全局表。
全局表配置
将数据节点user_db中的关联的字典表 tb_areas_provinces,tb_areas_city , tb_areas_region中的数据备份 :
1 2 3 mysqldump - uroot - pitcast user_db tb_areas_provinces > provinces; mysqldump - uroot - pitcast user_db tb_areas_city > city; mysqldump - uroot - pitcast user_db tb_areas_region > region;
将备份的表结构及数据信息,远程同步到其他两个数据节点的数据库中:
1 2 3 4 5 6 7 8 scp city root@192.168.192.158:/root scp city root@192.168.192.159:/root scp provinces root@192.168.192.158:/root scp provinces root@192.168.192.159:/root scp region root@192.168.192.158:/root scp region root@192.168.192.159:/root
在三台节点中分别导入到对应的数据库中:
1 2 3 mysql -uroot -p goods_db < city mysql -uroot -p goods_db < provinces mysql -uroot -p goods_db < region
MyCat逻辑表中的配置:
1 2 3 <table name ="tb_areas_city" dataNode ="dn1,dn2,dn3" primaryKey ="id" type ="global" /> <table name ="tb_areas_provinces" dataNode ="dn1,dn2,dn3" primaryKey ="id" type ="global" /> <table name ="tb_areas_region" dataNode ="dn1,dn2,dn3" primaryKey ="id" type ="global" />
重启MyCat:
测试:再次执行相同的连接查询,是可以正常查询出对应的数据的 :
1 2 SELECT order_id , payment ,receiver, province , city , area FROM tb_order_master o , tb_areas_provinces p , tb_areas_city c , tb_areas_region rWHERE o.receiver_province = p.provinceid AND o.receiver_city = c.cityid AND o.receiver_region = r.areaid ;
而当我们对Mycat全局表进行增删改的操作时,其他节点主机上的后端MySQL数据库中的数据是会同步变化的。
水平拆分 概述 根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种切分称之为数据的水平(横向)切分。
案例场景
在业务系统中, 有一张表(日志表), 业务系统每天都会产生大量的日志数据 , 单台服务器的数据存储及处理能力是有限的, 可以对数据库表进行拆分, 原有的数据库表拆分成以下表 :
准备工作
准备三台数据库实例
1 2 3 192.168.192.157 192.168.192.158 192.168.192.159
在三台数据库实例中创建数据
1 create database log_db DEFAULT CHARACTER SET utf8;
schema.xml的配置 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 <?xml version="1.0"?> <!DOCTYPE mycat :schema SYSTEM "schema.dtd" > <mycat:schema xmlns:mycat ="http://io.mycat/" > <schema name ="LOG_DB" checkSQLschema ="false" sqlMaxLimit ="100" > <table name ="tb_log" dataNode ="dn1,dn2,dn3" primaryKey ="id" rule ="mod-long" /> </schema > <dataNode name ="dn1" dataHost ="host1" database ="log_db" /> <dataNode name ="dn2" dataHost ="host2" database ="log_db" /> <dataNode name ="dn3" dataHost ="host3" database ="log_db" /> <dataHost name ="host1" 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.192.157:3306" user ="root" password ="123456" > </writeHost > </dataHost > <dataHost name ="host2" maxCon ="1000" minCon ="10" balance ="0" writeType ="0" dbType ="mysql" dbDriver ="native" switchType ="1" slaveThreshold ="100" > <heartbeat > select user()</heartbeat > <writeHost host ="hostM2" url ="192.168.192.158:3306" user ="root" password ="123456" > </writeHost > </dataHost > <dataHost name ="host3" maxCon ="1000" minCon ="10" balance ="0" writeType ="0" dbType ="mysql" dbDriver ="native" switchType ="1" slaveThreshold ="100" > <heartbeat > select user()</heartbeat > <writeHost host ="hostM3" url ="192.168.192.159:3306" user ="root" password ="123456" > </writeHost > </dataHost > </mycat:schema >
server.xml的配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <user name ="root" defaultAccount ="true" > <property name ="password" > 123456</property > <property name ="schemas" > LOG_DB</property > </user > <user name ="test" > <property name ="password" > 123456</property > <property name ="schemas" > LOG_DB</property > </user > <user name ="user" > <property name ="password" > 123456</property > <property name ="schemas" > LOG_DB</property > <property name ="readOnly" > true</property > </user >
测试
重启MyCat,查看逻辑表与逻辑库:
在MyCat数据库中执行建表语句,之后可以在三台数据库节点看到已经生成的tb_log表;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 CREATE TABLE `tb_log` ( `id` bigint (20 ) NOT NULL COMMENT 'ID' , `model_name` varchar (200 ) DEFAULT NULL COMMENT '模块名' , `model_value` varchar (200 ) DEFAULT NULL COMMENT '模块值' , `return_value` varchar (200 ) DEFAULT NULL COMMENT '返回值' , `return_class` varchar (200 ) DEFAULT NULL COMMENT '返回值类型' , `operate_user` varchar (20 ) DEFAULT NULL COMMENT '操作用户' , `operate_time` varchar (20 ) DEFAULT NULL COMMENT '操作时间' , `param_and_value` varchar (500 ) DEFAULT NULL COMMENT '请求参数名及参数值' , `operate_class` varchar (200 ) DEFAULT NULL COMMENT '操作类' , `operate_method` varchar (200 ) DEFAULT NULL COMMENT '操作方法' , `cost_time` bigint (20 ) DEFAULT NULL COMMENT '执行方法耗时, 单位 ms' , `source` int (1 ) DEFAULT NULL COMMENT '来源 : 1 PC , 2 Android , 3 IOS' , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8mb4;
MyCat中插入数据,并查询数据:
1 2 3 4 5 6 INSERT INTO `tb_log` (`id`, `model_name`, `model_value`, `return_value`, `return_class`, `operate_user`, `operate_time`, `param_and_value`, `operate_class`, `operate_method`, `cost_time`,`source`) VALUES ('1' ,'user' ,'insert' ,'success' ,'java.lang.String' ,'10001' ,'2020-02-26 18:12:28' ,'{\"age\":\"20\",\"name\":\"Tom\",\"gender\":\"1\"}' ,'cn.itcast.controller.UserController' ,'insert' ,'10' ,1 );INSERT INTO `tb_log` (`id`, `model_name`, `model_value`, `return_value`, `return_class`, `operate_user`, `operate_time`, `param_and_value`, `operate_class`, `operate_method`, `cost_time`,`source`) VALUES ('2' ,'user' ,'insert' ,'success' ,'java.lang.String' ,'10001' ,'2020-02-26 18:12:27' ,'{\"age\":\"20\",\"name\":\"Tom\",\"gender\":\"1\"}' ,'cn.itcast.controller.UserController' ,'insert' ,'23' ,1 );INSERT INTO `tb_log` (`id`, `model_name`, `model_value`, `return_value`, `return_class`, `operate_user`, `operate_time`, `param_and_value`, `operate_class`, `operate_method`, `cost_time`,`source`) VALUES ('3' ,'user' ,'update' ,'success' ,'java.lang.String' ,'10001' ,'2020-02-26 18:16:45' ,'{\"age\":\"20\",\"name\":\"Tom\",\"gender\":\"1\"}' ,'cn.itcast.controller.UserController' ,'update' ,'34' ,1 );INSERT INTO `tb_log` (`id`, `model_name`, `model_value`, `return_value`, `return_class`, `operate_user`, `operate_time`, `param_and_value`, `operate_class`, `operate_method`, `cost_time`,`source`) VALUES ('4' ,'user' ,'update' ,'success' ,'java.lang.String' ,'10001' ,'2020-02-26 18:16:45' ,'{\"age\":\"20\",\"name\":\"Tom\",\"gender\":\"1\"}' ,'cn.itcast.controller.UserController' ,'update' ,'13' ,2 );INSERT INTO `tb_log` (`id`, `model_name`, `model_value`, `return_value`, `return_class`, `operate_user`, `operate_time`, `param_and_value`, `operate_class`, `operate_method`, `cost_time`,`source`) VALUES ('5' ,'user' ,'insert' ,'success' ,'java.lang.String' ,'10001' ,'2020-02-26 18:30:31' ,'{\"age\":\"200\",\"name\":\"TomCat\",\"gender\":\"0\"}' ,'cn.itcast.controller.UserController' ,'insert' ,'29' ,3 );INSERT INTO `tb_log` (`id`, `model_name`, `model_value`, `return_value`, `return_class`, `operate_user`, `operate_time`, `param_and_value`, `operate_class`, `operate_method`, `cost_time`,`source`) VALUES ('6' ,'user' ,'find' ,'success' ,'java.lang.String' ,'10001' ,'2020-02-26 18:30:31' ,'{\"age\":\"200\",\"name\":\"TomCat\",\"gender\":\"0\"}' ,'cn.itcast.controller.UserController' ,'find' ,'29' ,2 );
根据分片规则rule=”mod-long”可知:
在数据库节点157上进行查询:
分片规则 MyCat的分片规则配置在conf目录下的rule.xml文件中定义 。
环境准备 :
schema.xml中的内容做好备份,并配置逻辑库:
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 <schema name ="PARTITION_DB" checkSQLschema ="false" sqlMaxLimit ="100" > <table name ="" dataNode ="dn1,dn2,dn3" rule ="" /> </schema > <dataNode name ="dn1" dataHost ="host1" database ="partition_db" /> <dataNode name ="dn2" dataHost ="host2" database ="partition_db" /> <dataNode name ="dn3" dataHost ="host3" database ="partition_db" /> <dataHost name ="host1" 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.192.157:3306" user ="root" password ="123456" > </writeHost > </dataHost > <dataHost name ="host2" maxCon ="1000" minCon ="10" balance ="0" writeType ="0" dbType ="mysql" dbDriver ="native" switchType ="1" slaveThreshold ="100" > <heartbeat > select user()</heartbeat > <writeHost host ="hostM2" url ="192.168.192.158:3306" user ="root" password ="123456" > </writeHost > </dataHost > <dataHost name ="host3" maxCon ="1000" minCon ="10" balance ="0" writeType ="0" dbType ="mysql" dbDriver ="native" switchType ="1" slaveThreshold ="100" > <heartbeat > select user()</heartbeat > <writeHost host ="hostM3" url ="192.168.192.159:3306" user ="root" password ="123456" > </writeHost > </dataHost >
在MySQL的三个节点的数据库中,创建数据库partition_db:
1 create database partition_db DEFAULT CHARACTER SET utf8mb4;
注意:下面对分片规则的测试,每次修改测试都需要重启 MyCat 服务器。
取模分片 1 2 3 4 5 6 7 8 9 10 <tableRule name ="mod-long" > <rule > <columns > id</columns > <algorithm > mod-long</algorithm > </rule > </tableRule > <function name ="mod-long" class ="io.mycat.route.function.PartitionByMod" > <property name ="count" > 3</property > </function >
配置说明 :
属性
描述
columns
标识将要分片的表字段
algorithm
指定分片函数与function的对应关系
class
指定该分片算法对应的类
count
数据节点的数量
范围分片 根据指定的字段及其配置的范围与数据节点的对应情况, 来决定该数据属于哪一个分片, 配置如下:
1 2 3 4 5 6 7 8 9 10 11 <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 > <property name ="defaultNode" > 0</property > </function >
autopartition-long.txt 配置如下:
1 2 3 4 5 6 7 0-500M =0 500M-1000M =1 1000M-1500M =2
配置说明:
属性
描述
columns
标识将要分片的表字段
algorithm
指定分片函数与function的对应关系
class
指定该分片算法对应的类
mapFile
对应的外部配置文件
type
默认值为0 ,0 表示Integer , 1 表示String
defaultNode
默认节点 默认节点的所用:枚举分片时,如果碰到不识别的枚举值, 就让它路由到默认节点 ; 如果没有默认值,碰到不识别的则报错 。
测试:
配置:
1 <table name ="tb_log" dataNode ="dn1,dn2,dn3" rule ="auto-sharding-long" />
数据:
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 1. 创建表CREATE TABLE `tb_log` ( id bigint (20 ) NOT NULL COMMENT 'ID' , operateuser varchar (200 ) DEFAULT NULL COMMENT '姓名' , operation int (2 ) DEFAULT NULL COMMENT '1: insert, 2: delete, 3: update , 4: select' , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8mb4; 2. 插入数据insert into tb_log (id,operateuser ,operation) values (1 ,'Tom' ,1 );insert into tb_log (id,operateuser ,operation) values (2 ,'Cat' ,2 );insert into tb_log (id,operateuser ,operation) values (3 ,'Rose' ,3 );insert into tb_log (id,operateuser ,operation) values (4 ,'Coco' ,2 );insert into tb_log (id,operateuser ,operation) values (5 ,'Lily' ,1 );#上面数据会被插入到157 节点上 insert into tb_log (id,operateuser ,operation) values (5000001 ,'Tom' ,1 );#这条数据被插入到158 节点上 insert into tb_log (id,operateuser ,operation) values (10000001 ,'Tom' ,1 );#这条数据被插入到159 节点上 insert into tb_log (id,operateuser ,operation) values (15000001 ,'Tom' ,1 );#这条数据被插入到157 节点上,因为在3 个节点上都没查到它的范围,因此默认插入到默认节点157 上
枚举分片 通过在配置文件中配置可能的枚举值,指定数据分布到不同数据节点上,本规则适用于按照省份或状态拆分数据等业务,配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 <tableRule name ="sharding-by-intfile" > <rule > <columns > status</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" > 0</property > <property name ="defaultNode" > 0</property > </function >
partition-hash-int.txt ,内容如下 :
配置说明:
属性
描述
columns
标识将要分片的表字段
algorithm
指定分片函数与function的对应关系
class
指定该分片算法对应的类
mapFile
对应的外部配置文件
type
默认值为0,0 表示Integer , 1 表示String
defaultNode
默认节点;小于0 标识不设置默认节点 , 大于等于0代表设置默认节点 ; 默认节点的所用:枚举分片时,如果碰到不识别的枚举值,就让它路由到默认节点;如果没有默认值,碰到不识别的则报错 。
测试:
配置:
1 <table name ="tb_user" dataNode ="dn1,dn2,dn3" rule ="sharding-by-enum-status" />
数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 1. 创建表CREATE TABLE `tb_user` ( id bigint (20 ) NOT NULL COMMENT 'ID' , username varchar (200 ) DEFAULT NULL COMMENT '姓名' , status int (2 ) DEFAULT '1' COMMENT '1: 未启用, 2: 已启用, 3: 已关闭' , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8mb4; 2. 插入数据insert into tb_user (id,username ,status) values (1 ,'Tom' ,1 );insert into tb_user (id,username ,status) values (2 ,'Cat' ,2 );insert into tb_user (id,username ,status) values (3 ,'Rose' ,3 );insert into tb_user (id,username ,status) values (4 ,'Coco' ,2 );insert into tb_user (id,username ,status) values (5 ,'Lily' ,1 );#status为1 插入到第一个节点上,status为2 插入到第二个节点上,status为3 插入到第三个节点上 #当插入status为4 时,找不到对应节点,会被插入到默认节点中
范围求模算法 该算法是先进行范围分片, 计算出分片组 , 再进行组内求模。
优点 : 综合了范围分片和求模分片的优点。 分片组内使用求模可以保证组内的数据分布比较均匀, 分片组之间采用范围分片可以兼顾范围分片的特点。
缺点 : 在数据范围是固定值(非递增值)时,存在不方便扩展的情况,例如将 dataNode Group size 从 2 扩展为 4 时,需要进行数据迁移才能完成 ; 如图所示:
配置如下:
1 2 3 4 5 6 7 8 9 10 11 <tableRule name ="auto-sharding-rang-mod" > <rule > <columns > id</columns > <algorithm > rang-mod</algorithm > </rule > </tableRule > <function name ="rang-mod" class ="io.mycat.route.function.PartitionByRangeMod" > <property name ="mapFile" > autopartition-range-mod.txt</property > <property name ="defaultNode" > 0</property > </function >
autopartition-range-mod.txt 配置格式 :
1 2 3 4 5 0-500M =1 500M1-2000M =2
配置说明:
属性
描述
columns
标识将要分片的表字段名
algorithm
指定分片函数与function的对应关系
class
指定该分片算法对应的类
mapFile
对应的外部配置文件
defaultNode
默认节点 ; 未包含以上规则的数据存储在defaultNode节点中, 节点从0开
测试:
配置:
1 <table name ="tb_stu" dataNode ="dn1,dn2,dn3" rule ="auto-sharding-rang-mod" />
数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 1. 创建表 CREATE TABLE `tb_stu` ( id bigint (20 ) NOT NULL COMMENT 'ID' , username varchar (200 ) DEFAULT NULL COMMENT '姓名' , status int (2 ) DEFAULT '1' COMMENT '1: 未启用, 2: 已启用, 3: 已关闭' , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8mb4; 2. 插入数据 insert into tb_stu (id,username ,status) values (1 ,'Tom' ,1 ); insert into tb_stu (id,username ,status) values (2 ,'Cat' ,2 ); insert into tb_stu (id,username ,status) values (3 ,'Rose' ,3 ); insert into tb_stu (id,username ,status) values (4 ,'Coco' ,2 ); insert into tb_stu (id,username ,status) values (5 ,'Lily' ,1 ); #上面执行的语句数据存储在第一个节点上(192.168 .192 .157 ) insert into tb_stu (id,username ,status) values (5000001 ,'Roce' ,1 ); insert into tb_stu (id,username ,status) values (5000002 ,'Jexi' ,2 ); insert into tb_stu (id,username ,status) values (5000003 ,'Mini' ,1 ); #上面的id为5000002 的数据由于5000002 % 2 = 0 ,则存储在第二个节点上(158 ),而id为5000001 和5000003 这两条数据(由于 5000001 \50000003 % 2 = 1 )存储在第三个节点上(159 )。
固定分片hash算法 该算法类似于十进制的求模运算,但是为二进制的操作,例如,取 id 的二进制低 10 位 与 1111111111 进行位 & 运算。
最小值:
最大值:
优点 : 这种策略比较灵活,可以均匀分配也可以非均匀分配,各节点的分配比例和容量大小由partitionCount和partitionLength两个参数决定。
缺点 :和取模分片类似。
配置如下 :
1 2 3 4 5 6 7 8 9 10 11 12 13 <tableRule name ="sharding-by-long-hash" > <rule > <columns > id</columns > <algorithm > func1</algorithm > </rule > </tableRule > <function name ="func1" class ="org.opencloudb.route.function.PartitionByLong" > <property name ="partitionCount" > 2,1</property > <property name ="partitionLength" > 256,512</property > </function > #在示例中配置的分片策略,希望将数据水平分成3份,前两份各占 25%,第三份占 50%。
配置说明:
属性
描述
columns
标识将要分片的表字段名
algorithm
指定分片函数与function的对应关系
class
指定该分片算法对应的类
partitionCount
分片个数列表
partitionLength
分片范围列表
约束 :
分片长度 : 默认最大2^10 , 为 1024 ;
count、length的数组长度必须是一致的 ;
两组数据的对应情况: (partitionCount[0]partitionLength[0])=(partitionCount[1]partitionLength[1]);
以上分为三个分区:0-255,256-511,512-1023。
测试:
配置:
1 <table name ="tb_brand" dataNode ="dn1,dn2,dn3" rule ="sharding-by-long-hash" />
数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1. 创建表 CREATE TABLE `tb_brand` ( id int (11 ) NOT NULL COMMENT 'ID' , name varchar (200 ) DEFAULT NULL COMMENT '名称' , firstChar char (1 ) COMMENT '首字母' , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8mb4; 2. 插入数据 insert into tb_brand (id,name ,firstChar) values (1 ,'七匹狼' ,'Q' ); insert into tb_brand (id,name ,firstChar) values (529 ,'八匹狼' ,'B' ); insert into tb_brand (id,name ,firstChar) values (1203 ,'九匹狼' ,'J' ); insert into tb_brand (id,name ,firstChar) values (1205 ,'十匹狼' ,'S' ); insert into tb_brand (id,name ,firstChar) values (1719 ,'六匹狼' ,'L' ); #通过上面数据的hash值的后十位与 1111111111 进行位 & 运可知,id为1 ,1203 ,1205 的数据存储在157 节点上,而id为529 ,1719 的数据 存储在159 节点上,158 节点上没有数据。
取模范围算法 该算法先进行取模,然后根据取模值所属范围进行分片。
优点 :可以自主决定取模后数据的节点分布。
缺点 :dataNode 划分节点是事先建好的,需要扩展时比较麻烦。
配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 <tableRule name ="sharding-by-pattern" > <rule > <columns > id</columns > <algorithm > sharding-by-pattern</algorithm > </rule > </tableRule > <function name ="sharding-by-pattern" class ="io.mycat.route.function.PartitionByPattern" > <property name ="mapFile" > partition-pattern.txt</property > <property name ="defaultNode" > 0</property > <property name ="patternValue" > 96</property > </function >
partition-pattern.txt 配置如下:
1 2 3 4 5 0-32 =0 33-64 =1 65-96 =2
配置说明:
属性
描述
columns
标识将要分片的表字段
algorithm
指定分片函数与function的对应关系
class
指定该分片算法对应的类
mapFile
对应的外部配置文件
defaultNode
默认节点 ; 如果id不是数字, 无法求模, 将分配在defaultNode上
patternValue
求模基数
测试:
配置:
1 <table name ="tb_mod_range" dataNode ="dn1,dn2,dn3" rule ="sharding-by-pattern" />
数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 1. 创建表 CREATE TABLE `tb_mod_range` ( id int (11 ) NOT NULL COMMENT 'ID' , name varchar (200 ) DEFAULT NULL COMMENT '名称' , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8mb4; 2. 插入数据 insert into tb_mod_range (id,name) values (1 ,'Test1' ); insert into tb_mod_range (id,name) values (2 ,'Test2' ); insert into tb_mod_range (id,name) values (3 ,'Test3' ); insert into tb_mod_range (id,name) values (4 ,'Test4' ); insert into tb_mod_range (id,name) values (5 ,'Test5' ); #上面的数据均存储在第一个节点上(157 ) insert into tb_mod_range (id,name) values (34 ,'Test34' ); insert into tb_mod_range (id,name) values (36 ,'Test36' ); #上面两条数据(34 / 36 % 96 在33 -64 之间)存储在第二个节点上(158 ) insert into tb_mod_range (id,name) values (67 ,'Test67' ); #上面两条数据(67 / 36 % 96 在65 -96 之间)存储在第三个节点上(159 )
注意 : 取模范围算法只能针对于数字类型进行取模运算;如果是字符串则无法进行取模分片。
字符串hash求模范围算法 与取模范围算法类似,该算法支持数值、符号、字母取模,首先截取长度为 prefixLength 的子串,在对子串中每一个字符的 ASCII 码求和,然后对求和值进行取模运算(sum%patternValue),就可以计算出子串的分片数。
优点 :可以自主决定取模后数据的节点分布。
缺点 :dataNode 划分节点是事先建好的,需要扩展时比较麻烦。
配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 <tableRule name ="sharding-by-prefixpattern" > <rule > <columns > username</columns > <algorithm > sharding-by-prefixpattern</algorithm > </rule > </tableRule > <function name ="sharding-by-prefixpattern" class ="io.mycat.route.function.PartitionByPrefixPattern" > <property name ="mapFile" > partition-prefixpattern.txt</property > <property name ="prefixLength" > 5</property > <property name ="patternValue" > 96</property > </function >
partition-prefixpattern.txt 配置如下:
1 2 3 4 5 6 7 8 9 0-32 =0 33-64 =1 65-96 =2
配置说明:
属性
描述
columns
标识将要分片的表字段
algorithm
指定分片函数与function的对应关系
class
指定该分片算法对应的类
mapFile
对应的外部配置文件
prefixLength
截取的位数; 将该字段获取前prefixLength位所有ASCII码的和, 进行求模sum%patternValue ,获取的值,在通配范围内的即分片数 。
patternValue
求模基数
如 :
1 2 3 4 5 6 7 8 9 10 11 12 13 字符串 : gf89f9a 截取字符串的前5位进行ASCII的累加运算 : g - 103 f - 102 8 - 56 9 - 57 f - 102 sum求和 : 103 + 102 + + 56 + 57 + 102 = 420 求模 : 420 % 96 = 36
附录 ASCII码表 :
测试:
配置:
1 <table name ="tb_u" dataNode ="dn1,dn2,dn3" rule ="sharding-by-prefixpattern" />
数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1. 创建表 CREATE TABLE `tb_u` ( username varchar (50 ) NOT NULL COMMENT '用户名' , age int (11 ) default 0 COMMENT '年龄' , PRIMARY KEY (`username`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8mb4; 2. 插入数据 insert into tb_u (username,age) values ('Test100001' ,18 ); insert into tb_u (username,age) values ('Test200001' ,20 ); insert into tb_u (username,age) values ('Test300001' ,19 ); insert into tb_u (username,age) values ('Test400001' ,25 ); insert into tb_u (username,age) values ('Test500001' ,22 ); #上面五条记录全部存储在第三个节点中(159 )。
应用指定算法 由运行阶段由应用自主决定路由到那个分片,直接根据字符子串(必须是数字)计算分片号,配置如下 :
1 2 3 4 5 6 7 8 9 10 11 12 13 <tableRule name ="sharding-by-substring" > <rule > <columns > id</columns > <algorithm > sharding-by-substring</algorithm > </rule > </tableRule > <function name ="sharding-by-substring" class ="io.mycat.route.function.PartitionDirectBySubString" > <property name ="startIndex" > 0</property > <property name ="size" > 2</property > <property name ="partitionCount" > 3</property > <property name ="defaultPartition" > 0</property > </function >
配置说明:
属性
描述
columns
标识将要分片的表字段
algorithm
指定分片函数与function的对应关系
class
指定该分片算法对应的类
startIndex
字符子串起始索引
size
字符长度
partitionCount
分区(分片)数量
defaultPartition
默认分片(在分片数量定义时, 字符标示的分片编号不在分片数量内时,使用默认分片)
示例说明 :
id=05-100000002,在此配置中代表根据id中从 startIndex=0,开始,截取siz=2位数字即05,05就是获取的分区,如果没传默认分配到defaultPartition 。
测试:
配置:
1 <table name ="tb_app" dataNode ="dn1,dn2,dn3" rule ="sharding-by-substring" />
数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1. 创建表 CREATE TABLE `tb_app` ( id varchar (10 ) NOT NULL COMMENT 'ID' , name varchar (200 ) DEFAULT NULL COMMENT '名称' , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8mb4; 2. 插入数据 insert into tb_app (id,name) values ('00-00001' ,'Testx00001' ); insert into tb_app (id,name) values ('01-00001' ,'Test100001' ); insert into tb_app (id,name) values ('01-00002' ,'Test200001' ); insert into tb_app (id,name) values ('02-00001' ,'Test300001' ); insert into tb_app (id,name) values ('02-00002' ,'TesT400001' ); #第一个数据存储在第一个节点(157 ),第二、三个数据存储在第二个节点(158 ),第四、五个数据存储在第三个节点(159 )。
字符串hash解析算法 截取字符串中的指定位置的子字符串, 进行hash算法, 算出分片 , 配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 <tableRule name ="sharding-by-stringhash" > <rule > <columns > name</columns > <algorithm > sharding-by-stringhash</algorithm > </rule > </tableRule > <function name ="sharding-by-stringhash" class ="io.mycat.route.function.PartitionByString" > <property name ="partitionLength" > 512</property > <property name ="partitionCount" > 2</property > <property name ="hashSlice" > 0:2</property > </function >
配置说明:
属性
描述
columns
标识将要分片的表字段
algorithm
指定分片函数与function的对应关系
class
指定该分片算法对应的类
partitionLength
hash求模基数 ; length*count=1024 (出于性能考虑)
partitionCount
分区数
hashSlice
hash运算位 , 根据子字符串的hash运算 ; 0 代表 str.length() , -1 代表 str.length()-1 , 大于0只代表数字自身 ; 可以理解为substring(start,end),start为0则只表示0。
测试:
配置:
1 <table name ="tb_strhash" dataNode ="dn1,dn2,dn3" rule ="sharding-by-stringhash" />
数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 1. 创建表create table tb_strhash( name varchar (20 ) primary key, content varchar (100 ) )engine= InnoDB DEFAULT CHARSET= utf8mb4; 2. 插入数据INSERT INTO tb_strhash (name,content) VALUES ('T1001' , UUID());INSERT INTO tb_strhash (name,content) VALUES ('ROSE' , UUID());INSERT INTO tb_strhash (name,content) VALUES ('JERRY' , UUID());INSERT INTO tb_strhash (name,content) VALUES ('CRISTINA' , UUID());INSERT INTO tb_strhash (name,content) VALUES ('TOMCAT' , UUID());#第三、四个数据存储在第一个节点,其他三条数据存储在第二个节点中。
原理:
一致性hash算法 一致性hash算法有效的解决了分布式数据的扩容问题 , 配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <tableRule name ="sharding-by-murmur" > <rule > <columns > id</columns > <algorithm > murmur</algorithm > </rule > </tableRule > <function name ="murmur" class ="io.mycat.route.function.PartitionByMurmurHash" > <property name ="seed" > 0</property > <property name ="count" > 3</property > <property name ="virtualBucketTimes" > 160</property > </function >
配置说明:
属性
描述
columns
标识将要分片的表字段
algorithm
指定分片函数与function的对应关系
class
指定该分片算法对应的类
seed
创建murmur_hash对象的种子,默认0
count
要分片的数据库节点数量,必须指定,否则没法分片
virtualBucketTimes
一个实际的数据库节点被映射为这么多虚拟节点,默认是160倍,也就是虚拟节点数是物理节点数的160倍;virtualBucketTimes*count就是虚拟结点数量 ;
weightMapFile
节点的权重,没有指定权重的节点默认是1。以properties文件的格式填写,以从0开始到count-1的整数值也就是节点索引为key,以节点权重值为值。所有权重值必须是正整数,否则以1代替
bucketMapPath
用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的murmur hash值与物理节点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西
测试:
配置:
1 <table name ="tb_order" dataNode ="dn1,dn2,dn3" rule ="sharding-by-murmur" />
数据:
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 1. 创建表create table tb_order( id int (11 ) primary key, money int (11 ), content varchar (200 ) )engine= InnoDB ; 2. 插入数据INSERT INTO tb_order (id,money,content) VALUES (1 , 100 , UUID());INSERT INTO tb_order (id,money,content) VALUES (212 , 100 , UUID());INSERT INTO tb_order (id,money,content) VALUES (312 , 100 , UUID());INSERT INTO tb_order (id,money,content) VALUES (412 , 100 , UUID());INSERT INTO tb_order (id,money,content) VALUES (534 , 100 , UUID());INSERT INTO tb_order (id,money,content) VALUES (621 , 100 , UUID());INSERT INTO tb_order (id,money,content) VALUES (754563 , 100 , UUID());INSERT INTO tb_order (id,money,content) VALUES (8123 , 100 , UUID());INSERT INTO tb_order (id,money,content) VALUES (91213 , 100 , UUID());INSERT INTO tb_order (id,money,content) VALUES (23232 , 100 , UUID());INSERT INTO tb_order (id,money,content) VALUES (112321 , 100 , UUID());INSERT INTO tb_order (id,money,content) VALUES (21221 , 100 , UUID());INSERT INTO tb_order (id,money,content) VALUES (112132 , 100 , UUID());INSERT INTO tb_order (id,money,content) VALUES (12132 , 100 , UUID());INSERT INTO tb_order (id,money,content) VALUES (124321 , 100 , UUID());INSERT INTO tb_order (id,money,content) VALUES (212132 , 100 , UUID());#在大数据情况下,数据会被均匀的分配到各个节点。
日期分片算法 按照日期来分片:
1 2 3 4 5 6 7 8 9 10 11 12 13 <tableRule name ="sharding-by-date" > <rule > <columns > create_time</columns > <algorithm > sharding-by-date</algorithm > </rule > </tableRule > <function name ="sharding-by-date" class ="io.mycat.route.function.PartitionByDate" > <property name ="dateFormat" > yyyy-MM-dd</property > <property name ="sBeginDate" > 2020-01-01</property > <property name ="sEndDate" > 2021-12-31</property > <property name ="sPartionDay" > 10</property > </function >
配置说明:
属性
描述
columns
标识将要分片的表字段
algorithm
指定分片函数与function的对应关系
class
指定该分片算法对应的类
dateFormat
日期格式
sBeginDate
开始日期
sEndDate
结束日期,如果配置了结束日期,则代码数据到达了这个日期的分片后,会重复从开始分片插入
sPartionDay
分区天数,默认值 10 ,从开始日期算起,每个10天一个分区
注意:配置规则的表的 dataNode 的分片,必须和分片规则数量一致,例如 2021-01-01 到 2021-12-31 ,每10天一个分片,一共需要37个分片。
单月小时算法 单月内按照小时拆分,最小粒度是小时,一天最多可以有24个分片,最小1个分片,下个月从头开始循环,每个月末需要手动清理数据。
配置如下:
1 2 3 4 5 6 7 8 9 10 <tableRule name ="sharding-by-hour" > <rule > <columns > create_time</columns > <algorithm > sharding-by-hour</algorithm > </rule > </tableRule > <function name ="sharding-by-hour" class ="io.mycat.route.function.LatestMonthPartion" > <property name ="splitOneDay" > 24</property > </function >
配置说明:
属性
描述
columns
标识将要分片的表字段 ; 字符串类型(yyyymmddHH), 需要符合JAVA标准
algorithm
指定分片函数与function的对应关系
splitOneDay
一天切分的分片数
自然月分片算法 使用场景为按照月份列分区,每个自然月为一个分片,配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 <tableRule name ="sharding-by-month" > <rule > <columns > create_time</columns > <algorithm > sharding-by-month</algorithm > </rule > </tableRule > <function name ="sharding-by-month" class ="io.mycat.route.function.PartitionByMonth" > <property name ="dateFormat" > yyyy-MM-dd</property > <property name ="sBeginDate" > 2021-01-01</property > <property name ="sEndDate" > 2021-12-31</property > </function >
配置说明:
属性
描述
columns
标识将要分片的表字段
algorithm
指定分片函数与function的对应关系
class
指定该分片算法对应的类
dateFormat
日期格式
sBeginDate
开始日期
sEndDate
结束日期,如果配置了结束日期,则代码数据到达了这个日期的分片后,会重复从开始分片插入
日期范围hash算法 其思想和范围求模分片一样,先根据日期进行范围分片求出分片组,再根据时间hash使得短期内数据分布的更均匀。
优点 : 可以避免扩容时的数据迁移,又可以一定程度上避免范围分片的热点问题。
注意 : 要求日期格式尽量精确些,不然达不到局部均匀的目的。
1 2 3 4 5 6 7 8 9 10 11 12 13 <tableRule name ="range-date-hash" > <rule > <columns > create_time</columns > <algorithm > range-date-hash</algorithm > </rule > </tableRule > <function name ="range-date-hash" class ="io.mycat.route.function.PartitionByRangeDateHash" > <property name ="dateFormat" > yyyy-MM-dd HH:mm:ss</property > <property name ="sBeginDate" > 2021-01-01 00:00:00</property > <property name ="groupPartionSize" > 6</property > <property name ="sPartionDay" > 10</property > </function >
配置说明:
属性
描述
columns
标识将要分片的表字段
algorithm
指定分片函数与function的对应关系
class
指定该分片算法对应的类
dateFormat
日期格式 , 符合Java标准
sBeginDate
开始日期 , 与 dateFormat指定的格式一致
groupPartionSize
每组的分片数量
sPartionDay
代表多少天为一组
MyCat高级 MyCat 性能监控 MyCat-web简介 Mycat-web 是 Mycat 可视化运维的管理和监控平台,弥补了 Mycat 在监控上的空白,帮 Mycat 分担统计任务和配置管理任务。Mycat-web 引入了 ZooKeeper 作为配置中心,可以管理多个节点。Mycat-web 主要管理和监控 Mycat 的流量、连接、活动线程和内存等,具备 IP 白名单、邮件告警等模块,还可以统计 SQL 并分析慢 SQL 和高频 SQL 等。为优化 SQL 提供依据。
MyCat-web下载 下载地址 : http://dl.mycat.io/
Mycat-web安装配置 安装
安装Zookeeper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 A. 上传安装包 alt + p -----> put D:\tmp\zookeeper-3.4.11.tar.gz B. 解压 tar -zxvf zookeeper-3.4.11.tar.gz -C /usr/local/ C. 创建数据存放目录 mkdir data D. 修改配置文件名称并配置 mv zoo_sample.cfg zoo.cfg E. 配置数据存放目录 dataDir=/usr/local/zookeeper-3.4.11/data F. 启动Zookeeper bin/zkServer.sh start
安装Mycat-web
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 A. 上传安装包 alt + p --------> put D:\tmp\Mycat-web-1.0-SNAPSHOT-20170102153329-linux.tar.gz B. 解压 tar -zxvf Mycat-web-1.0-SNAPSHOT-20170102153329-linux.tar.gz -C /usr/local/ C. 目录介绍 drwxr-xr-x. 2 root root 4096 Oct 19 2015 etc ----> jetty配置文件 drwxr-xr-x. 3 root root 4096 Oct 19 2015 lib ----> 依赖jar包 drwxr-xr-x. 7 root root 4096 Jan 1 2017 mycat-web ----> mycat-web项目 -rwxr-xr-x. 1 root root 116 Oct 19 2015 readme.txt -rwxr-xr-x. 1 root root 17125 Oct 19 2015 start.jar ----> 启动jar -rwxr-xr-x. 1 root root 381 Oct 19 2015 start.sh ----> linux启动脚本 D. 启动 sh start.sh E. 访问 http://192.168.192.147:8082/mycat
如果Zookeeper与Mycat-web不在同一台服务器上,需要设置Zookeeper的地址,在/usr/local/mycat-web/mycat-web/WEB-INF/classes/mycat.properties文件中配置:
配置
Mycat-web之MyCat性能监控 在 Mycat-web 上可以进行 Mycat 性能监控,例如:内存分析、流量分析、连接分析、活动线程分析等等。 如下图:
MyCat内存分析:MyCat的内存分析,反映了当前的内存使用情况与历史时间段的峰值、平均值。
MyCat流量分析:MyCat流量分析统计了历史时间段的流量峰值、当前值、平均值,是MyCat数据传输的重要指标, In代表输入, Out代表输出。
MyCat连接分析:MyCat连接分析,反映了MyCat的连接数 。
MyCat TPS分析:MyCat TPS 是并发性能的重要参数指标,指系统在每秒内能够处理的请求数量。 MyCat TPS的值越高,代表MyCat单位时间内能够处理的请求就越多,并发能力也就越高。
MyCat活动线程分析反映了MyCat线程的活动情况。
MyCat缓存队列分析,反映了当前在缓存队列中的任务数量。
Mycat-web之MySQL性能监控指标
MySQL配置
MySQL监控指标:可以通过MySQL服务监控检测每一个MySQL节点的运行状态,包含缓存命中率 、增删改查比例、流量统计、慢查询比例、线程、临时表等相关性能数据。
Mycat-web之SQL监控
SQL 统计
SQL表分析
SQL监控
高频SQL
慢SQL统计
SQL解析
MyCat 读写分离 MySQL主从复制原理 复制是指将主数据库的DDL 和 DML 操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持同步。MySQL支持一台主库同时向多台从库进行复制,从库同时也可以作为其他从服务器的主库,实现链状复制。
MySQL主从复制的原理如下 :
从上图来看,复制分成三步:
Master 主库在事务提交时,会把数据变更作为时间 Events 记录在二进制日志文件 Binlog 中;
主库推送二进制日志文件 Binlog 中的日志事件到从库的中继日志 Relay Log ;
slave重做中继日志中的事件,将改变反映它自己的数据。
MySQL 复制的优点:
主库出现问题,可以快速切换到从库提供服务。
可以在从库上执行查询操作,从主库中更新,实现读写分离,降低主库的访问压力。
可以在从库中执行备份,以避免备份期间影响主库的服务。
MySQL一主一从搭建 准备的两台机器:
MySQL
IP
端口号
Master
192.168.192.157
3306
Slave
192.168.192.158
3306
master
在master 的配置文件(/usr/my.cnf)中,配置如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 server-id =1 log-bin =/var/lib/mysql/mysqlbin binlog_format =STATEMENT read-only =0 binlog-do-db =db01
执行完毕之后,需要重启MySQL:
创建同步数据的账户,因为MySQL进行主从复制的时候需要一个同步账户(Salve节点通过这个同步账户读取Master节点上的二进制日志),并且进行授权操作:
1 2 3 grant replication slave on * .* to 'itcast' @'192.168.192.158' identified by '123456' ; flush privileges;
查看master状态:
字段含义:
1 2 3 File : 从哪个日志文件开始推送日志文件 Position : 从哪个位置开始推送日志 Binlog_Ignore_DB : 指定不需要同步的数据库
slave
在MySQL集群的slave节点配置文件/usr/my.cnf中,配置如下内容:
1 2 3 4 5 6 7 8 server-id =2 log-bin =/var/lib/mysql/mysqlbin relay-log =mysql-relay
执行完毕之后,需要重启MySQL:
执行如下指令 :
1 2 #指定当前从库对应的主库的IP地址,用户名,密码,从哪个日志文件开始的那个位置开始同步推送日志。 change master to master_host= '192.168.192.157' , master_user= 'itcast' , master_password= '123456' , master_log_file= 'mysqlbin.000001' , master_log_pos= 413 ;
开启同步操作
1 2 3 start slave;show slave status;
停止同步操作
验证主从同步
在主库中创建数据库,创建表,并插入数据 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 create database db01;user db01;create table user ( id int (11 ) not null auto_increment, name varchar (50 ) not null , sex varchar (1 ), primary key (id) )engine= innodb default charset= utf8; insert into user (id,name,sex) values (null ,'Tom' ,'1' );insert into user (id,name,sex) values (null ,'Trigger' ,'0' );insert into user (id,name,sex) values (null ,'Dawn' ,'1' );
在从库中查询数据,进行验证 :
在从库中,可以查看到刚才创建的数据库:
在该数据库中,查询user表中的数据:
MyCat一主一从读写分离 读写分离原理
读写分离:简单地说是把对数据库的读和写操作分开,以对应不同的数据库服务器。主数据库提供写操作,从数据库提供读操作,这样能有效地减轻单台数据库的压力。
通过MyCat即可轻易实现上述功能,不仅可以支持MySQL,也可以支持Oracle和SQL Server。MyCat控制后台数据库的读写分离,负载均衡由schema.xml文件datahost标签的balance属性控制。
读写分离配置 配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?xml version="1.0"?> <!DOCTYPE mycat :schema SYSTEM "schema.dtd" > <mycat:schema xmlns:mycat ="http://io.mycat/" > <schema name ="ITCAST" checkSQLschema ="true" sqlMaxLimit ="100" > <table name ="user" dataNode ="dn1" primaryKey ="id" /> </schema > <dataNode name ="dn1" dataHost ="localhost1" database ="db01" /> <dataHost name ="localhost1" 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.192.157:3306" user ="root" password ="123456" > <readHost host ="hostS1" url ="192.168.192.158:3306" user ="root" password ="123456" /> </writeHost > </dataHost > </mycat:schema >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <user name ="root" defaultAccount ="true" > <property name ="password" > 123456</property > <property name ="schemas" > ITCAST</property > </user > <user name ="test" > <property name ="password" > 123456</property > <property name ="schemas" > ITCAST</property > </user > <user name ="user" > <property name ="password" > 123456</property > <property name ="schemas" > ITCAST</property > <property name ="readOnly" > true</property > </user >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 属性含义说明: checkSQLschema 当该值设置为true时, 如果我们执行语句"select * from test01.user ;" 语句时, MyCat则会把schema字符去掉 , 可以避免后端数据库执行时报错 ; balance 负载均衡类型, 目前取值有4种: balance="0" : 不开启读写分离机制 , 所有读操作都发送到当前可用的writeHost上. balance="1" : 全部的readHost 与 stand by writeHost (备用的writeHost) 都参与select 语句的负载均衡,简而言之,就是采用双主双从模式(M1 --> S1 , M2 --> S2, 正常情况下,M2,S1,S2 都参与 select 语句的负载均衡。); balance="2" : 所有的读写操作都随机在writeHost , readHost上分发 balance="3" : 所有的读请求随机分发到writeHost对应的readHost上执行, writeHost不负担读压力 ;balance=3 只在MyCat1.4 之后生效 .
验证读写分离 当balance的值为默认值0时,查询MyCat中的逻辑表的数据变化:
在MyCat上执行下面查询:
通过日志信息发现,查询操作在主节点上执行:
将原来balance的默认值0修改为1,并重启MyCat,查询MyCat中的逻辑表的数据变化:
在MyCat上执行查询操作,通过日志信息发现,查询操作在从节点上执行:
之后在MyCat上执行下面的插入操作:
1 insert into user values (5 ,'Cat2' ,'1' );
结论:当设置balance=1时,所有的读请求在从节点上,而插入、更新等操作走主节点。
MySQL双主双从搭建 架构 一个主机 Master1 用于处理所有写请求,它的从机 Slave1 和另一台主机 Master2 还有它的从机 Slave2 负责所有读请求。当 Master1 主机宕机后,Master2 主机负责写请求,Master1 、Master2 互为备机。架构图如下:
双主双从配置 准备的机器如下:
编号
角色
IP地址
端口号
1
Master1
192.168.192.157
3306
2
Slave1
192.168.192.158
3306
3
Master2
192.168.192.159
3306
4
Slave2
192.168.192.160
3306
首先需要断掉之前的主从关系,执行下面两条语句:
1 2 stop salve; reset master;
1. 双主机配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 server-id =1 log-bin =mysql-bin binlog-do-db =db02 binlog-do-db =db03 binlog-do-db =db04 binlog_format =STATEMENT log-slave-updates
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 server-id =3 log-bin =mysql-bin binlog-do-db =db02 binlog-do-db =db03 binlog-do-db =db04 binlog_format =STATEMENT log-slave-updates
2. 双从机配置
1 2 3 4 5 server-id =2 relay-log =mysql-relay
1 2 3 4 5 server-id =4 relay-log =mysql-relay
3. 双主机、双从机重启 mysql 服务
4. 主机从机都关闭防火墙
5. 在两台主机上建立帐户并授权 slave
1 2 3 4 #在主机MySQL里执行授权命令 GRANT REPLICATION SLAVE ON * .* TO 'itcast' @'%' IDENTIFIED BY '123456' ;flush privileges;
查询Master1的状态 :
查询Master2的状态 :
6. 在从机上配置需要复制的主机:Slave1 复制 Master1,Slave2 复制 Master2。
slave1 指令:
1 2 3 4 CHANGE MASTER TO MASTER_HOST= '192.168.192.157' , MASTER_USER= 'itcast' , MASTER_PASSWORD= '123456' , MASTER_LOG_FILE= 'mysql-bin.000001' ,MASTER_LOG_POS= 399 ;
slave2 指令:
1 2 3 4 CHANGE MASTER TO MASTER_HOST= '192.168.192.159' , MASTER_USER= 'itcast' , MASTER_PASSWORD= '123456' , MASTER_LOG_FILE= 'mysql-bin.000001' ,MASTER_LOG_POS= 399 ;
7. 启动两台从服务器复制功能 , 查看主从复制的运行状态:
1 2 3 start slave;show slave status\G;
8. 两个主机互相复制:Master2 复制 Master1,Master1 复制 Master2。
Master1 执行指令:
1 2 3 4 CHANGE MASTER TO MASTER_HOST= '192.168.192.159' , MASTER_USER= 'itcast' , MASTER_PASSWORD= '123456' , MASTER_LOG_FILE= 'mysql-bin.000001' ,MASTER_LOG_POS= 399 ;
Master2 执行指令:
1 2 3 4 CHANGE MASTER TO MASTER_HOST= '192.168.192.157' , MASTER_USER= 'itcast' , MASTER_PASSWORD= '123456' , MASTER_LOG_FILE= 'mysql-bin.000001' ,MASTER_LOG_POS= 399 ;
9. 启动两台主服务器复制功能 , 查看主从复制的运行状态:
1 2 3 start slave;show slave status\G;
10. 验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 create database db03;use db03; create table user ( id int (11 ) not null auto_increment, name varchar (50 ) not null , sex varchar (1 ), primary key (id) )engine= innodb default charset= utf8; insert into user (id,name,sex) values (null ,'Tom' ,'1' );insert into user (id,name,sex) values (null ,'Trigger' ,'0' );insert into user (id,name,sex) values (null ,'Dawn' ,'1' );#上面三条语句在157 节点上执行,通过查询各个节点数据可知,上面的数据信息,全部同步到158 / 159 / 160 三个节点上。 insert into user (id,name,sex) values (null ,'Jack Ma' ,'1' );insert into user (id,name,sex) values (null ,'Coco' ,'0' );insert into user (id,name,sex) values (null ,'Jerry' ,'1' );#而这里的上面三条的插入语句在159 节点上执行,通过查询各个节点数据可知,上面的数据信息,也全部同步到157 / 158 / 160 三个节点上。
在Master1上创建数据库:
在Master1上创建表 :
11. 停止从服务复制功能
12. 重新配置主从关系
1 2 stop slave; reset master;
MyCat双主双从读写分离 配置 修改<dataHost>的 balance属性,通过此属性配置读写分离的类型 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?xml version="1.0"?> <!DOCTYPE mycat :schema SYSTEM "schema.dtd" > <mycat:schema xmlns:mycat ="http://io.mycat/" > <schema name ="ITCAST" checkSQLschema ="true" sqlMaxLimit ="100" > <table name ="user" dataNode ="dn1" primaryKey ="id" /> </schema > <dataNode name ="dn1" dataHost ="localhost1" database ="db03" /> <dataHost name ="localhost1" 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.192.157:3306" user ="root" password ="itcast" > <readHost host ="hostS1" url ="192.168.192.158:3306" user ="root" password ="123456" /> </writeHost > <writeHost host ="hostM2" url ="192.168.192.159:3306" user ="root" password ="itcast" > <readHost host ="hostS2" url ="192.168.192.160:3306" user ="root" password ="123456" /> </writeHost > </dataHost > </mycat:schema >
writeType:
switchType:
读写分离验证 查询数据 : select * from user;
插入数据 : insert into user(id,name,sex) values(null,’Dawn’,’1’);
可用性验证 关闭Master1,然后再执行写入的SQL语句 , 通过日志查询当前写入操作可知,写操作切换到Master2节点上。