Mycat


文章目录

* 一、Mycat介绍
  * 1、是什么(数据库中间件)
  * 2、干什么的
    * (1)读写分离
    * (2)数据分片
    * (3)多数据源整合
  * 3、原理
  * 4、Mycat监控
  * 5、Mycat架构
* 二、安装启动
  * 1、安装
  * 2、三个文件
  * 3、启动前先修改schema.xml
  * 4、再修改server.xml
  * 5、验证数据库访问情况
  * 6、启动程序
  * 7、启动时可能出现报错
  * 8、登录
    * 8.1 后台管理窗口
    * 8.2 数据窗口
* 三、读写分离
  * 1、schema.xml
  * 2、读写分离
* 四、分库
  * 1、如何选择分库表
  * 2、schema.xml
* 五、水平分表
  * 1、schema.xml
  * 2、rule.xml
  * 3、跨库join
    * (1)ER表
    * (2)全局表
  * 4、全局序列(三种方式)
    * 4.1 本地文件(不推荐)
    * 4.2 数据库方式
      * (1)数据库序列方式原理
      * (2)建库序列脚本(推荐)
      * (3)修改mycat配置
      * (4)插入语句
    * 4.3 时间戳方式
    * 4.4 自主生成
* 六、如何运用
* 七、注意事项(要提前看)

一、Mycat介绍

1、是什么(数据库中间件)

  • 前身是阿里的cobar
  • 一个彻底开源的,面向企业应用开发的大数据库集群
  • 支持事务、ACID、可以替代MySQL的加强版数据库
  • 一个可以视为MySQL集群的企业级数据库,用来替代昂贵的Oracle集群
  • 一个融合内存缓存技术、NoSQL技术、HDFS大数据的新型SQL Server
  • 结合传统数据库和新型分布式数据仓库的新一代企业级数据库产品
  • 一个新颖的数据库中间件产品

2、干什么的

(1)读写分离

(2)数据分片

  • 垂直拆分

  • 水平拆分

  • 垂直+水平拆分

(3)多数据源整合

3、原理

拦截

Mycat 的原理中最重要的一个动词是“拦截”,它拦截了用户发送过来的 SQL 语句,首先对 SQL 语句做了

一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此 SQL 发往后端的真实数据库,

并将返回的结果做适当的处理,最终再返回给用户

这种方式把数据库的分布式从代码中解耦出来,程序员察觉不出来后台使用mycat还是mysql。

4、Mycat监控

  • 支持对Mycat、Mysql性能监控
  • 支持对Mycat的JVM内存提供监控服务
  • 支持对线程的监控
  • 支持对操作系统的CPU、内存、磁盘、网络的监控

5、Mycat架构

1.6版本架构图:

二、安装启动

1、安装

(1)下载安装包:去官网( http://www.mycat.io/

Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz

(2)解压缩文件拷贝到linux下 /usr/local/

tar   -zxvf    Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz  -C    /usr/local/

(3)安装完成!

2、三个文件

  • schema.xml

作用:定义逻辑库,表、分片节点等内容

  • rule.xml

作用:定义分片规则

  • server.xml

作用:定义用户以及系统相关变量,如端口等.

3、启动前先修改schema.xml

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
   <!--逻辑库    name名称,   checkSQLschema         sqlMaxLimit 末尾是否要加 limit xxx-->
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"></schema>
<!--逻辑库 name名称,   dataHost 引用的哪个dataHost      database:对应mysql的database-->
 <dataNode name="dn1" dataHost="localhost1" database="db1" />
 <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
    writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
     <!--心跳机制 -->
      <heartbeat>select user()</heartbeat>
       <!-- can have multi write hosts -->
        <writeHost host="hostM1" url="localhost:3306" user="root"                           password="123456">
        </writeHost>
   </dataHost>
</mycat:schema>



(1)<schema>:逻辑库!schema中的name是虚拟出来的逻辑库的数据库名

(2)<dataNode>:dataNode中的name对应schema中的dataNode的值,可以有多个dataNode,根据需求指定!
database是真实数据库中的库名
(3)<dataHost>:dataHost中的name对应dataNode中的dataHost得值!
balance指的是负载均衡的类型(后面有详细说明)

4、再修改server.xml

<user name="mycat"> <!-- user中的name不要指定为root,使用Mycat登录,用这个用户名  -->
        <property name="password">654321</property><!-- 使用Mycat登录,用这个密码 -->

        <property name="schemas">TESTDB</property><!-- 虚拟出来的逻辑库,通过配置它包含真实库的信息 -->
</user>

5、验证数据库访问情况

mysql -uroot -p123123 -h 192.168.154.1 -P 3306

mysql -uroot -p123123 -h 192.168.154.154 -P 3306


如本机远程访问报错,请建对应用户

grant all privileges on . to root@'缺少的host' identified by '123123';

6、启动程序

  • 控制台启动 :去mycat/bin 目录下 mycat console

  • 后台启动 :去mycat/bin 目录下 mycat start

7、启动时可能出现报错

域名解析失败


(1)用vim 修改 /etc/hosts 文件

在 127.0.0.1 后面增加你的机器名

(2)修改后重新启动网络服务

8、登录

8.1 后台管理窗口

mysql -uroot -p654321 -P9066 -h192.168.67.131

命令:

show database

show @@help

8.2 数据窗口

mysql -uroot -p654321 -P8066 -h192.168.67.131

三、读写分离

1、schema.xml

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
        </schema>
        <dataNode name="dn1" dataHost="host1" database="user" />
        <!--balance="2",所有读操作都随机的在 writeHost、readhost 上分发。 -->
        <dataHost name="host1" maxCon="1000" minCon="10" balance="2"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
           <heartbeat>select user()</heartbeat>
           <writeHost host="hostm1" url="主机(写数据的服务器)IP地址:3306" user="用户名"
                                   password="密码">
            <!--读库(从库)的配置 -->
             <readHost host="hosts1" url="从机(读机器的服务器)IP地址:3306" user="用户名"
                                   password="密码">
             </readHost>
           </writeHost>
        </dataHost>
</mycat:schema>
  • balance负载均衡类型,目前的取值有4 种:
    • balance=“0”, 不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上。
    • balance=“1”,全部的 readHost 与 stand by writeHost 参与 select 语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且 M1 与 M2 互为主备),正常情况下,M2,S1,S2 都参与 select 语句的负载均衡。
    • balance=“2”,所有读操作都随机的在 writeHost、readhost 上分发。
    • balance=“3”,所有读请求随机的分发到 readhost 执行,writerHost 不负担读压力

配置server.xml

<user name="mycat"> <!-- user中的name不要指定为root,使用Mycat登录,用这个用户名  -->
        <property name="password">654321</property><!-- 使用Mycat登录,用这个密码 -->

        <property name="schemas">TESTDB</property><!-- 虚拟出来的逻辑库,通过配置它包含真实库的信息 -->
</user>

2、读写分离

(1)创建库/表(从机和主机都要有)

create database user;


create table t_replica 

(  id int auto_increment primary key, 

   name varchar(200) 

);

(2)分别在主机和从机两个库下插入

insert into t_replica(name) values (@@hostname)

(3)使用mycat账号登录数据库

mysql -umycat -p654321 -h 192.168.186.100(mycat所在的服务器IP)-P 3306



use TESTDB;
select * from t_replica;

四、分库

1、如何选择分库表

#客户表 rows:20万 

CREATE TABLE customer(

  id INT AUTO_INCREMENT,

  NAME VARCHAR(200),

  PRIMARY KEY(id)

);


#订单表  rows:600万

CREATE TABLE orders(

  id INT AUTO_INCREMENT,

  order_type INT,

  customer_id INT,

  amount DECIMAL(10,2),

  PRIMARY KEY(id) 

); 


#订单详细表   rows:600万

CREATE TABLE orders_detail(

  id INT AUTO_INCREMENT,

  detail VARCHAR(2000),

  order_id INT,

  PRIMARY KEY(id)

);


#订单状态字典表  rows:20

CREATE TABLE dict_order_type(

  id INT AUTO_INCREMENT,

  order_type VARCHAR(200),

  PRIMARY KEY(id)

);

#测试联合查询
select o.*,od.detail

from orders o 

inner join orders_detail od on o.id =od.order_id;

2、schema.xml

<mycat:schema xmlns:mycat="http://io.mycat/">

        <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
               <!--指定 customer表放在哪台数据库服务器上,其余的表存放在另一台服务器上-->
               <table name="customer" dataNode="dn2" ></table>
        </schema>


        <dataNode name="dn1" dataHost="host1" database="shopping" />
        <dataNode name="dn2" dataHost="host2" database="shopping" />



        <dataHost name="host1" maxCon="1000" minCon="10" balance="2"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="hostm1" url="第一台数据库服务器IP:3306" user="root"
                                   password="123123">

                </writeHost>
        </dataHost>


        <dataHost name="host2" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="hostm2" url="第二台数据库服务器IP:3306" user="root"
                                   password="123123">
                </writeHost>
        </dataHost>

</mycat:schema>

五、水平分表

1、schema.xml

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

        <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
                <table name="customer" dataNode="dn2" ></table>
                <!--指定要分哪张表(orders),并且存放在哪几个数据库服务器上(dn1,dn2)!mod_rule指的是那种分表规则 -->
                <table name="orders" dataNode="dn1,dn2"  rule="mod_rule" ></table>
        </schema>
        <dataNode name="dn1" dataHost="host1" database="shopping" />
        <dataNode name="dn2" dataHost="host2" database="shopping" />
        <dataHost name="host1" maxCon="1000" minCon="10" balance="2"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="hostm1" url="第一台数据库服务器IP:3306" user="root"
                                   password="123123">
                <!--       <readHost host="hosts1" url="192.168.67.131:3306" user="root"
                                   password="123123">
                       </readHost>-->
                </writeHost>
        </dataHost>
        <dataHost name="host2" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="hostm2" url="第二台数据库服务器IP:3306" user="root"
                                   password="123123">
                </writeHost>
        </dataHost>

2、rule.xml

<tableRule name="mod_rule">
             <rule>
                  <columns>customer_id</columns>
                  <algorithm>mod-long</algorithm>
             </rule>
</tableRule>

<!--指的是上面的分表规则用的是那种算法 -->
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
                <!-- 指的是服务器的个数 -->
                <property name="count">2</property>
</function>

3、跨库join

(1)ER表

  • 为了相关联的表的行尽量分在一个库下

  • schema.xml

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

        <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
                <table name="customer" dataNode="dn2" ></table>
                <table name="orders" dataNode="dn1,dn2"  rule="mod_rule" >
                    <!--对于需要进行多表连接查询的表,指定自己的主键,父表的主键,连接的外键-->
                    <childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
                </table>
            <!--type="global":指的是每个数据库上保存的表数据一致 -->
                <table name="dict_status" dataNode="dn1,dn2" type="global" ></table>
        </schema>
        <dataNode name="dn1" dataHost="host1" database="shopping" />
        <dataNode name="dn2" dataHost="host2" database="shopping" />
        <dataHost name="host1" maxCon="1000" minCon="10" balance="2"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="hostm1" url="第一台数据库服务器IP:3306" user="root"
                                   password="123123">
                <!--       <readHost host="hosts1" url="192.168.67.131:3306" user="root"
                                   password="123123">
                       </readHost>-->
                </writeHost>
        </dataHost>
        <dataHost name="host2" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="hostm2" url="第二台数据库服务器IP" user="root"
                                   password="123123">
                </writeHost>
        </dataHost>

</mycat:schema>
  • 建相关表

    #客户表 rows:20万

    Create table customer( id int auto_increment, name varchar(200), primary key(id) );

    #订单表 rows:600万 create table orders( id int auto_increment, order_type int, customer_id int, amount decimal(10,2), PRIMARY KEY(id) );

    #订单详情表 rows:600万 create table orders_detail( id INT AUTO_INCREMENT, detail varchar(2000), order_id int, PRIMARY KEY(id) );

    #订单状态字典表 rows:20 create table dict_order_type( id INT AUTO_INCREMENT, order_type varchar(200), PRIMARY KEY(id) );

TRUNCATE TABLE 
insert into customer values(100,'zhang3');
INSERT INTO customer VALUES(101,'li4');

insert into orders(id,order_type,customer_id,amount) values(1,101,100,100100);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(2,101,100,100300);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(3,101,101,120000);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(4,101,101,103000);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(5,102,101,100400);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(6,102,100,100020);


insert into dict_order_type values(101,'type1');
insert into dict_order_type values(101,'type1');

select * from orders o inner join customer c on  o.customer_id=c.id;
select * from orders o inner join dict_order_type ot on ot.id=o.order_type;

select * from orders

SELECT * FROM orders o INNER JOIN orders_detail od ON od.order_id=o.id;

insert into orders_detail(id,detail,order_id) values(1,'detail1',1);
INSERT INTO orders_detail(id,detail,order_id) VALUES(2,'detail1',2);
INSERT INTO orders_detail(id,detail,order_id) VALUES(3,'detail1',3);
INSERT INTO orders_detail(id,detail,order_id) VALUES(4,'detail1',4);
INSERT INTO orders_detail(id,detail,order_id) VALUES(5,'detail1',5);
INSERT INTO orders_detail(id,detail,order_id) VALUES(6,'detail1',6);

use TESTDB;
delete from dict_order_type;

insert into dict_order_type(id,order_type) values(101,'type1');
INSERT INTO dict_order_type(id,order_type) VALUES(102,'type2');


alter table orders change id id bigint;

insert into `orders`(id,amount,customer_id,order_type) values(next value for MYCATSEQ_GLOBAL,1000,103,101);

select * from orders;

(2)全局表

  • 设定为全局的表,会直接复制给每个数据库一份,所有写操作也会同步给多个库。

  • 所以全局表一般不能是大数据表或者更新频繁的表

  • 一般是字典表或者系统表为宜。

  • schema.xml

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

        <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
                <table name="customer" dataNode="dn2" ></table>
                <table name="orders" dataNode="dn1,dn2"  rule="mod_rule" ></table>
                 <table name="dict_order_type" dataNode="dn1,dn2" type="global" ></table>
        </schema>
        <dataNode name="dn1" dataHost="host1" database="atguigu_mc" />
        <dataNode name="dn2" dataHost="host2" database="atguigu_sm" />
        <dataHost name="host1" maxCon="1000" minCon="10" balance="2"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="hostm1" url="192.168.67.1:3306" user="root"
                                   password="123123">
                <!--       <readHost host="hosts1" url="192.168.67.131:3306" user="root"
                                   password="123123">
                       </readHost>-->
                </writeHost>
        </dataHost>
        <dataHost name="host2" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="hostm2" url="192.168.67.1:3306" user="root"
                                   password="123123">
                </writeHost>
        </dataHost>

4、全局序列(三种方式)

4.1 本地文件(不推荐)

4.2 数据库方式

(1)数据库序列方式原理
  • 利用数据库一个表 来进行计数累加。
  • 但是并不是每次生成序列都读写数据库,这样效率太低
  • mycat会预加载一部分号段到mycat的内存中,这样大部分读写序列都是在内存中完成的。
  • 如果内存中的号段用完了 mycat会再向数据库要一次。
  • 问:那如果mycat崩溃了,那内存中的序列岂不是都没了?
    • 是的。如果是这样,那么mycat启动后会向数据库申请新的号段,原有号段会弃用。
    • 也就是说如果mycat重启,那么损失是当前的号段没用完的号码,但是不会因此出现主键重复。
(2)建库序列脚本(推荐)
win10

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;




DELIMITER $$

CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS VARCHAR(64)
DETERMINISTIC  
BEGIN
DECLARE retval VARCHAR(64);
SET retval="-999999999,null";
SELECT CONCAT(CAST(current_value AS CHAR),",",CAST(increment AS CHAR)) INTO retval FROM
MYCAT_SEQUENCE WHERE NAME = seq_name;
RETURN retval;
END $$
DELIMITER ;

DELIMITER $$
CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50),VALUE INTEGER) RETURNS VARCHAR(64)
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = VALUE
WHERE NAME = seq_name;
RETURN mycat_seq_currval(seq_name);
END $$
DELIMITER ;

DELIMITER $$
CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS VARCHAR(64) 
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = current_value + increment WHERE NAME = seq_name;
RETURN mycat_seq_currval(seq_name);
END $$
DELIMITER ;


SELECT * FROM MYCAT_SEQUENCE

TRUNCATE TABLE MYCAT_SEQUENCE

##增加要用的序列
INSERT INTO MYCAT_SEQUENCE(NAME,current_value,increment) VALUES ('ORDERS', 400000,
100);
(3)修改mycat配置
  • sequence_db_conf.properties

vim sequence_db_conf.properties

意思是 ORDERS这个序列在dn1这个节点上,具体dn1节点是哪台机子,请参考schema.xml

  • server.xml

vim server.xml

  • 然后重启
(4)插入语句
insert into orders(id,amount,customer_id,order_type) values(next value for MYCATSEQ_ORDERS,1000,101,102);

4.3 时间戳方式

长度:18位

缺点:比较长

4.4 自主生成

根据业务逻辑组合

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

六、如何运用

首先要准备一些服务器,并且创建数据库(其中没有任何数据)!根据已有的数据库,分析哪些表、哪些库需要分表、分库,配置好Mycat的配置文件,就可以使用了!

七、注意事项(要提前看)

(1)将服务器上对应的账号开放远程访问权限

(2)关闭防火墙

(3)服务器在同一个网关下

(4)修改完Mycat的配置文件,需要重新启动(mycat console)

(5)要保证数据库都能通过远程访问

(6)固定好服务器的IP地址


原文链接:https://blog.csdn.net/weixin_41043145/article/details/102753351