mycat的介绍及使用

mycat介绍

  • 什么是mycat

    mycat是Java语言开发的目前比较主流的数据库中间件之一,是mycat团队在阿里开源的 Cobar 产品基础上而研发的,也一直在维护。mycat实现了 MySQL 协议的服务器,前端用户可以把它看作是一个数据库代理,用 MySQL 客户端工具和命令行访问,而其后端可以用 MySQL 原生协议与多个 MySQL 服务器通信,也可以用 JDBC 协议与大多数主流数据库服务器通信。

    mycat不单只可以做MySQL的代理,它的后端可以支持MySQL、SQL Server、Oracle、DB2、PostgreSQL 等主流数据库,也支持 MongoDB 这种新型NoSQL 方式的存储,未来还会支持更多类型的存储。

  • mycat的实现原理

    mycat主要是通过对SQL的拦截,然后经过一定规则的分片解析、路由分析、读写分离分析、缓存分析等,然后将SQL发给后端真实的数据块,并将返回的结果做适当处理返回给客户端。

  • mycat的应用场景

    • 数据库读写分离,此时配置最为简单,支持读写分离,主从切换
    • 数据库分库分表,这也是mycat的核心功能之一,可以解决数据库分库分表之后的数据插入和查询问题
    • 多租户应用,每个应用一个数据库,但程序只需连接mycat,程序不改变,实现多租户化
    • 海量数据的存储及实时查询,使用mycat是一种简单有效的方案
    • 替代Hbase,分析大数据


mycat安装

官网地址:http://www.mycat.org.cn/

安装包下载地址:http://dl.mycat.org.cn/

mycat有windows版和Linux版,这里只说Linux的安装

PS:使用mycat前必须安装Java的JDK环境,so easy,这里不讲了,一定记得提前安装!!!

wget http://dl.mycat.org.cn/1.6.7.6/20210730131311/Mycat-server-1.6.7.6-release-20210730131311-linux.tar.gz
tar -C /usr/local -zxvf Mycat-server-1.6.7.6-release-20210730131311-linux.tar.gz
cd /usr/local/mycat/
#./bin/mycat { console | start | stop | restart | status | dump }
./bin/mycat start

连接测试(mycat默认端口是8066,默认密码是123456)

root@f15fc642fedf:/# mysql -uroot -p123456 -P8066 -h192.168.241.140
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.6.29-mycat-1.6.7.6-release-20210730131311 MyCat Server (OpenCloudDB)

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

mycat核心配置

mycat的配置文件在 conf 文件夹下,最核心的配置文件有三个:schema.xml、server.xml、rule.xml

一、schema.xml

该配置文件主要是用于配置逻辑库、逻辑表等相关信息

  1. schema标签

    定义mycat实例中的逻辑库,mycat可以有多个逻辑库,每个逻辑库都有自己的相关配置。可以使用schema标签来划分这些不同的逻辑库。如果不配置schema标签,所有表的配置会属于同一个默认的逻辑库。

    逻辑库的概念和MySQL的database的概念一样,在查询两个不同逻辑库中的表的时候,需要切换到该逻辑库下进行查询。示例:

<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1"></schema>
  • name:定义逻辑库名,必须唯一不能重复
  • checkSQLschema:检查发给mycat的SQL是否含有库名。当该值为true时,例如执行语句 select * from TESTDB.company mycat会把语句修改为 select * from company 去掉库名 TESTDB
  • sqlMaxLimit:限制返回结果集的行数。该值设置为某个数值时,每条执行的sql语句,如果没有加上limit参数,mycat会自动加上这个属性的值。不写的话,默认返回所有的值。需要注意的是,如果运行的schema为非拆分库的,那么该属性不会生效。需要自己sql语句加limit。
  • randomDataNode:这是新版mycat新加入的属性,定义将一些随机语句发送到该数据节点中


2. table 标签

定义mycat中的逻辑表,所有需要拆分的表都需要在这个标签中定义。示例:

<table name="customer" primaryKey="id" dataNode="dn1,dn2" rule="sharding-by-intfile" autoIncrement="true" fetchStoreNodeByJdbc="true">
 <childTable name="customer_addr" primaryKey="id" joinKey="customer_id" parentKey="id"> </childTable>
</table>
  • name:定义逻辑表名,同一个schema中逻辑表名必须唯一
  • primaryKey:指定逻辑表中的主键,也是需要与物理表的主键一致
  • dataNode:指定物理表所在数据节点的名称,该属性的值需要和dataNode标签中的name值相对应。配置多个以逗号隔开,或者使用 dn1$0-100 方式
  • rule:指定逻辑表使用的分片规则名称,规则名称在rule.xml中定义,必须与tableRule标签中name属性对应
  • autoIncrement:是否自增,mycat提供了自增长主键功能,但是对应的mysql节点上数据表,没有auto_increment,那么在mycat层调用last_insert_id()也是不会返回结果的
  • fetchStoreNodeByJdbc:是否启用ER表使用JDBC方式获取DataNode

2.1)childTable标签:用于定义 ER 分片的子表。通过标签上的属性与父表进行关联

  • name:子表名称
  • primaryKey:主键
  • joinKey:子表中字段的名称
  • parentKey:父表中字段名称

3. dataNode标签

定义mycat中的数据节点,也就是我们所说的数据分片。一个dataNode标签就是一个独立的数据分片。数据节点指向的是存储逻辑表的物理数据库。示例:

<dataNode name="dn1" dataHost="localhost1" database="db1" />
<dataNode name="dn2" dataHost="localhost1" database="db2" />
<dataNode name="dn3" dataHost="localhost1" database="db3" />
  • name:定义数据节点的名字,这个名字需要唯一。上边在table标签上用这个名字来建立表与分片对应的关系
  • dataHost:定义该分片属于哪个数据库实例,属性与dataHost标签上定义的name对应
  • database:定义该分片属于数据库实例上的具体库

4. dataHost标签

这个标签直接定义了具体数据库实例,读写分离配置和心跳语句。示例:

<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
 <heartbeat>select user()</heartbeat>
 <writeHost host="hostM1" url="jdbc:mysql://localhost:3306" user="root" password="root">
     <readHost host="hostS1" url="jdbc:mysql://localhost:3306" user="root" password="123456"/>
 </writeHost>
</dataHost>
  • name:唯一标示dataHost标签,供上层使用,必须唯一

  • maxCon:指定每个读写实例连接池的最大连接

  • minCon:指定每个读写实例连接池的最小连接,初始化连接池的大小

  • balance:指定读写分离的负载均衡类型,目前有4种类型

    0:代表不开启读写分离机制,所有读操作都发送到当前可用的writeHost上

    1:代表全部的readHost与stand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1-S1,M2-S2 并且M1 M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡。

    2:所有读操作都随机的在writeHost、readHost上分发

    3:所有读请求随机的分发到writeHost对应的readHost执行,writeHost不负担读压力(1.4 新增)

  • writeType:指定写实例的负载均衡类型,目前有4种类型

    -1:表示不自动切换

    0:所有写操作发送到配置的第一个writeHost,第一个挂了切到还生存的第二个writeHost。重新启动后以切换后的为准,切换记录在配置文件中:dnindex.properties

    1:所有写操作都随机的发送到配置的writeHost,1.5 以后废弃不推荐使用

    2:基于MySQL主从同步的状态决定是否切换(1.4 新增)

  • dbType:指定后端连接的数据库类型

  • dbDriver:指定连接后端数据库使用的驱动,目前可选的值有 native 和 jdbc

  • switchType:指定主从切换的方式

    -1:表示不自动切换

    1:默认值,自动切换

    2:基于MySQL主从同步的状态决定是否切换,心跳检测语句为:show slave status

    3:基于MySQL galary cluster 的切换机制(适合集群,1.4.1新增),心跳检测语句为:show status like 'wsrep%'

  • slaveThreshold:定义主从复制延时阈值,当 Seconds_Behind_Master > slaveThreshold 时,读写分离筛选器会过滤掉此Slave机器,防止读到很久之前的旧数据。该属性是用于配合writeType属性实现根据主从延时来进行主从切换的

4.1)heartbeat 标签:指明用于和后端数据库进行心跳检查的语句。例如,MySQL可以使用select user(),Oracle可以使用select 1 from dual

4.2)writehost 标签及 readHost 标签:这两个标签属性相同,这里一起介绍

  • host:标识不同实例名称,一般writeHost名称使用M1作为后缀,readHost则使用S1作为后缀
  • url:配置数据库的连接地址,如果dbDriver属性是 native,则一般为address:port这种形式。如果是jdbc或其他的,则需要特殊指定。
  • user:配置数据库用户名
  • password:配置数据库密码
  • weight:配置某个数据库在 readHost 中作为读节点的权重
  • usingDecrypt:指定是否对密码加密,默认为0, 若需要开启则配置为1

二、server.xml

该配置文件主要是用于配置系统参数、用户信息、访问权限及SQL防火墙和SQL拦截功能等

  1. system标签

    用于配置mycat的系统配置参数。常见的配置参数示例:

<system>
    <!-- mycat 服务连接端口 -->
    <property name="serverPort">8066</property>
    <!-- mycat 服务管理端口 -->
    <property name="managerPort">9066</property>
    <!-- mycat 服务监听的ip -->
    <property name="bindIp">0.0.0.0</property>
    <!-- 0为需要密码登陆、1为不需要密码登陆;默认为0,设置为1则需要指定默认账户-->
    <property name="nonePasswordLogin">0</property>
    <!-- 前端连接的写队列大小 -->
    <property name="frontWriteQueueSize">2048</property>
    <!-- 设置字符集编码 -->
    <property name="charset">utf8</property>
    <!-- mycat 的进程数量 -->
    <property name="processors">8</property>
    <!-- 闲置连接超时时间,单位:毫秒 -->
    <property name="idleTimeout">1800000</property>
    <!-- 默认最大返回的数据集大小 -->
    <property name="defaultMaxLimit">100</property>
    <!-- 允许的最大包大小 -->
    <property name="maxPacketSize">104857600</property>
    <!-- 0遇上没有实现的报文(Unknown command:),就会报错、1为忽略该报文,返回ok报文。
在某些mysql客户端存在客户端已经登录的时候还会继续发送登录报文,mycat会报错,该设置可以绕过这个错误-->
    <property name="ignoreUnknownCommand">0</property>
    <property name="useHandshakeV10">1</property>
    <property name="removeGraveAccent">1</property>
    <!-- 1为开启实时统计、0为关闭 -->
    <property name="useSqlStat">0</property>
    <!-- 1为开启全加班一致性检测、0为关闭 -->
    <property name="useGlobleTableCheck">0</property>
    <!-- SQL 执行超时 单位:秒-->
    <property name="sqlExecuteTimeout">300</property>
    <property name="sequnceHandlerType">1</property>
    <!--必须带有MYCATSEQ_或者 mycatseq_进入序列匹配流程 注意MYCATSEQ_有空格的情况-->
    <property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>
    <!-- 子查询中存在关联查询的情况下,检查关联字段中是否有分片字段 .默认 false -->
    <property name="subqueryRelationshipCheck">false</property>
    <property name="sequenceHanlderClass">io.mycat.route.sequence.handler.HttpIncrSequenceHandler</property>
    <!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool -->
    <property name="processorBufferPoolType">0</property>
    <!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,但是记录分布式事务日志-->
    <property name="handleDistributedTransactions">0</property>
    <!-- off heap for merge/order/group/limit  1开启;0关闭 -->
    <property name="useOffHeapForMerge">0</property>
    <!--是否采用zookeeper协调切换  -->
    <property name="useZKSwitch">false</property>
    <!--如果为 true的话 严格遵守隔离级别,不会在仅仅只有select语句的时候在事务中切换连接-->
    <property name="strictTxIsolation">false</property>
    <!-- Mycat连接数据库时使用的隔离级别
         1 - 读未提交
         2 - 读已提交
         3 - 可重复读
         4 - 串行化
     -->
    <property name="txIsolation">2</property>
    <property name="useZKSwitch">true</property>
    <!--如果为0的话,涉及多个DataNode的catlet任务不会跨线程执行-->
    <property name="parallExecute">0</property>
</system>

2. user标签

用于配置mycat的访问用户及权限。示例:

<!-- 用户名,defaultAccount属性是指定当前账户是否为默认账户 -->
<user name="root" defaultAccount="true">
    <!-- 密码 -->
    <property name="password">123456</property>
    <!-- 允许该用户访问的逻辑库 -->
    <property name="schemas">TESTDB</property>
    <!-- 可配置多个允许访问的逻辑库,使用逗号分隔 -->
    <!-- <property name="schemas">db1,db2</property>  -->
    <!-- 是否只读 -->
    <property name="readOnly">false</property>
    <!-- 表级 DML 权限配置,check属性表示是否开启该配置 -->
    <privileges check="true">
        <!-- 特别权限应用的逻辑库 -->
        <schema name="TESTDB" dml="0110">
            <!-- 
                配置用户对该表的访问权限,dml属性用于指定权限位,
                如果table标签没有配置该属性的话,默认取schema标签的dml属性值,
                剩余没有配置的其他表默认也是取schema标签的dml属性值
            -->
            <table name="tb01" dml="0000"></table>
            <table name="tb02" dml="1111"></table>
        </schema>
    </privileges>
</user>

dml属性配置的数字是权限位,分别对应着 insert,update,select,delete 四种权限。例如,当dml的值为0110时,表示拥有 update 和 select 权限,不具有 insert 和 delete 权限。所以权限位为1时代表拥有对应的操作权限,为0时代表没有该操作权限。

三、rule.xml

该配置文件主要用于配置切分规则的。数据库水平拆分之后,就需要用这个配置文件定义分片算法规则

配置文件中主要有两种标签,tableRule 和 function ,这两个是一一对应的,示例:

<!-- name属性指定分片规则的名称,必须在 rule.xml 文件中是唯一的 -->
<tableRule name="mod-long">
    <rule>
        <!-- 指定使用表中的哪个列(字段)进行分片 -->
        <columns>id</columns>
        <!-- 指定表的分片算法,取值为<function>标签的name属性 -->
        <algorithm>mod-long</algorithm>
    </rule>
</tableRule>
<!-- name属性指定分片算法的名称,同样需要是唯一的;class属性指定该算法的具体实现类 -->
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
    <!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
    <property name="count">3</property>
</function>

mycat默认已经内置了很多分片算法,除了使用这些外也可以根据自己的实际需求自定义算法规则。

mycat内置的常用分片算法有:

  • PartitionByMod:简单取模,直接通过列值进行取模得出分片位置。适用于整数类型的列,不能用于非整型的列,且不需要人工干预
  • PartitionByHashMod:哈希取模,先将列值进行hash运算之后再取模得出分片位置。适用于非整型的列,且不需要人工干预
  • PartitionByFileMap:分片枚举,根据枚举值对数据进行分片。适用于需要人工指定某些数据到哪个分片下。例如在异地多活的场景中通过地区id进行数据分片的场景。
  • PartitionByPrefixPattern:字符串范围取模,根据长字符串的前面几位进行取模分片


四、加密明文密码(可选)

在mycat中,不论是 server.xml 配置文件中的mycat登录密码,还是 schema.xml 配置文件的writeHost和readHost标签的连接密码,都是以明文显示的,只要查看这两个配置文件,就可以知道密码,这种方式可能会存在安全隐患。

所以mycat提供了一个工具用于加密明文密码,该工具在一个jar包内,可使用如下命令对密码进行加密,执行成功后会得到一个加密后的字符串,然后替换配置文件中的明文密码即可

java -cp lib/Mycat-server-1.6.7.6-release.jar io.mycat.util.DecryptUtil 0:root:123456

命令解释:

0:user:password 是加密字符串,有两种格式,示例

  • 0:root:123456:代表对mycat用户名root明文密码123456的账户进行加密,修改配置文件示例

<user name="root" defaultAccount="true">
  <property name="usingDecrypt">1</property>
  <property name="password">d6D+pOmkuUoY09p4/aivwMsScLa7zfjIwAxvkEhr3v7en06mEXoX9DTTjQNug5CfvGf7Wy9oLcthYI3yLMSjIg==</property>
  <property name="schemas">TESTDB</property>
</user>
  • 1:hostM1:root:123456 :代表对 schema.xml 配置文件中dataHost标签下数据库连接加密,修改示例
<writeHost host="hostM1" url="localhost:3306" user="root" password="BpkNIjF7LfzS1C76HT7B1bJgmGIDtPihqIvHBlC92L1IFqsMfoJEMk1EkxSzjasWB4GWoUcODYO4AaJstdAp5w==" usingDecrypt="1">
</writeHost>

实例演示

以商城为例,假使现在的需求是:一个订单表数据量太大,需要进行水平拆分。

假使解决方案是:使用三台服务器部署mysql,实现一主二从读写分离。数据库名为shop,订单表拆分成64张表,表名为orders0-63。使用雪花算法实现分布式唯一id,由于id是整型,所以切片规则选择PartitionByMod算法,也就是直接对id进行取模,这样可以保证数据分布均匀且易扩展,将算法中分片数设置为64

按照以上方案,直接贴出相关配置文件中的核心代码

schema.xml

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
    <schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
         <table name="orders" primaryKey="id" subTables="orders_$0-63" dataNode="dn1" rule="mod-long" />
    </schema>
    <dataNode name="dn1" dataHost="shop" database="shop" />
    <dataHost name="shop" maxCon="1000" minCon="10" balance="3" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
        <heartbeat>select user()</heartbeat>
        <writeHost host="hostM1" url="192.168.241.140:3306" user="root" password="root">
            <readHost host="hostS1" url="192.168.241.141:3306" user="root" password="root"/>
            <readHost host="hostS2" url="192.168.241.142:3306" user="root" password="root"/>
        </writeHost>
    </dataHost>
</mycat:schema>

rule.xml

<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">64</property>
</function>

测试

mysql> explain insert into orders (id,no) values(1,'1111111');
+-----------+------------------------------------------------------------+
| DATA_NODE | SQL                                                        |
+-----------+------------------------------------------------------------+
| dn1       | INSERT INTO orders_1 (id, no) VALUES ('1', '1111111') |
+-----------+------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> insert into orders (id,no) values(1,'1111111');
Query OK, 1 row affected (0.01 sec)

mysql> explain select id,no from orders where id in (1);
+-----------+---------------------------------------------------------+
| DATA_NODE | SQL                                                     |
+-----------+---------------------------------------------------------+
| dn1       | SELECT id, no FROM orders_1 orders WHERE id IN (1) |
+-----------+---------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select id,no from orders where id in (1);
+----+---------+
| id | no      |
+----+---------+
|  1 | 1111111 |
+----+---------+
1 row in set (0.00 sec)

mysql> show variables like "server_id";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 2     |
+---------------+-------+
1 row in set (0.00 sec)

mysql> show variables like "server_id";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 3     |
+---------------+-------+
1 row in set (0.01 sec)

mysql>

通过上述可观察到,数据在插入和查询时mycat会自动取模计算找到对应的表。查询 server-id 参数发现在变,证明确实在两个从库中进行负载轮询。到此说明整个操作成功。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值