Mycat学习总结
Posted 爱码人
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mycat学习总结相关的知识,希望对你有一定的参考价值。
http://www.mycat.org.cn/
https://github.com/MyCATApache/Mycat-Server
当Java服务直接连接mysql时,会出现java服务与数据库紧耦合,高访问量高并发对数据库的压力,读写请求数据不一致。
支持 SQL 92 标准;
支持 Mysql 集群,可以作为 Proxy 使用;
支持 JDBC 连接多数据库;
支持 NoSQL 数据库;
支持 galera for mysql 集群,percona-cluster 或者 mariadb cluster,提供高可用性数据分片集群;
自动故障切换,高可用性;
支持读写分离,支持 Mysql 双主多从,以及一主多从的模式;
支持全局表,数据自动分片到多个节点,用于高效表关联查询;
支持独有的基于 E-R 关系的分片策略,实现了高效的表关联查询;
支持一致性 Hash 分片,有效解决分片扩容难题;
多平台支持,部署和实施简单;
支持 Catelet 开发,类似数据库存储过程,用于跨分片复杂 SQL 的人工智能编码实现,143 行 Demo 完成 跨分片的两个表的 JION 查询;
支持 NIO 与 AIO 两种网络通信机制,Windows 下建议 AIO,Linux 下目前建议 NIO;
支持 Mysql 存储过程调用;
以插件方式支持 SQL 拦截和改写;
支持自增长主键、支持 Oracle 的 Sequence 机制。
单纯的读写分离,此时配置最为简单,支持读写分离,主从切换;
分表分库,对于超过 1000 万的表进行分片,最大支持 1000 亿的单表分片;
多租户应用,每个应用一个库,但应用程序只连接 Mycat,从而不改造程序本身,实现多租户化;
报表系统,借助于 Mycat 的分表能力,处理大规模报表的统计;
替代 Hbase,分析大数据;
作为海量数据实时查询的一种简单有效方案,比如 100 亿条频繁查询的记录需要在 3 秒内查询出来结果,
除了基于主键的查询,还可能存在范围查询或其他属性查询,此时 Mycat 可能是最简单有效的选择。
DDL 日志:MySQL DDL 日志,也称为元数据日志(metadata log)记录了数据定义语言执行的元数据操作,例如 DROP TABLE 或者 ALTER TABLE。
如果元数据操作的过程中发生故障,MySQL 使用该日志进行恢复。
数据库中间件:Mycat 是一个开源的分布式数据库系统,但是由于真正的数据库需要存储引擎,而 Mycat 并没有存储引擎,所以并不是完全意义的分布式数据库系统。那么 Mycat 是什么?Mycat 是数据库中间件,就是介于数据库与应用之间,进行数据处理与交互的中间服务。由于前面讲的对数据进行分片处理之后,从原有的一个库,被切分为多个分片数据库,所有的分片数据库集群构成了整个完整的数据库存储。数据被分到多个分片数据库后,应用如果需要读取数据,就要需要处理多个数据源的数据。如果没有数据库中间件,那么应用将直接面对分片集群,数据源切换、事务处理、数据聚合都需要应用直接处理,原本该是专注于业务的应用,将会花大量的工作来处理分片后的问题,最重要的是每个应用处理将是完全的重复造轮子。所以有了数据库中间件,应用只需要集中与业务处理,大量的通用的数据聚合,事务,数据源切换都由中间件来处理,中间件的性能与处理能力将直接决定应用的读写性能,所以一款好的数据库中间件至关重要。
逻辑库(schema):通常对实际应用来说,并不需要知道中间件的存在,业务开发人员只需要知道数据库的概念,所以数据库中间件可以被看做是一个或多个数据库集群构成的逻辑库。在云计算时代,数据库中间件可以以多租户的形式给一个或多个应用提供服务,每个应用访问的可能是一个独立或者是共享的物理库,常见的如阿里云数据库服务器 RDS。
3.2 分片表:分片表,是指那些原有的很大数据的表,需要切分到多个数据库的表,这样,每个分片都有一部分数据,所有分片构成了完整的数据。例如在 mycat 配置中的 t_node 就属于分片表,数据按照规则被分到 dn1,dn2 两个分片节点(dataNode)上。
<table name="t_node" primaryKey="vid" autoIncrement="true" dataNode="dn1,dn2" rule="rule1" />
3.3 非分片表:一个数据库中并不是所有的表都很大,某些表是可以不用进行切分的,非分片是相对分片表来说的,就是那些不需要进行数据切分的表。如下配置中 t_node,只存在于分片节点(dataNode)dn1 上。
<table name="t_node" primaryKey="vid" autoIncrement="true" dataNode="dn1" />
3.4 ER 表:关系型数据库是基于实体关系模型(Entity-Relationship Model)之上,通过其描述了真实世界中事物与关系,Mycat 中的 ER 表即是来源于此。根据这一思路,提出了基于 E-R 关系的数据分片策略,子表的记录与所关联的父表记录存放在同一个数据分片上,即子表依赖于父表,通过表分组(Table Group)保证数据 Join 不会跨库操作。表分组(Table Group)是解决跨分片数据 join 的一种很好的思路,也是数据切分规划的重要一条规则。
3.5 全局表:一个真实的业务系统中,往往存在大量的类似字典表的表,这些表基本上很少变动,字典表具有以下几个特性:
• 变动不频繁;
• 数据量总体变化不大;
• 数据规模不大,很少有超过数十万条记录。
对于这类的表,在分片的情况下,当业务表因为规模而进行分片以后,业务表与这些附属的字典表之间的关
联,就成了比较棘手的问题,所以 Mycat 中通过数据冗余来解决这类表的 join,即所有的分片都有一份数据的拷
贝,所有将字典表或者符合字典表特性的一些表定义为全局表。数据冗余是解决跨分片数据 join 的一种很好的思路,
也是数据切分规划的另外一条重要规则。
4. 分片节点(dataNode):数据切分后,一个大表被分到不同的分片数据库上面,每个表分片所在的数据库就是分片节点(dataNode)。
5.节点主机(dataHost):数据切分后,每个分片节点(dataNode)不一定都会独占一台机器,同一机器上面可以有多个分片数据库,这样一个或多个分片节点(dataNode)所在的机器就是节点主机(dataHost),为了规避单节点主机并发数限制,尽量将读写压力高的分片节点(dataNode)均衡的放在不同的节点主机(dataHost)。
6.分片规则(rule):一个大表被分成若干个分片表,就需要一定的规则,这样按照某种业务规则把数据分到某个分片的规则就是分片规则,数据切分选择合适的分片规则非常重要,将极大的避免后续数据处理的难度。
7.全局序列号(sequence):数据切分后,原有的关系数据库中的主键约束在分布式条件下将无法使用,因此需要引入外部机制保证数据唯一性标识,这种保证全局性的数据唯一标识的机制就是全局序列号(sequence)。
8.多租户:多租户技术或称多重租赁技术,是一种软件架构技术,它是在探讨与实现如何于多用户的环境下共用相同的系统或程序组件,并且仍可确保各用户间数据的隔离性。在云计算时代,多租户技术在共用的数据中心以单一系
统架构与服务提供多数客户端相同甚至可定制化的服务,并且仍然可以保障客户的数据隔离。目前各种各样的云计算服务就是这类技术范畴,例如阿里云数据库服务(RDS)、阿里云服务器等等。多租户在数据存储上存在三种主要的方案,分别是:
8.1 独立数据库
这是第一种方案,即一个租户一个数据库,这种方案的用户数据隔离级别最高,安全性最好,但成本也高。
优点:
为不同的租户提供独立的数据库,有助于简化数据模型的扩展设计,满足不同租户的独特需求;如果出现故障,恢复数据比较简单。
缺点:
增大了数据库的安装数量,随之带来维护成本和购置成本的增加。
这种方案与传统的一个客户、一套数据、一套部署类似,差别只在于软件统一部署在运营商那里。如果面对的是银行、医院等需要非常高数据隔离级别的租户,可以选择这种模式,提高租用的定价。如果定价较低,产品走低价路线,这种方案一般对运营商来说是无法承受的。
8.2 共享数据库,隔离数据架构
这是第二种方案,即多个或所有租户共享 Database,但是每个租户一个 Schema。
优点:
为安全性要求较高的租户提供了一定程度的逻辑数据隔离,并不是完全隔离;每个数据库可以支持更多的租户数量。
缺点:
如果出现故障,数据恢复比较困难,因为恢复数据库将牵扯到其他租户的数据;如果需要跨租户统计数据,存在一定困难。
8.3 共享数据库,共享数据架构
这是第三种方案,即租户共享同一个 Database、同一个 Schema,但在表中通过 TenantID 区分租户的数据。这是共享程度最高、隔离级别最低的模式。
优点:
三种方案比较,第三种方案的维护和购置成本最低,允许每个数据库支持的租户数量最多。
缺点:
隔离级别最低,安全性最低,需要在设计开发时加大对安全的开发量;
数据备份和恢复最困难,需要逐表逐条备份和还原;
如果希望以最少的服务器为最多的租户提供服务,并且租户接受以牺牲隔离级别换取降低成本,这种方案最适合。
二、Mycat安装
schema.xml :定义逻辑库 、表,分片节点等内容。
server.xml :定义用户以及系统相关变量,用户名密码端口等。
rule.xml : 定义分片规则。
# 安装
https://github.com/dekuan/docker.mycat
# 详细配置建议参考官方配置说明
https://github.com/MyCATApache/Mycat-Server
# 参考资料:
https://github.com/liuwel/docker-mycat
第一步:下载源码 Docker 制作源码、修改后的 Mycat 1.6.5 jar 包、Mycat 配置文件:
git clone https://github.com/dekuan/docker.mycat.gi
第二步:编译/创建 Mycat Docker
cd docker.mycat
docker-compose build
第三步:启动 Mycat Docker
docker-compose up -d
配置方法:
Mycat 用户名和密码配置:打开配置文件 config/mycat/server.xml
vim config/mycat/server.xml
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
...
</system>
<!-- Mycat 数据库用户名 -->
<user name="druid">
<!-- Mycat 数据库密码 -->
<property name="password">druid</property>
<!-- Mycat 数据库名 -->
<property name="schemas">druid</property>
<!-- 是否使用加密的密码,0 表示不使用加密的密码 -->
<property name="usingDecrypt">0</property>
</user>
</mycat:server>
Mycat 数据节点、数据库、分库分表配置:打开配置文件 config/mycat/schema.xml
vim config/mycat/schema.xml
# 详细配置建议参考官方配置说明
https://github.com/MyCATApache/Mycat-Server
列子:
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="druid" dataNode="dataNode1" checkSQLschema="true" sqlMaxLimit="100">
<!--subTables="boards_$0-127"-->
<table
name="boards"
dataNode="dataNode1,dataNode2,dataNode3,dataNode4,dataNode5,dataNode6"
rule="sharding-by-murmur"/>
</schema>
<dataNode name="dataNode1" dataHost="dataHost1" database="druid_1" />
<dataNode name="dataNode2" dataHost="dataHost2" database="druid_2" />
<dataNode name="dataNode3" dataHost="dataHost3" database="druid_3" />
<dataNode name="dataNode4" dataHost="dataHost4" database="druid_4" />
<dataNode name="dataNode5" dataHost="dataHost5" database="druid_5" />
<dataNode name="dataNode6" dataHost="dataHost6" database="druid_6" />
<!--
balance
0, 不开启读写分离机制,所有读操作都发送到当前可用的writeHost上。
1,全部的readHost与stand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且M1与M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡。
2,所有读操作都随机的在writeHost、readhost上分发。
3,所有读请求随机的分发到wiriterHost对应的readhost执行,writerHost不负担读压力
writeType 表示写模式
0,所有的操作发送到配置的第一个writehost
1,随机发送到配置的所有writehost
2,不执行写操作
switchType 指的是切换的模式,目前的取值也有4种:
-1,表示不自动切换
1,默认值,表示自动切换
2,基于MySQL主从同步的状态决定是否切换,心跳语句为show slave status
3,基于MySQL galary cluster的切换机制(适合集群)(1.4.1),心跳语句为show status like ‘wsrep%‘。
-->
<dataHost name="dataHost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="-1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost
host="192.168.1.104"
url="jdbc:mysql://192.168.1.104:3306?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"
user="druid_1" password="druid_1">
<!--<readHost host="192.168.1.104" url="192.168.1.104:3306" user="druid" password="druid" />-->
</writeHost>
</dataHost>
<dataHost name="dataHost2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="-1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost
host="192.168.1.104"
url="jdbc:mysql://192.168.1.104:3306?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"
user="druid_2" password="druid_2">
<!--<readHost host="192.168.1.104" url="192.168.1.104:3306" user="druid" password="druid" />-->
</writeHost>
</dataHost>
<dataHost name="dataHost3" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="-1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost
host="192.168.1.104"
url="jdbc:mysql://192.168.1.104:3306?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"
user="druid_3" password="druid_3">
<!--<readHost host="192.168.1.104" url="192.168.1.104:3306" user="druid" password="druid" />-->
</writeHost>
</dataHost>
<dataHost name="dataHost4" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="-1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost
host="192.168.1.104"
url="jdbc:mysql://192.168.1.104:3306?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"
user="druid_4" password="druid_4">
<!--<readHost host="192.168.1.104" url="192.168.1.104:3306" user="druid" password="druid" />-->
</writeHost>
</dataHost>
<dataHost name="dataHost5" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="-1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost
host="192.168.1.104"
url="jdbc:mysql://192.168.1.104:3306?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"
user="druid_5" password="druid_5">
<!--<readHost host="192.168.1.104" url="192.168.1.104:3306" user="druid" password="druid" />-->
</writeHost>
</dataHost>
<dataHost name="dataHost6" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="-1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost
host="192.168.1.104"
url="jdbc:mysql://192.168.1.104:3306?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"
user="druid_6" password="druid_6">
<!--<readHost host="192.168.1.104" url="192.168.1.104:3306" user="druid" password="druid" />-->
</writeHost>
</dataHost>
</mycat:schema>
三、实战
1.搭建读写分离
MySQL支持单向、异步复制,复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器。
MySQL复制是基于主服务器在二进制日志中跟踪所有对数据库的更改。因此,要进行复制,必须在主服务器上启用二进制日志。每个从服务器从主服务器接收主服务器已经记录到日志的数据。
当一个从服务器连接主服务器时,它通知主服务器从服务器在日志中读取的最后一次成功更新的位置。从服务器接收从那时起发生的任何更新,并在本机上执行相同的更新。然后封锁并等待主服务器通知新的更新。从服务器执行备份不会干扰主服务器,在备份过程中主服务器可以继续处理更新。
Binary log:主数据库的二进制日志。
Relay log:从服务器的中继日志。
第一步:master在每个事务更新数据完成之前,将该操作记录串行地写入到binlog文件中。
第二步:salve开启一个I/O Thread,该线程在master打开一个普通连接,主要工作是binlog dump process。如果读取的进度已经跟上了master,就进入睡眠状态并等待master产生新的事件。I/O线程最终的目的是将这些事件写入到中继日志中。
第三步:SQL Thread会读取中继日志,并顺序执行该日志中的SQL事件,从而与主数据库中的数据保持一致。
主数据库出现问题,可以切换到从数据库。
可以进行数据库层面的读写分离。
可以在从数据库上进行日常备份。
1.1 一主一从
安装好mysql数据库,(121数据库作为主库,202数据库作为从库):
# 在121服务器上 启动主库
docker run -d --name master_mysql -v /root/mysql/data:/var/lib/mysql -v /root/mysql/conf:/etc/mysql/mysql.conf.d -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 mysql:5.7
# 在202服务器上 启动从库
docker run -d --name slave_mysql -v /root/mysql/data:/var/lib/mysql -v /root/mysql/conf:/etc/mysql/mysql.conf.d -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 mysql:5.7
第一步:主库配置:修改配置文件mysqld.cnf
# 主服务器唯一ID
server-id=121
# 启用二进制日志
log-bin=mysql-bin
# 设置不需要复制额数据库 (可设置多个)
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
# 设置需要复制的数据库 需要复制的主数据库名
binlog-do-db=testdb
# 设置logbin格式binlog_format=STATEMENT
注:logbin有三种格式:
1.STATEMENT:每一条会修改数据的sql都会记录在binlog中。
2.ROW:不记录sql语句上下文相关信息,仅保存哪条记录被修改。
3.MIXED: 是以上两种level的混合使用,一般的语句修改使用statment格式保存binlog,如一些函数,statement无法完成主从复制的操作,则采用row格式保存binlog,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一种.新版本的MySQL中队row level模式也被做了优化,并不是所有的修改都会以row level来记录,像遇到表结构变更的时候就会以statement模式来记录。至于update或者delete等修改数据的语句,还是会记录所有行的变更。
Mysql默认是使用STATEMENT日志格式,推荐使用MIXED.由于一些特殊使用,可以考虑使用ROWED,如自己通过binlog日志来同步数据的修改,这样会节省很多相关操作。对于binlog数据处理会变得非常轻松,相对mixed,解析也会很轻松(当然前提是增加的日志量所带来的IO开销在容忍的范围内即可)。
第二步:从库配置:修改配置文件mysqld.cnf
# 从服务器唯一ID
server-id=202
# 启用中继日志
relay-log=mysql-relay
第三步:主机从机重启mysql服务。
第四步:主机从机关闭防火墙或者修改防火墙保证主从互通,这步需要注意,如果连不上数据库,需要检查防火墙和安全组是否给了权限。
第五步:在主机上建立账户,并授权slave
# 在主机mysql 里执行授权命令
GRANT REPLICATION SLAVE ON *.* TO 'slave'@'120.55.65.202' IDENTIFIED BY '123520';
# 查询master的状态
show master status\G;
第六步:在从机上配制需要复制的主机
# 登进去从服务器
mysql -uroot -proot
CHANGE MASTER TO MASTER_HOST='主机的IP地址',
MASTER_USER='slave',
MASTER_PASSWORD='123520',
# 从主服务器上取
MASTER_LOG_FILE='mysql-bin.具体值',MASTER_LOG_POS=具体值;
# 重新配置主从
# 如果之前搭建过主从,请先停止主从,否则会报错,没错的话,就忽略这步
stop slave:
#如果报错,重新设置一下master,没错的话,就忽略这步
reset master
# 启动从服务器复制功能
start slave;
# 查看从服务器状态
show slave status\G
搭建完毕!!!!!!!!!
测试:
在主库里(121)创建一个刚才设置好的数据库testdb:
去从服务器(202)上看看是否有testdb数据库:
可以看出来从库已经有了数据库testdb。再在主库上创建一张表user,从库上也有表user,在主库添加数据,从库也会有数据:
刷新一下从数据库,发现已经有表了:
一主一从的主从复制搭建完毕!!!!!!!!!!!!!!!
一主一从的读写分离:
第一步:配置好一主一从的主从复制,保证主从复制的功能正常。
第二步:部署好mycat的服务,修改配置文件schema.xml:
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" dataNode="dn1" checkSQLschema="true" sqlMaxLimit="100">
</schema>
<dataNode name="dn1" dataHost="host1" database="testdb" />
<!--
balance
0, 不开启读写分离机制,所有读操作都发送到当前可用的writeHost上。
1,全部的readHost与stand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且M1与M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡。
2,所有读操作都随机的在writeHost、readhost上分发。
3,所有读请求随机的分发到wiriterHost对应的readhost执行,writerHost不负担读压力
writeType 表示写模式
0,所有的操作发送到配置的第一个writehost
1,随机发送到配置的所有writehost
2,不执行写操作
switchType 指的是切换的模式,目前的取值也有4种:
-1,表示不自动切换
1,默认值,表示自动切换
2,基于MySQL主从同步的状态决定是否切换,心跳语句为show slave status
3,基于MySQL galary cluster的切换机制(适合集群)(1.4.1),心跳语句为show status like ‘wsrep%‘。
-->
<dataHost name="host1" maxCon="1000" minCon="10" balance="2"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="-1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost
host="121.4.237.121"
url="jdbc:mysql://121.4.237.121:3306?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"
user="root" password="root">
<readHost host="120.55.65.202" url="jdbc:mysql://120.55.65.202:3306?useSSL=false&serverTimezone=UTC&characterEncoding=utf8" user="root" password="root" />
</writeHost>
</dataHost>
</mycat:schema>
# 注: 为了测试,我把balance设置了2,在企业实战中把balance设置1或者3
修改server.xml文件:
重新启动mycat服务。测试连接:
验证读写分离:binlog日志当sql语句有函数、或系统变量等就会造成主从复制的数据不一样。
# 在主机主库里添加一条数据:
INSERT INTO `user` VALUES(3,@@hostname,16)
# 主从库的数据不一致了
# 在mycat里查询
select * from user
在mycat上多查几次,发现:
此时,我想起了nginx的负载均衡了。再将配置文件中的balance的值改为3,即所有读请求随机的分发到wiriterHost对应的readhost执行,writerHost不负担读压力。重启mycat,猜测:在mycat上无论查询多少次,都是到从库e3082667be60:
猜想正确,读写分离实现了。
1.2 双主双从(正式环境的话,最好是一台服务器安装一个mysql)
准备4台机器,在这我用2台机器仿4个mysql服务做实验:
角色 |
主机:端口 |
备注 |
m1 |
121.4.237.121:3306 | master1 |
s1 |
120.55.65.202:3306 | slave1 |
m2 |
120.55.65.202:3307 | master2 |
s2 |
121.4.237.121:3307 | slave2 |
搭建双主双从的主从复制:
第一步:启动4个mysql服务容器,可用。
# 在121上启动
docker run -d --name m1 -v /root/mysql_m1/data:/var/lib/mysql -v /root/mysql_m1/conf:/etc/mysql/mysql.conf.d -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 mysql:5.7
docker run -d --name s2 -v /root/mysql_s2/data:/var/lib/mysql -v /root/mysql_s2/conf:/etc/mysql/mysql.conf.d -e MYSQL_ROOT_PASSWORD=root -p 3307:3306 mysql:5.7
# 在202上启动
docker run -d --name m2 -v /root/mysql_m2/data:/var/lib/mysql -v /root/mysql_m2/conf:/etc/mysql/mysql.conf.d -e MYSQL_ROOT_PASSWORD=root -p 3307:3306 mysql:5.7
docker run -d --name s1 -v /root/mysql_s1/data:/var/lib/mysql -v /root/mysql_s1/conf:/etc/mysql/mysql.conf.d -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 mysql:5.7
第二步:双主机配置:
修改m1配置文件:
# 主服务器唯一ID
server-id=1
# 启用二进制日志
log-bin=mysql-bin
# 设置不需要复制额数据库 (可设置多个)
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
# 设置需要复制的数据库 需要复制的主数据库名
binlog-do-db=需要复制的主数据库名
# 设置logbin格式
binlog_format=STATEMENT
# 在作为从数据库的时候,有写入操作也要更新二进制日志文件
log-slave-updates
# 表示自增长字段 每次递增的量,其默认值为1,范围1...65535
auto-increment-increment=2
# 表示自增长字段从哪个数开始,取值范围1...65535
auto-increment-offset=1
修改m2的配置文件:
# 主服务器唯一ID
server-id=3
# 启用二进制日志
log-bin=mysql-bin
# 设置不需要复制额数据库 (可设置多个)
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
# 设置需要复制的数据库 需要复制的主数据库名
binlog-do-db=需要复制的主数据库名
# 设置logbin格式
binlog_format=STATEMENT
# 在作为从数据库的时候,有写入操作也要更新二进制日志文件
log-slave-updates
# 表示自增长字段 每次递增的量,其默认值为1,范围1...65535
auto-increment-increment=2
# 表示自增长字段从哪个数开始,取值范围1...65535
auto-increment-offset=2
修改s1配置文件:
# 从服务器唯一ID
server-id=2
# 启用中继日志
relay-log=mysql-relay
修改s2配置文件:
# 从服务器唯一ID
server-id=4
# 启用中继日志
relay-log=mysql-relay
第三步:双主机,双从机重启mysql服务
第四步:保证机器之间互通,关闭防火墙或者设置防火墙
第五步:在两台主机上建立账户,并授权slave
# 进去容器内
docker exec -it m1 bash
# 进去mysql
mysql -uroot -proot
# 在主机mysql 里执行授权命令
GRANT REPLICATION SLAVE ON *.* TO 'slave'@'%' IDENTIFIED BY '123520';
# 查询master的状态
show master status;
注:由于刚才在服务器上搭建了一主一从的主从复制,所以先关闭一下:
# 这些命令需要进入数据库
# 在从服务器上执行
stop slave;
# 在从主服务器上执行
reset master;
第六步:在分别在从机上配制各自对应的需要复制的主机:
# 停止slave
stop slave;
# s1
CHANGE MASTER TO MASTER_HOST='121.4.237.121',
MASTER_USER='slave',
MASTER_PASSWORD='123520',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=443;
# s2
CHANGE MASTER TO MASTER_HOST='120.55.65.202',
MASTER_USER='slave',
MASTER_PASSWORD='123520',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=443;
# 开启slave
start slave;
# 查看slave
show slave status\G
小插曲:S2没有同步成功,看一下日志:
从日志看,是没有找对主机的二进制文件,那是因为我一台服务器上部署了2个mysql,以不同的端口号进行区分的,我在配置从机的时候,没有加主机的端口号,所以找不到二进制文件,细心呀!!!
# s1
CHANGE MASTER TO MASTER_HOST='121.4.237.121',
MASTER_USER='slave',
MASTER_PORT=3306,
MASTER_PASSWORD='123520',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=443;
# s2
CHANGE MASTER TO MASTER_HOST='120.55.65.202',
MASTER_USER='slave',
MASTER_PORT=3307,
MASTER_PASSWORD='123520',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=443;
# 开启slave
start slave;
# 查看slave
show slave status\G
# 标准示范
change master to master_user='名称',master_password='密码',master_host='主数据IP',master_port=3306,master_log_file='当前日志名称',master_log_pos=数字;
第七步:两个主机互相复制
# 在m1上操作 m1相当于是m2的从机
CHANGE MASTER TO MASTER_HOST='120.55.65.202',
MASTER_USER='slave',
MASTER_PORT=3307,
MASTER_PASSWORD='123520',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=443;
# 在m2上操作 m2相当于是m1的从机
CHANGE MASTER TO MASTER_HOST='121.4.237.121',
MASTER_USER='slave',
MASTER_PORT=3306,
MASTER_PASSWORD='123520',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=443;
# 开启slave
start slave;
# 查看slave
show slave status\G
到此,双主双从的主从复制搭建完毕。测试一下:在m2上创建数据库hugedb,然后创建一个表user,看看其他三个数据库上有没有:
四台机器上的数据都是一样的,测试成功!!!!!!!!!上面的双主双从的主从复制搭建完毕,接着要搭建双主双从的读写分离:
第八步:修改mycat配置文件schema.xml文件:
注:
balance="1"
switchType="1" //默认值,表示自动切换
<schema name="TESTDB" dataNode="dn1" checkSQLschema="true" sqlMaxLimit="100">
</schema>
<dataNode name="dn1" dataHost="host1" database="hugedb" />
0, 不开启读写分离机制,所有读操作都发送到当前可用的writeHost上。
2,所有读操作都随机的在writeHost、readhost上分发。
3,所有读请求随机的分发到wiriterHost对应的readhost执行,writerHost不负担读压力
writeType 表示写模式
0,所有的操作发送到配置的第一个writehost
1,随机发送到配置的所有writehost
2,不执行写操作
switchType 指的是切换的模式,目前的取值也有4种:
-1,表示不自动切换
1,默认值,表示自动切换
2,基于MySQL主从同步的状态决定是否切换,心跳语句为show slave status
3,基于MySQL galary cluster的切换机制(适合集群)(1.4.1),心跳语句为show status like ‘wsrep%‘。
-->
<dataHost name="host1" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="-1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost
host="121.4.237.121"
url="jdbc:mysql://121.4.237.121:3306?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"
user="root" password="root">
<readHost host="120.55.65.202" url="jdbc:mysql://120.55.65.202:3306?useSSL=false&serverTimezone=UTC&characterEncoding=utf8" user="root" password="root" />
</writeHost>
<writeHost
host="120.55.65.202"
url="jdbc:mysql://120.55.65.202:3307?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"
user="root" password="root">
<readHost host="121.4.237.121" url="jdbc:mysql://121.4.237.121:3307?useSSL=false&serverTimezone=UTC&characterEncoding=utf8" user="root" password="root" />
</writeHost>
</dataHost>
</mycat:schema>
第九步:启动mycat
docker-compose up -d
第十步:验证双主双从的读写分离
在m1主机上数据库表中插入带系统变量的数据,造成主从数据不一样
INSERT INTO user VALUES (2,@@hostname,99)
查看一下mycat,多刷新几次:
2.垂直分库实战
优点:
拆分后业务清晰,拆分规则明确;
系统之间整合或扩展容易;
数据维护简单。
缺点:
部分业务表无法 join,只能通过接口方式解决,提高了系统复杂度;
受每种业务不同的限制存在单库性能瓶颈,不易数据扩展跟性能提高;
事务处理复杂。
由于垂直切分是按照业务的分类将表分散到不同的库,所以有些业务表会过于庞大,存在单库读写与存储瓶颈,所以就需要水平拆分来做解决。比如四张表:用户表、订单表、订单详情表、字典表。注:在两台主机上的两个数据库中的表本可以关联查询。分库原则:有紧密关联关系的表应该在一个库里,相互没有关系的表可以分到不同的数据库里。
# 用户表
CREATE TABLE customer(
id INT AUTO_INCREMENT,
name VARCHAR(200),
PRIMARY KEY(id)
);
# 订单表
CREATE TABLE orders(
id INT AUTO_INCREMENT,
order_type INT,
customer_id INT,
amount DECIMAL(10,2),
PRIMARY KEY(id)
);
# 订单详情表
CREATE TABLE order_details(
id INT AUTO_INCREMENT,
detail VARCHAR(2000),
order_id INT,
PRIMARY KEY(id)
);
#字典表
CREATE TABLE dict_order_type(
id INT AUTO_INCREMENT,
order_type VARCHAR(200),
PRIMARY KEY(id)
);
第一步:修改mycat配置文件
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" dataNode="dn1" checkSQLschema="true" sqlMaxLimit="100">
<table name="customer" dataNode="dn2" ></table>
</schema>
<dataNode name="dn1" dataHost="host1" database="orders" />
<dataNode name="dn2" dataHost="host2" database="orders" />
<!--
balance
0, 不开启读写分离机制,所有读操作都发送到当前可用的writeHost上。
1,全部的readHost与stand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且M1与M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡。
2,所有读操作都随机的在writeHost、readhost上分发。
3,所有读请求随机的分发到wiriterHost对应的readhost执行,writerHost不负担读压力
writeType 表示写模式
0,所有的操作发送到配置的第一个writehost
1,随机发送到配置的所有writehost
2,不执行写操作
switchType 指的是切换的模式,目前的取值也有4种:
-1,表示不自动切换
1,默认值,表示自动切换
2,基于MySQL主从同步的状态决定是否切换,心跳语句为show slave status
3,基于MySQL galary cluster的切换机制(适合集群)(1.4.1),心跳语句为show status like ‘wsrep%‘。
-->
<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="121.4.237.121"
url="jdbc:mysql://121.4.237.121:3306?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"
user="root" password="root">
</writeHost>
</dataHost>
<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="120.55.65.202"
url="jdbc:mysql://120.55.65.202:3306?useSSL=false&serverTimezone=UTC&characterEncoding=utf8" user="root" password="root">
</writeHost>
</dataHost>
</mycat:schema>
第二步:启动dn1,dn2两个数据库,并在两个数据库创建orders数据库
第三步:重启mycat数据库,并在mycat上面创建表
第四步:验证。查看在mycat上面创建的表是否按照配置的,分到对应的实际数据库里。
从上面测试来看,已经实现了分库的操作,在mycat里可以查出四张表,在dn1里可以查出三张表,dn2可以查出一张表。
3.水平分表实战
几种典型的分片规则包括:
按照用户 ID 求模,将数据分散到不同的数据库,具有相同数据用户的数据都被分散到一个库中;
按照日期,将不同月甚至日的数据分散到不同的库中;
按照某个特定的字段求摸,或者根据特定范围段分散到不同的库中。
如图,切分原则都是根据业务找到适合的切分规则分散到不同的库,下面用用户 ID 求模举例:
优点:
拆分规则抽象好,join 操作基本可以数据库做;
不存在单库大数据,高并发的性能瓶颈;
应用端改造较少;
提高了系统的稳定性跟负载能力。
缺点:
拆分规则难以抽象;
分片事务一致性难以解决;
数据多次扩展难度跟维护量极大;
跨库 join 性能较差。
比较垂直切分跟水平切分的不同跟优缺点,会发现每种切分方式都有缺点,但共同的特点缺点有:
引入分布式事务的问题;
跨节点 Join 的问题;
跨节点合并排序分页问题;
多数据源管理问题。
针对数据源管理,目前主要有两种思路:
客户端模式,在每个应用程序模块中配置管理自己需要的一个(或者多个)数据源,直接访问各个数据库, 在模块内完成数据的整合;
通过中间代理层来统一管理所有的数据源,后端数据库集群对前端应用程序透明;
对上面这两种解决思路的时候都会倾向于选择第二种,尤其是系统不断变得庞大复杂的时候。确实,这是一个非常正确的选择,虽然短期内需要付出的成本可能会相对更大一些,但是对整个系统的扩展性来说,是非常有帮助的。Mycat 通过数据切分解决传统数据库的缺陷,又有了 NoSQL 易于扩展的优点。通过中间代理层规避了多数 据源的处理问题,对应用完全透明,同时对数据切分后存在的问题,也做了解决方案。由于数据切分后数据 Join 的难度在此也分享一下数据切分的经验:
第一原则:能不切分尽量不要切分。
第二原则:如果要切分一定要选择合适的切分规则,提前规划好。
第三原则:数据切分尽量通过数据冗余或表分组(Table Group)来降低跨库 Join 的可能。
第四原则:由于数据库中间件对数据 Join 实现的优劣难以把握,而且实现高性能难度极大,业务读取尽量少使用多表 Join。
3.1 水平分表
尽可能的比较均匀分布数据到各个节点上;
该业务字段是最频繁的或者最重要的查询条件。
# 1.修改定义的mod_rule
<table name="orders" dataNode="dn1,dn2" rule="mod_rule" ></table>
# 2.修改对应算法的分片数,我们有2个节点,就改成2
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">2</property>
</function>
<!-- - - 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. -->
<mycat:rule xmlns:mycat="http://io.mycat/">
<tableRule name="mod_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="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>u_mid</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">6</property><!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
<property name="virtualBucketTimes">160</property><!-- 一个实际的数据库节点被映射为这么多虚拟节点,默认是160倍,也就是虚拟节点数是物理节点数的160倍 -->
<!-- <property name="weightMapFile">weightMapFile</property> 节点的权重,没有指定权重的节点默认是1。以properties文件的格式填写,以从0开始到count-1的整数值也就是节点索引为key,以节点权重值为值。所有权重值必须是正整数,否则以1代替 -->
<!-- <property name="bucketMapPath">/etc/mycat/bucketMapPath</property>
用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的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 -->
<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="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>
分片规则可以参考:
https://blog.csdn.net/feiying0canglang/article/details/105323654
第三步:保证2个节点dn1和dn2上都有orders这个表,没有的要提前创建好,然后重启mycat。
第四步:访问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,100200);
如果不带字段情况,报错,也就是找不到分片的字段:
第五步:测试验证,在mycat上面查看应该是6条:
dn1上:
dn2上:
测试符合预期目标,将数据库的数据分在了不同机器上的不同数据里,并且是根据客户ID去分的。
3.2 水平分表ER表
<table name="orders" dataNode="dn1,dn2" rule="mod_rule" >
<childTable name="detail" primaryKey="id" joinKey="order_id" parentKey="id"/>
</table>
INSERT INTO order_details(id,detail,order_id) VALUES(1,"zhuimeng1",1);
INSERT INTO order_details(id,detail,order_id) VALUES(2,"zhuimeng2",2);
INSERT INTO order_details(id,detail,order_id) VALUES(3,"zhuimeng3",3);
INSERT INTO order_details(id,detail,order_id) VALUES(4,"zhuimeng4",4);
INSERT INTO order_details(id,detail,order_id) VALUES(5,"zhuimeng5",5);
INSERT INTO order_details(id,detail,order_id) VALUES(6,"zhuimeng6",6);
这样可能会报错:
这是个大坑:网上给的解决办法:
https://github.com/MyCATApache/Mycat-Server/issues/1370
查一下现在使用的是1.6.5版本。mycat目前稳定版本是1.6.7.x版本,试一下1.6.7.6版本看看。参考:
https://github.com/MyCATApache/Mycat-Server/wiki/2.1-docker%E5%AE%89%E8%A3%85Mycat
启动mycat:
docker run --privileged=true -p 8066:8066 -p 9066:9066 --name mycat -v /root/mycat/conf:/usr/local/mycat/conf -v /root/mycat/logs:/usr/local/mycat/logs -d mycat:1.6.7.6
尝试了各种方法还是没解决,有哪位大神知道解决方法麻烦告诉一下!但是,网上给了我思路:当insert ER子表 joinKey的字段是父表的分片字段时,插入子表是正常的,不会抛错误的。于是,我把实验改一下,将orders分片的字段改成id,然后在mycat里重新添加数据,最终添加子表的数据不会报错了。
测试一下,符合测试预期要求。
select o.*,od.detail from orders o inner join order_details od on o.id=od.order_id
3.3 水平分表全局表
变动不频繁
数据量总体变化不大
数据规模不大,很少有超过数十万条记录。
MyCAT 定义了一种特殊的表,称之为“全局表”,全局表具有以下特性:
全局表的插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性
全局表的查询操作,只从一个节点获取
全局表可以跟任何一个表进行 JOIN 操作
将字典表或者符合字典表特性的一些表定义为全局表,则从另外一个方面,很好的解决了数据 JOIN 的难题。通过全局表+基于 E-R 关系的分片策略,MyCAT 可以满足 80%以上的企业应用开发。
第一步:修改配置文件schema.xml文件
<table name="dict_order_type" dataNode="dn1,dn2" type="global"></table>
第二步:保证在两个数据节点上都有dict_order_type这个表,然后重启mycat
第三步:在mycat里向dict_order_type插入数据
INSERT INTO dict_order_type(id,order_type)VALUES(101,"type101");
INSERT INTO dict_order_type(id,order_type)VALUES(102,"type102");
第四步:验证,看看两个节点dn1和dn2上面是否都有数据,测试成功。
4. 分片规则(只要理解了,全是配置了)
上面的例子采用的是取模的分片规则。
4.1 分片枚举
/**
* defaultNode 默认节点:小于 0 表示不设置默认节点,大于等于 0 表示设置默认节点
* 默认节点的作用:枚举分片时,如果碰到不识别的枚举值,就让它路由到默认节点
* 如果不配置默认节点(defaultNode 值小于 0 表示不配置默认节点),碰到
* 不识别的枚举值就会报错,
* like this:can’t find datanode for sharding column:column_nameval:ffffffff
*/
# rule.xml
<tableRule name="sharding-by-intfile">
<rule>
<columns>user_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>
<property name="type">0</property>
<property name="defaultNode">0</property>
</function>
# partition-hash-int.txt 配置
10000=0
10010=1
DEFAULT_NODE=1
# 上面 columns 标识将要分片的表字段,algorithm 分片函数,
# 其中分片函数配置中,mapFile 标识配置文件名称,type 默认值为 0,0 表示 Integer,非零表示 String,
# 所有的节点配置都是从 0 开始,及 0 代表节点 1
本条规则类似于十进制的求模运算,区别在于是二进制的操作,是取 id 的二进制低 10 位,即 id 二进制 &1111111111。此算法的优点在于如果按照 10 进制取模运算,在连续插入 1-10 时候 1-10 会被分到 1-10 个分片,增 大了插入的事务控制难度,而此算法根据二进制则可能会分到连续的分片,减少插入事务事务控制难度。
此分片适用于,提前规划好分片字段某个范围属于哪个分片,
此规则为对分片字段求摸运算。
此规则为按天分片。
此种规则是取模运算与范围约束的结合,主要为了后续数据迁移做准备,即可以自主决定取模后数据的节点 分布。
此种规则类似于取模范围约束,此规则支持数据符号字母取模。
此规则是在运行阶段有应用自主决定路由到那个分片。
此规则是截取字符串中的 int 数值 hash 分片。
一致性 hash 预算有效解决了分布式数据的扩容问题。
此规则是单月内按照小时拆分,最小粒度是小时,可以一天最多 24 个分片,最少 1 个分片,一个月完后下月 从头开始循环。每个月月尾,需要手工清理数据。
先进行范围分片计算出分片组,组内再求模 优点可以避免扩容时的数据迁移,又可以一定程度上避免范围分片的热点问题 综合了范围分片和求模分片的优点,分片组内使用求模可以保证组内数据比较均匀,分片组之间是范围分片可以 兼顾范围查询。最好事先规划好分片的数量,数据扩容时按分片组扩容,则原有分片组的数据不需要迁移。由于分片组内数据比 较均匀,所以分片组内可以避免热点数据问题。
思想与范围求模一致,当由于日期在取模会有数据集中问题,所以改成 hash 方法。先根据日期分组,再根据时间 hash 使得短期内数据分布的更均匀 优点可以避免扩容时的数据迁移,又可以一定程度上避免范围分片的热点问题 要求日期格式尽量精确些,不然达不到局部均匀的目的。
根据日期查询日志数据 冷热数据分布 ,最近 n 个月的到实时交易库查询,超过 n 个月的按照 m 天分片。
按月份列分区 ,每个自然月一个分片,格式 between 操作解析的范例。
有状态分片算法与之前的分片算法不同,它是为数据自动迁移而设计的.
crc32solt 是有状态分片算法的实现之一,具体参考第六章 数据自动迁移方案设计
在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一。为此,MyCat 提供了全局 sequence,并且提供了包含本地配置和数据库配置等多种实现方式。
推荐使用这种方式。原理:在数据库中建立一张表,存放 sequence 名称(name),sequence 当前值(current_value),步长(increment int 类型每次读取多少个 sequence,假设为 K)等信息;
# 在dn1上
# – 创建存放 sequence 的表 DROP TABLE IF EXISTS MYCAT_SEQUENCE; – name sequence 名称 – current_value 当前 value – increment 增长步长! 可理解为 mycat 在数据库中一次读取多少个 sequence. 当这些用完后, 下次再从数 据库中读取。
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;
# 在dn1上
# – 获取当前 sequence 的值 (返回当前值,增量)
DROP FUNCTION IF EXISTS mycat_seq_currval;
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;
# – 设置 sequence 值
DROP FUNCTION IF EXISTS mycat_seq_setval;
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;
# – 获取下一个 sequence 值
DROP FUNCTION IF EXISTS mycat_seq_nextval;
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;
# 插一条数据
INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES ('orders', 400000,100);
# 注:sequnceHandlerType 需要配置为 1,表示使用数据库方式生成 sequence。
# 0:本地文件 1:数据库方式 2:时间戳 方式
<system><property name="sequnceHandlerType">1</property></system>
第五步:验证全局序列
# 进入mycat,插入数据
insert into orders(id,order_type,customer_id,amount) values (next value for MYCATSEQ_ORDERS,1000,101,102);
insert into orders(id,order_type,customer_id,amount) values (next value for MYCATSEQ_ORDERS,1000,101,102);
# 在mycat查询
select * from orders
目前 Mycat 对于中间件的连接控制并没有做太复杂的控制,目前只做了中间件逻辑库级别的读写权限控制。在server.xml 里配置的。
<user name="mycat">
<property name="password">mycat</property>
<property name="schemas">order</property>
<property name="readOnly">true</property>
</user>
<user name="mycat2">
<property name="password">mycat</property>
<property name="schemas">order</property>
</user>
# 配置说明:
# 配置中 name 是应用连接中间件逻辑库的用户名。
# mycat 中 password 是应用连接中间件逻辑库的密码。
# order 中是应用当前连接的逻辑库中所对应的逻辑表。schemas 中可以配置一个或多个。
# true 中 readOnly 是应用连接中间件逻辑库所具有的权限。true 为只读,false 为读写都有,默认为 false。
6.2 表的权限控制(privileges)
<user name="mycat">
<property name="password">mycat</property>
<property name="schemas">order</property>
<privileges check="true">
<schema name="TESTDB" dml="1111">
<table name="orders" dml="0000" ></table>
</schema>
</privileges >
</user>
6.3 SQL拦截-白名单
<firewall>
<whitehost>
<host host="121.4.237.121" user="mycat"/>
</whitehost>
</firewall>
6.4 SQL拦截-黑名单
<firewall>
<whitehost>
<host host="121.4.237.121" user="mycat"/>
</whitehost>
<blacklist check="true">
<property name="deleteAllow">false</property>
</blacklist>
</firewall>
四、Mycat监控工具
第一步:搭建安装Zookeeper
docker run --restart=always --privileged=true -d --name zookeeper -p 2181:2181 zookeeper:latest
第二步:搭建安装mycat-web
# 搜索mycat-web镜像
docker search mycat-web
# 拉取mycat-web镜像
docker pull itolwcw/mycat-web
# 运行mycat-web镜像
docker run --name mycat-web -d -p 8082:8082 --restart=always itolwcw/mycat-web
# 注: 这个镜像内部集成了zk,所以第一步暂无用
第三步:访问mycat_web,并配置mycat
http://120.55.65.202:8082/mycat
配置:
于是就可以查看你需要的信息:
五、Mycat高可用
HAproxy资料:
http://www.ttlsa.com/linux/haproxy-study-tutorial/
第一步:在两台机器上都部署mycat
第二步:在两台机器上都部署HAproxy
# 1.查镜像
docker search HAproxy
# 2.下载镜像
docker pull haproxy
# 3.创建文件夹
mkdir -p /root/haproxy
#4.编辑配置文件
global
daemon
chroot /usr/local/etc/haproxy
defaults
log 127.0.0.1 local0 err
mode http
retries 2
option redispatch
option abortonclose
option dontlognull
maxconn 4096
timeout connect 5000ms
timeout client 30000ms
timeout server 30000ms
listen admin_status
bind 0.0.0.0:8888
mode http
stats uri /mycat
stats realm Global\ statistics
stats auth admin:123456
listen proxy-mycat
bind 0.0.0.0:48066
mode tcp
# 负载均衡算法
# static-rr 权重, leastconn 最少连接, source 请求IP, 轮询 roundrobin
balance roundrobin
option tcplog
# 这要添加权限才能访问
# option mysql-check user haproxy
server MYCAT_1 121.4.237.121:8066 check weight 1 maxconn 2000
server MYCAT_2 120.55.65.202:8066 check weight 1 maxconn 2000
# 5. 启动
docker run -d --name haproxy -p 8888:8888 -p 48066:48066 -v /root/haproxy:/usr/local/etc/haproxy --restart=always haproxy
第三步:进入haproxy的监控页面,发现成功连接。
第四步:连接haproxy,验证mycat的负载均衡
# 登录
mysql -umycat -pmycat -h120.55.65.202 -P48066
# 或者利用navicat登录
以上是关于Mycat学习总结的主要内容,如果未能解决你的问题,请参考以下文章