Mycat学习总结

Posted 爱码人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mycat学习总结相关的知识,希望对你有一定的参考价值。

   
Mycat 是一个强大的数据库中间件,不仅仅可以用作读写分离、以及分表分库、容灾备份,而且可以用于多租户应用开发、云平台基础设施、让你的架构具备很强的适应性和灵活性,借助于即将发布的 Mycat 智能优化模块,系统的数据访问瓶颈和热点一目了然,根据这些统计分析数据,你可以自动或手工调整后端存储,将不同的表映射到不同存储引擎上,而整个应用的代码一行也不用改变。
Mycat资料:
http://www.mycat.org.cn/
https://github.com/MyCATApache/Mycat-Server
架构图:

为什么使用Mycat?

当Java服务直接连接mysql时,会出现java服务与数据库紧耦合,高访问量高并发对数据库的压力,读写请求数据不一致。

Mycat 目前有哪些功能与特性?
  • 支持 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 机制。


数据量规模多少如何选择数据库还是Hadoop?
当前是个大数据的时代,但究竟怎样规模的数据适合数据库系统呢?对此,国外有一个数据库领域的权威人 士说了一个结论: 千亿以下的数据规模仍然是数据库领域的专长,而 Hadoop 等这种系统,更适合的是千亿以上 的规模。 所以,Mycat 适合 1000 亿条以下的单表规模,如果你的数据超过了这个规模,请投靠 Mycat Plus 吧!
应用场景:
  •  单纯的读写分离,此时配置最为简单,支持读写分离,主从切换;

  • 分表分库,对于超过 1000 万的表进行分片,最大支持 1000 亿的单表分片;

  • 多租户应用,每个应用一个库,但应用程序只连接 Mycat,从而不改造程序本身,实现多租户化;

  • 报表系统,借助于 Mycat 的分表能力,处理大规模报表的统计;

  • 替代 Hbase,分析大数据;

  • 作为海量数据实时查询的一种简单有效方案,比如 100 亿条频繁查询的记录需要在 3 秒内查询出来结果,

  • 除了基于主键的查询,还可能存在范围查询或其他属性查询,此时 Mycat 可能是最简单有效的选择。

Mycat的作用?
读写分离

Mycat学习总结

数据分片: 垂直拆分(分库)、水平拆分(分表)、垂直+水平拆分(分库分表)

Mycat学习总结

Mysql日志类型:
重做日志(redo log):确保事务的持久性。redo日志记录事务执行后的状态,用来恢复未写入data file的已成功事务更新的数据。防止在发生故障的时间点,尚有脏页未写入磁盘,在重启mysql服务的时候,根据redo log进行重做,从而达到事务的持久性这一特性。
回滚日志(undo log):保证数据的原子性,保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读
二进制日志(binlog):用于复制,在主从复制中,从库利用主库上的binlog进行重播,实现主从同步;用于数据库的基于时间点的还原。
错误日志(errorlog):错误日志记录着mysqld启动和停止,以及服务器在运行过程中发生的错误的相关信息。在默认情况下,系统记录错误日志的功能是关闭的,错误信息被输出到标准错误输出。
慢查询日志(slow query log): 慢日志记录执行时间过长和没有使用索引的查询语句,报错select、update、delete以及insert语句,慢日志只会记录执行成功的语句。
一般查询日志(general log): 记录了服务器接收到的每一个查询或是命令,无论这些查询或是命令是否正确甚至是否包含语法错误,general log 都会将其记录下来 ,记录的格式为 {Time ,Id ,Command,Argument }。也正因为mysql服务器需要不断地记录日志,开启General log会产生不小的系统开销。因此,Mysql默认是把General log关闭的。
中继日志(relay log): MySQL 中继日志(relay log)只存在于主从复制结构中的从节点上,用于保存主节点传输过来的数据变更事件,然后将这些事件应用于从节点。

DDL 日志:MySQL DDL 日志,也称为元数据日志(metadata log)记录了数据定义语言执行的元数据操作,例如 DROP TABLE 或者 ALTER TABLE。

如果元数据操作的过程中发生故障,MySQL 使用该日志进行恢复。

多数据源整合

Mycat学习总结

Mycat原理:
Mycat的原理中最重要的一个动词是“拦截”,它拦截了用户发送过来的SQL语句,首先对SQL语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此SQL发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。
一、Mycat中的概念
  1. 数据库中间件:Mycat 是一个开源的分布式数据库系统,但是由于真正的数据库需要存储引擎,而 Mycat 并没有存储引擎,所以并不是完全意义的分布式数据库系统。那么 Mycat 是什么?Mycat 是数据库中间件,就是介于数据库与应用之间,进行数据处理与交互的中间服务。由于前面讲的对数据进行分片处理之后,从原有的一个库,被切分为多个分片数据库,所有的分片数据库集群构成了整个完整的数据库存储。数据被分到多个分片数据库后,应用如果需要读取数据,就要需要处理多个数据源的数据。如果没有数据库中间件,那么应用将直接面对分片集群,数据源切换、事务处理、数据聚合都需要应用直接处理,原本该是专注于业务的应用,将会花大量的工作来处理分片后的问题,最重要的是每个应用处理将是完全的重复造轮子。所以有了数据库中间件,应用只需要集中与业务处理,大量的通用的数据聚合,事务,数据源切换都由中间件来处理,中间件的性能与处理能力将直接决定应用的读写性能,所以一款好的数据库中间件至关重要。

  2. 逻辑库(schema):通常对实际应用来说,并不需要知道中间件的存在,业务开发人员只需要知道数据库的概念,所以数据库中间件可以被看做是一个或多个数据库集群构成的逻辑库。在云计算时代,数据库中间件可以以多租户的形式给一个或多个应用提供服务,每个应用访问的可能是一个独立或者是共享的物理库,常见的如阿里云数据库服务器 RDS。

Mycat学习总结

3. 逻辑表(table)
3.1  逻辑表:既然有逻辑库,那么就会有逻辑表,分布式数据库中,对应用来说,读写数据的表就是逻辑表。逻辑表,可以是数据切分后,分布在一个或多个分片库中,也可以不做数据切分,不分片,只有一个表构成。

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安装

docker-mycat for MySQL 5.x and 8.0。支持连接 MySQL 8.0 的 Mycat 数据库中间件 Docker 容器。一键制作 Docker,一键启动 Docker,非常方便。
三个配置文件:
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学习总结

Mycat学习总结

Mycat学习总结

配置方法:

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学习总结

Mycat 数据节点、数据库、分库分表配置:打开配置文件 config/mycat/schema.xml

vim config/mycat/schema.xml# 详细配置建议参考官方配置说明https://github.com/MyCATApache/Mycat-Server

列子:

<?xml version="1.0"?><!DOCTYPE mycat:schema SYSTEM "schema.dtd"><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&amp;serverTimezone=UTC&amp;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&amp;serverTimezone=UTC&amp;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&amp;serverTimezone=UTC&amp;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&amp;serverTimezone=UTC&amp;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&amp;serverTimezone=UTC&amp;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&amp;serverTimezone=UTC&amp;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 来说,标准的读写分离是主从模式,一个写节点 Master 后面跟着多个读节点,读节点的数量取决于系统的压力,通常是 1-3 个读节点的配置。 Mysql主从复制原理:
  • MySQL支持单向、异步复制,复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器。

  • MySQL复制是基于主服务器在二进制日志中跟踪所有对数据库的更改。因此,要进行复制,必须在主服务器上启用二进制日志。每个从服务器从主服务器接收主服务器已经记录到日志的数据。

  • 当一个从服务器连接主服务器时,它通知主服务器从服务器在日志中读取的最后一次成功更新的位置。从服务器接收从那时起发生的任何更新,并在本机上执行相同的更新。然后封锁并等待主服务器通知新的更新。从服务器执行备份不会干扰主服务器,在备份过程中主服务器可以继续处理更新。

Mycat学习总结

Binary log:主数据库的二进制日志。

Relay log:从服务器的中继日志。


第一步:master在每个事务更新数据完成之前,将该操作记录串行地写入到binlog文件中。

第二步:salve开启一个I/O Thread,该线程在master打开一个普通连接,主要工作是binlog dump process。如果读取的进度已经跟上了master,就进入睡眠状态并等待master产生新的事件。I/O线程最终的目的是将这些事件写入到中继日志中。

第三步:SQL Thread会读取中继日志,并顺序执行该日志中的SQL事件,从而与主数据库中的数据保持一致。

什么是主从复制?
将主数据库中的DDL和DML操作通过二进制日志传输到从数据库上,然后将这些日志重新执行(重做);从而使得从数据库的数据与主数据库保持一致。
主从复制的作用?
  • 主数据库出现问题,可以切换到从数据库。

  • 可以进行数据库层面的读写分离。

  • 可以在从数据库上进行日常备份。

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


Mycat学习总结

Mycat学习总结

第一步:主库配置:修改配置文件mysqld.cnf

# 主服务器唯一IDserver-id=121# 启用二进制日志log-bin=mysql-bin
# 设置不需要复制额数据库 (可设置多个)binlog-ignore-db=mysqlbinlog-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开销在容忍的范围内即可)。 

Mycat学习总结

第二步:从库配置:修改配置文件mysqld.cnf

# 从服务器唯一IDserver-id=202# 启用中继日志relay-log=mysql-relay

Mycat学习总结

第三步:主机从机重启mysql服务。

第四步:主机从机关闭防火墙或者修改防火墙保证主从互通,这步需要注意,如果连不上数据库,需要检查防火墙和安全组是否给了权限。

第五步:在主机上建立账户,并授权slave

# 在主机mysql 里执行授权命令 GRANT REPLICATION SLAVE ON *.* TO 'slave'@'120.55.65.202' IDENTIFIED BY '123520';# 查询master的状态show master status\G;

Mycat学习总结


第六步:在从机上配制需要复制的主机

# 登进去从服务器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

Mycat学习总结

Mycat学习总结

搭建完毕!!!!!!!!!

测试:

在主库里(121)创建一个刚才设置好的数据库testdb:

Mycat学习总结

去从服务器(202)上看看是否有testdb数据库:

Mycat学习总结

可以看出来从库已经有了数据库testdb。再在主库上创建一张表user,从库上也有表user,在主库添加数据,从库也会有数据:

Mycat学习总结

刷新一下从数据库,发现已经有表了:

Mycat学习总结

Mycat学习总结

Mycat学习总结

一主一从的主从复制搭建完毕!!!!!!!!!!!!!!!

一主一从的读写分离:

第一步:配置好一主一从的主从复制,保证主从复制的功能正常。

第二步:部署好mycat的服务,修改配置文件schema.xml:

<?xml version="1.0"?><!DOCTYPE mycat:schema SYSTEM "schema.dtd"><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&amp;serverTimezone=UTC&amp;characterEncoding=utf8" user="root" password="root"> <readHost host="120.55.65.202" url="jdbc:mysql://120.55.65.202:3306?useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=utf8" user="root" password="root" /> </writeHost> </dataHost>
</mycat:schema>
# 注: 为了测试,我把balance设置了2,在企业实战中把balance设置1或者3

修改server.xml文件:

Mycat学习总结

重新启动mycat服务。测试连接:

Mycat学习总结


验证读写分离:binlog日志当sql语句有函数、或系统变量等就会造成主从复制的数据不一样。

Mycat学习总结


# 在主机主库里添加一条数据:INSERT INTO `user` VALUES(3,@@hostname,16)# 主从库的数据不一致了# 在mycat里查询select * from user

Mycat学习总结

在mycat上多查几次,发现:

Mycat学习总结

Mycat学习总结

此时,我想起了nginx的负载均衡了。再将配置文件中的balance的值改为3,即所有读请求随机的分发到wiriterHost对应的readhost执行,writerHost不负担读压力。重启mycat,猜测:在mycat上无论查询多少次,都是到从库e3082667be60:

Mycat学习总结

猜想正确,读写分离实现了。

1.2 双主双从(正式环境的话,最好是一台服务器安装一个mysql)

一个主机m1用于处理所有的写请求,它的从机是s1和另一台主机的m2还有它的从机上s2负责所有的读请求。当m1主机宕机后,m2主机负责写请求,m1和m2互为备机。

Mycat学习总结

准备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.7docker 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.7docker 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

Mycat学习总结

Mycat学习总结

第二步:双主机配置:

修改m1配置文件:

# 主服务器唯一IDserver-id=1# 启用二进制日志log-bin=mysql-bin# 设置不需要复制额数据库 (可设置多个)binlog-ignore-db=mysqlbinlog-ignore-db=information_schema# 设置需要复制的数据库 需要复制的主数据库名binlog-do-db=需要复制的主数据库名# 设置logbin格式binlog_format=STATEMENT# 在作为从数据库的时候,有写入操作也要更新二进制日志文件log-slave-updates# 表示自增长字段 每次递增的量,其默认值为1,范围1...65535auto-increment-increment=2# 表示自增长字段从哪个数开始,取值范围1...65535auto-increment-offset=1

修改m2的配置文件:

# 主服务器唯一IDserver-id=3# 启用二进制日志log-bin=mysql-bin# 设置不需要复制额数据库 (可设置多个)binlog-ignore-db=mysqlbinlog-ignore-db=information_schema# 设置需要复制的数据库 需要复制的主数据库名binlog-do-db=需要复制的主数据库名# 设置logbin格式binlog_format=STATEMENT# 在作为从数据库的时候,有写入操作也要更新二进制日志文件log-slave-updates# 表示自增长字段 每次递增的量,其默认值为1,范围1...65535auto-increment-increment=2# 表示自增长字段从哪个数开始,取值范围1...65535auto-increment-offset=2

修改s1配置文件:

# 从服务器唯一IDserver-id=2# 启用中继日志relay-log=mysql-relay

修改s2配置文件:

# 从服务器唯一IDserver-id=4# 启用中继日志relay-log=mysql-relay

第三步:双主机,双从机重启mysql服务

第四步:保证机器之间互通,关闭防火墙或者设置防火墙

第五步:在两台主机上建立账户,并授权slave

# 进去容器内 docker exec -it m1 bash# 进去mysqlmysql -uroot -proot
# 在主机mysql 里执行授权命令GRANT REPLICATION SLAVE ON *.* TO 'slave'@'%' IDENTIFIED BY '123520';# 查询master的状态show master status;

注:由于刚才在服务器上搭建了一主一从的主从复制,所以先关闭一下:

# 这些命令需要进入数据库# 在从服务器上执行stop slave;# 在从主服务器上执行reset master;

Mycat学习总结

Mycat学习总结


第六步:在分别在从机上配制各自对应的需要复制的主机:

# 停止slavestop slave;# s1CHANGE MASTER TO MASTER_HOST='121.4.237.121',MASTER_USER='slave',MASTER_PASSWORD='123520',MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=443;# s2CHANGE MASTER TO MASTER_HOST='120.55.65.202',MASTER_USER='slave',MASTER_PASSWORD='123520',MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=443;
# 开启slavestart slave;# 查看slaveshow slave status\G

Mycat学习总结

Mycat学习总结

Mycat学习总结

小插曲:S2没有同步成功,看一下日志:

Mycat学习总结

从日志看,是没有找对主机的二进制文件,那是因为我一台服务器上部署了2个mysql,以不同的端口号进行区分的,我在配置从机的时候,没有加主机的端口号,所以找不到二进制文件,细心呀!!!

Mycat学习总结

# s1CHANGE 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;# s2CHANGE 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;
# 开启slavestart slave;# 查看slaveshow 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;
# 开启slavestart slave;# 查看slaveshow slave status\G


Mycat学习总结

Mycat学习总结

到此,双主双从的主从复制搭建完毕。测试一下:在m2上创建数据库hugedb,然后创建一个表user,看看其他三个数据库上有没有:

Mycat学习总结

Mycat学习总结

四台机器上的数据都是一样的,测试成功!!!!!!!!!上面的双主双从的主从复制搭建完毕,接着要搭建双主双从的读写分离:

第八步:修改mycat配置文件schema.xml文件:

注:balance="1"switchType="1"  //默认值,表示自动切换

<?xml version="1.0"?>
<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&amp;serverTimezone=UTC&amp;characterEncoding=utf8" user="root" password="root"> <readHost host="120.55.65.202" url="jdbc:mysql://120.55.65.202:3306?useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=utf8" user="root" password="root" /> </writeHost> <writeHost host="120.55.65.202" url="jdbc:mysql://120.55.65.202:3307?useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=utf8" user="root" password="root"> <readHost host="121.4.237.121" url="jdbc:mysql://121.4.237.121:3307?useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=utf8" user="root" password="root" /> </writeHost>
</dataHost>
</mycat:schema>

第九步:启动mycat

docker-compose up -d


Mycat学习总结

Mycat学习总结

第十步:验证双主双从的读写分离

在m1主机上数据库表中插入带系统变量的数据,造成主从数据不一样

INSERT INTO user VALUES (2,@@hostname,99)

Mycat学习总结


Mycat学习总结

Mycat学习总结

Mycat学习总结

查看一下mycat,多刷新几次:

Mycat学习总结

Mycat学习总结


2.垂直分库实战

一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是 指按照业务将表进行分类,分布到不同的数据库上面,这样也就将数据或者说压力分担到不同的库上面,如下图:系统被切分成了,用户,订单交易,支付几个模块。

Mycat学习总结

其实现在的微服务架构也能解决这样的场景。
一个架构设计较好的应用系统,其总体功能肯定是由很多个功能模块所组成的,而每一个功能模块所需要的数据对应到数据库中就是一个或者多个表。而在架构设计中,各个功能模块相互之间的交互点越统一越少,系统的耦合度就越低,系统各个模块的维护性以及扩展性也就越好。这样的系统,实现数据的垂直切分也就越容易。但是往往系统之有些表难以做到完全的独立,存在这扩库 join 的情况,对于这类的表,就需要去做平衡,是数据库让步业务,共用一个数据源,还是分成多个库,业务之间通过接口来做调用。在系统初期,数据量比较少,或者资源有限的情况下,会选择共用数据源,但是当数据发展到了一定的规模,负载很大的情况,就需要必须去做分割。一般来讲业务存在着复杂 join 的场景是难以切分的,往往业务独立的易于切分。如何切分,切分到何种程度是考验技术架构的一个难题。

优点:

  • 拆分后业务清晰,拆分规则明确;

  • 系统之间整合或扩展容易;

  • 数据维护简单。

缺点:

  • 部分业务表无法 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配置文件

<?xml version="1.0"?><!DOCTYPE mycat:schema SYSTEM "schema.dtd"><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&amp;serverTimezone=UTC&amp;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&amp;serverTimezone=UTC&amp;characterEncoding=utf8" user="root" password="root"> </writeHost>        </dataHost></mycat:schema>

第二步:启动dn1,dn2两个数据库,并在两个数据库创建orders数据库

第三步:重启mycat数据库,并在mycat上面创建表

Mycat学习总结

第四步:验证。查看在mycat上面创建的表是否按照配置的,分到对应的实际数据库里。

Mycat学习总结

Mycat学习总结

Mycat学习总结

从上面测试来看,已经实现了分库的操作,在mycat里可以查出四张表,在dn1里可以查出三张表,dn2可以查出一张表。

3.水平分表实战

相对于垂直拆分,水平拆分不是将表做分类,而是 按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中,如图:

Mycat学习总结


拆分数据就需要定义分片规则。关系型数据库是行列的二维模型,拆分的第一原则是找到拆分维度。比如:从会员的角度来分析,商户订单交易类系统中查询会员某天某月某个订单,那么就需要按照会员结合日期来拆分,不同的数据按照会员 ID 做分组,这样所有的数据查询 join 都会在单库内解决;如果从商户的角度来讲,要查询某个商家某天所有的订单数,就需要按照商户 ID 做拆分;但是如果系统既想按会员拆分,又想按商家数据,则会有一定的困难。如何找到合适的分片规则需要综合考虑衡量。 数据冗余,表分组(Table Group),这都是业务上规 避跨库 join 的很好的方式。

几种典型的分片规则包括:

  • 按照用户 ID 求模,将数据分散到不同的数据库,具有相同数据用户的数据都被分散到一个库中;

  • 按照日期,将不同月甚至日的数据分散到不同的库中;

  • 按照某个特定的字段求摸,或者根据特定范围段分散到不同的库中。

如图,切分原则都是根据业务找到适合的切分规则分散到不同的库,下面用用户 ID 求模举例:

Mycat学习总结

优点:

  • 拆分规则抽象好,join 操作基本可以数据库做;

  • 不存在单库大数据,高并发的性能瓶颈;

  • 应用端改造较少;

  • 提高了系统的稳定性跟负载能力。

缺点:

  • 拆分规则难以抽象;

  • 分片事务一致性难以解决;

  • 数据多次扩展难度跟维护量极大;

  • 跨库 join 性能较差。

比较垂直切分跟水平切分的不同跟优缺点,会发现每种切分方式都有缺点,但共同的特点缺点有:

  • 引入分布式事务的问题;

  • 跨节点 Join 的问题;

  • 跨节点合并排序分页问题;

  • 多数据源管理问题。

针对数据源管理,目前主要有两种思路:

  1. 客户端模式,在每个应用程序模块中配置管理自己需要的一个(或者多个)数据源,直接访问各个数据库, 在模块内完成数据的整合;

  2. 通过中间代理层来统一管理所有的数据源,后端数据库集群对前端应用程序透明;

对上面这两种解决思路的时候都会倾向于选择第二种,尤其是系统不断变得庞大复杂的时候。确实,这是一个非常正确的选择,虽然短期内需要付出的成本可能会相对更大一些,但是对整个系统的扩展性来说,是非常有帮助的。Mycat 通过数据切分解决传统数据库的缺陷,又有了 NoSQL 易于扩展的优点。通过中间代理层规避了多数 据源的处理问题,对应用完全透明,同时对数据切分后存在的问题,也做了解决方案。由于数据切分后数据 Join 的难度在此也分享一下数据切分的经验:

第一原则:能不切分尽量不要切分。

第二原则:如果要切分一定要选择合适的切分规则,提前规划好。

第三原则:数据切分尽量通过数据冗余或表分组(Table Group)来降低跨库 Join 的可能。

第四原则:由于数据库中间件对数据 Join 实现的优劣难以把握,而且实现高性能难度极大,业务读取尽量少使用多表 Join。


3.1 水平分表

Mysql单表存储数据是有瓶颈的,单表到1000万条数据就到达了瓶颈,会影响查询效率,需要进行水平分表进行优化。
当你没有任何字段可以作为分片字段的时候, 主键分片就是唯一选择,其优点是按照主键的查询最快,当采 用自动增长的序列号作为主键时,还能比较均匀的将数据分片在不同的节点上。若有某个合适的业务字段比较合适作为分片字段,则建议采用此业务字段分片,选择分片字段的条件如下:
  • 尽可能的比较均匀分布数据到各个节点上;

  • 该业务字段是最频繁的或者最重要的查询条件。

例如,以订单表为例:以客户id( customer_id)去划分。在工作中,不同的业务和表要具体分析哦。
第一步:修改配置文件schema.xml:
# 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>

Mycat学习总结

第二步:修改配置文件rule.xml
<?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="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>

Mycat学习总结

分片规则可以参考:

https://blog.csdn.net/feiying0canglang/article/details/105323654

第三步:保证2个节点dn1和dn2上都有orders这个表,没有的要提前创建好,然后重启mycat。

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学习总结

第五步:测试验证,在mycat上面查看应该是6条:

Mycat学习总结


dn1上:

Mycat学习总结

dn2上:

Mycat学习总结


测试符合预期目标,将数据库的数据分在了不同机器上的不同数据里,并且是根据客户ID去分的。

3.2 水平分表ER表

有一类业务,例如订单(orders)跟订单明细(detail),明细表会依赖于订单,也就是说会存在表的主 从关系,这类似业务的切分可以抽象出合适的切分规则,比如根据用户 ID 切分,其他相关的表都依赖于用户 ID, 再或者根据订单 ID 切分,总之部分业务总会可以抽象出父子关系的表。这类表适用于 ER 分片表,子表的记录与 所关联的父表记录存放在同一个数据分片上,避免数据 Join 跨库操作。 detail表里的order_id和orders表里的id是关联的:目标就是订单表分片了,它的关联从表订单明细也要和对应的订单表一样分片。
第一步:修改配置文件schema.xml。
<table name="orders" dataNode="dn1,dn2" rule="mod_rule" >     <childTable name="detail" primaryKey="id" joinKey="order_id" parentKey="id"/></table>
第二步:在两台分片主机库里都必须有 detail表,没有的要提前创建好,然后重启mycat。
第三步:访问mycat向 order_details表里插入数据。
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);

这样可能会报错:

Mycat学习总结

这是个大坑:网上给的解决办法:

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里重新添加数据,最终添加子表的数据不会报错了。

Mycat学习总结

测试一下,符合测试预期要求。

select o.*,od.detail from orders o inner join order_details od on o.id=od.order_id

Mycat学习总结

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上面是否都有数据,测试成功。

Mycat学习总结

Mycat学习总结

Mycat学习总结

4. 分片规则(只要理解了,全是配置了)

上面的例子采用的是取模的分片规则。   

4.1 分片枚举

通过在配置文件中配置可能的枚举 id,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照省 份或区县来做保存,而全国省份区县固定的,这类业务使用本条规则,配置如下:
/*** 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=010010=1DEFAULT_NODE=1# 上面 columns 标识将要分片的表字段,algorithm 分片函数,# 其中分片函数配置中,mapFile 标识配置文件名称,type 默认值为 0,0 表示 Integer,非零表示 String,# 所有的节点配置都是从 0 开始,及 0 代表节点 1
4.2 固定分片 hash 算法

本条规则类似于十进制的求模运算,区别在于是二进制的操作,是取 id 的二进制低 10 位,即 id 二进制 &1111111111。此算法的优点在于如果按照 10 进制取模运算,在连续插入 1-10 时候 1-10 会被分到 1-10 个分片,增 大了插入的事务控制难度,而此算法根据二进制则可能会分到连续的分片,减少插入事务事务控制难度。

4.3 范围约定

此分片适用于,提前规划好分片字段某个范围属于哪个分片, 

start <= range <= end. 
range start-end ,data no de index
K=1000,M=10000.
4.4 取 模

此规则为对分片字段求摸运算。

4.5 按日期(天)分片

此规则为按天分片。

4.6 取模范围约束

此种规则是取模运算与范围约束的结合,主要为了后续数据迁移做准备,即可以自主决定取模后数据的节点 分布。

4.7 截取数字做 hash 求模范围约束

此种规则类似于取模范围约束,此规则支持数据符号字母取模。

4.8 应用指定

此规则是在运行阶段有应用自主决定路由到那个分片。

4.9 截取数字 hash 解析

此规则是截取字符串中的 int 数值 hash 分片。

4.10 一致性 hash

一致性 hash 预算有效解决了分布式数据的扩容问题。

4.11 按单月小时拆分

此规则是单月内按照小时拆分,最小粒度是小时,可以一天最多 24 个分片,最少 1 个分片,一个月完后下月 从头开始循环。每个月月尾,需要手工清理数据。

4.12 范围求模分片

先进行范围分片计算出分片组,组内再求模 优点可以避免扩容时的数据迁移,又可以一定程度上避免范围分片的热点问题 综合了范围分片和求模分片的优点,分片组内使用求模可以保证组内数据比较均匀,分片组之间是范围分片可以 兼顾范围查询。最好事先规划好分片的数量,数据扩容时按分片组扩容,则原有分片组的数据不需要迁移。由于分片组内数据比 较均匀,所以分片组内可以避免热点数据问题。

4.13 日期范围 hash 分片

思想与范围求模一致,当由于日期在取模会有数据集中问题,所以改成 hash 方法。先根据日期分组,再根据时间 hash 使得短期内数据分布的更均匀 优点可以避免扩容时的数据迁移,又可以一定程度上避免范围分片的热点问题 要求日期格式尽量精确些,不然达不到局部均匀的目的。

4.14 冷热数据分片

根据日期查询日志数据 冷热数据分布 ,最近 n 个月的到实时交易库查询,超过 n 个月的按照 m 天分片。

4.15 自然月分片

按月份列分区 ,每个自然月一个分片,格式 between 操作解析的范例。

4.16 有状态分片算法

有状态分片算法与之前的分片算法不同,它是为数据自动迁移而设计的.

4.17 crc32slot 分片算法

crc32solt 是有状态分片算法的实现之一,具体参考第六章 数据自动迁移方案设计

以上的规则,在项目上需要的话可以去官网看一下配置方式就行,这里就不一一测试了。
5.分表全局序列号

在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一。为此,MyCat 提供了全局 sequence,并且提供了包含本地配置和数据库配置等多种实现方式。

5.1 本地文件方式
      优点: 本地加载,读取速度较快
      缺点:抗风险能力差,当 MyCAT 重新发布后,配置文件中的 sequence 会恢复到初始值。
5.2 数据库方式

   推荐使用这种方式。原理:在数据库中建立一张表,存放 sequence 名称(name),sequence 当前值(current_value),步长(increment int 类型每次读取多少个 sequence,假设为 K)等信息;

第一步:创建 MYCAT_SEQUENCE 表
# 在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 NOTNULL,increment INT NOT NULL DEFAULT 100, PRIMARY KEY(name)) ENGINE=InnoDB;
第二步:创建相关 function
# 在dn1上# – 获取当前 sequence 的值 (返回当前值,增量)DROP FUNCTION IF EXISTS mycat_seq_currval;DELIMITER $$CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS varchar(64)DETERMINISTICBEGINDECLARE 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)DETERMINISTICBEGINUPDATE MYCAT_SEQUENCESET current_value = valueWHERE 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)DETERMINISTICBEGINUPDATE MYCAT_SEQUENCESET 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);
第三步:sequence_db_conf.properties 相关配置,指定 sequence 相关配置在哪个节点上
如:USER_SEQ=test_dn1
注意:MYCAT_SEQUENCE 表和以上的 3 个 function,需要放在同一个节点上。function 请直接在具体节 点的数据库上执行,如果执行的时候报:you might want to use the less safe log_bin_trust_function_creators variable 需要对数据库做如下设置:windows 下 my.ini[mysqld]加上log_bin_trust_function_creators=1 linux 下/etc/my.cnf 下 my.ini[mysqld]加上 log_bin_trust_function_creators=1 修改完后,即可在 mysql 数据库中执行上面的函数。
使用示例:insert into table1(id,name) values(next value for MYCATSEQ_ORDERS,‘test’);

Mycat学习总结

第四步:配置方式:server.xml 配置:
# 注:sequnceHandlerType 需要配置为 1,表示使用数据库方式生成 sequence。# 0:本地文件 1:数据库方式 2:时间戳 方式<system><property name="sequnceHandlerType">1</property></system>

Mycat学习总结

第五步:验证全局序列

# 进入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学习总结

5.3 本地时间戳方式
      优点:配置简单
      缺点:18位ID过长
5.4 分布式 ZK ID 生成器
5.5 Zk 递增方式
5.6 其他方式
5.7 自增长主键

6. Mycat安全权限

目前 Mycat 对于中间件的连接控制并没有做太复杂的控制,目前只做了中间件逻辑库级别的读写权限控制。在server.xml 里配置的。

6.1 user权限控制
<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标签下的privileges标签可以对逻辑库(schema),表(table)进行精细化的DML权限控制。 p rivileges 标签下的check, true开启权限检查,false不开启,默认为false。
<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>
权限对应表:

Mycat学习总结

6.3 SQL拦截-白名单

firewall标签用来定义防火墙,firewall标签下whitehost标签用来定义IP白名单。在server.xml里配置。
<firewall> <whitehost> <host host="121.4.237.121" user="mycat"/> </whitehost></firewall>

6.4 SQL拦截-黑名单

firewall标签下blacklist用来定义SQL的黑名单。可以通过设置黑名单,实现mycat对具体的SQL操作拦截,如增删改查的拦截。在server.xml里配置。
<firewall> <whitehost> <host host="121.4.237.121" user="mycat"/> </whitehost> <blacklist check="true"> <property name="deleteAllow">false</property> </blacklist></firewall>

Mycat学习总结

Mycat学习总结

Mycat学习总结


四、Mycat监控工具

 Mycat-web 是 Mycat 可视化运维的管理和监控平台,弥补了 Mycat 在监控上的空白。帮 Mycat 分担统计任务和配置管理任务。Mycat-web 引入了 ZooKeeper 作为配置中心,可以管理多个节点。Mycat-web 主要管理和监控 Mycat 的流量、连接、活动线程和内存等,具备 IP 白名单、邮件告警等模块,还可以统计SQL 并分析慢 SQL 和高频 SQL 等。为优化 SQL 提供依据。

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学习总结

配置:

Mycat学习总结

于是就可以查看你需要的信息:

Mycat学习总结


五、Mycat高可用

HAproxy资料:

http://www.ttlsa.com/linux/haproxy-study-tutorial/

Mycat学习总结

可以使用HAproxy+Keepalived配合两台Mycat搭起Mycat集群,实现高可用性。HAproxy实现了Mycat多节点的集群高可用和负载均衡,而HAproxy自身的高可用则可以通过Keepalived来实现。由于前面部署过keepalived,这里就先不部署了,原理都一样。这里主要部署一下HAproxy:

第一步:在两台机器上都部署mycat

Mycat学习总结

Mycat学习总结

第二步:在两台机器上都部署HAproxy

# 1.查镜像docker search HAproxy# 2.下载镜像docker pull haproxy# 3.创建文件夹mkdir -p /root/haproxy#4.编辑配置文件
global daemon chroot /usr/local/etc/haproxydefaults 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 30000mslisten admin_status bind 0.0.0.0:8888 mode http stats uri /mycat stats realm Global\ statistics stats auth admin:123456listen 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

Mycat学习总结

第三步:进入haproxy的监控页面,发现成功连接。

Mycat学习总结


第四步:连接haproxy,验证mycat的负载均衡

# 登录mysql -umycat -pmycat -h120.55.65.202 -P48066 # 或者利用navicat登录


以上是关于Mycat学习总结的主要内容,如果未能解决你的问题,请参考以下文章

mycat学习

mycat学习01-- mycat我带你入门

Python学习总结

《MyCat 学习笔记》第六篇.数据分片 之 按月数据分片

线程学习知识点总结

Mycat 读写分离+分库分表