zoukankan      html  css  js  c++  java
  • MyCat 概念与配置

    1.1 Mycat 介绍与核心概念
    • 1.1.1 基本介绍
    历史:从阿里 cobar 升级而来,由开源组织维护,2.0 正在开发中。
    定位:运行在应用和数据库之间,可以当做一个 MySQL 服务器使用,实现对 MySQL数据库的分库分表,也可以通过 JDBC 支持其他的数据库。
    Mycat 的关键特性(官网首页)
    • 1、可以当做一个 MySQL 数据库来使用;
    • 2、支持 MySQL 之外的数据库,通过 JDBC 实现;
    • 3、解决了我们提到的所有问题,多表 join、分布式事务、全局序列号、翻页排序;
    • 4、支持 ZK 配置,带监控 mycat-web;
    • 5、2.0 正在开发中;
    • 1.1.2 核心概念

    概念 含义
    主机
    物理主机,一台服务器,一个数据库服务,一个 3306 端口
    物理数据库
    真实的数据库,例如 146、150、151 的 gpcat 数据库
    物理表
    真实的表,例如 146、150、151 的 gpcat 数据库的 order_info 表 
    分片
    将原来单个数据库的数据切分后分散存储在不同的数据库节点
    分片节点
    分片以后数据存储的节点
    分片键
    分片依据的字段,例如 order_info 表以 id 为依据分片,id 就是分片键,通常是主键
    分片算法
    分片的规则,例如随机、取模、范围、哈希、枚举以及各种组合算法
    逻辑表
    相对于物理表,是分片表聚合后的结果,对于客户端来说跟真实的表没有区别
    逻辑数据库
    相对于物理数据库,是数据节点聚合后的结果,例如 catmall
    下载、解压 Mycat(有 Windows 版本,可以在本地数据库测试):
    http://dl.mycat.io/
    wget http://dl.mycat.io/1.6.7.3/20190927161129/Mycat-server-1.6.7.3-release-20190927161129-linux.tar.gz tar -xzvf Mycat-server-1.6.7.3-release-20190927161129-linux.tar.gz
    
    Mycat 解压以后有 5 个目录:
    目录 作用
    bin
    启动目录
    catlet
    空目录
    conf
    配置目录 
    lib
    jar 包依赖
    logs
    日志目录
    1.2 Mycat 配置详解
    主要的配置文件 server.xml、schema.xml、rule.xml 和具体的分片配置文件。
    坑非常多,配置错误会导致无法启动,这个时候要看日志!
    注意备份,不知道什么时候就跑不起来了……
    • 1.2.1 server.xml
    包含系统配置信息。
    system 标签:例如字符集、线程数、心跳、分布式事务开关等等。
    user 标签:配置登录用户和权限。
    <user name="root" defaultAccount="true"> 
      <property name="password">123456</property> 
      <property name="schemas">catmall</property>
    </user>
    
    mycat 对密码加密:
    java -cp Mycat-server-1.6.7.3-release.jar io.mycat.util.DecryptUtil 0:root:123456
    • 1.2.2 schema.xml
    https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_schema
    schema 在 MySQL 里面跟数据库是等价的。
    schema.xml 包括逻辑库、表、分片规则、分片节点和数据源,可以定义多个 schema。
    这里面有三个主要的标签(table、dataNode、dataHost):
    <table/>
    表名和库名最好都用小写
    定义了逻辑表,以及逻辑表分布的节点和分片规则: 
    <schema name="catmall" checkSQLschema="false" sqlMaxLimit="100">
      <!-- 范围分片 -->
      <table name="customer" primaryKey="id" dataNode="dn1,dn2,dn3" rule="rang-long-cust" />
      <!-- 取模分片 --> 
      <table name="order_info" dataNode="dn1,dn2,dn3" rule="mod-long-order" >
    	 <!-- ER 表 -->
    	 <childTable name="order_detail" primaryKey="id" joinKey="order_id" parentKey="order_id"/> 
      </table> 
      <!-- 全局表 -->
      <table name="student" primaryKey="sid" type="global" dataNode="dn1,dn2,dn3" />
     </schema>
    
    配置
    作用
    primaryKey
    指定该逻辑表对应真实表的主键。MyCat 会缓存主键(通过 primaryKey 属性配置)与
    具体 dataNode 的信息。
    当分片规则(rule)使用非主键进行分片时,那么在使用主键进行查询时,MyCat 就
    会通过缓存先确定记录在哪个 dataNode 上,然后再在该 dataNode 上执行查询。
    如果没有缓存/缓存并没有命中的话,还是会发送语句给所有的 dataNode
    dataNode
    数据分片的节点
    autoIncrement
    自增长(全局序列),true 代表主键使用自增长策略
    type
    全局表:global。其他:不配置
    <dataNode/>
    <dataNode name="dn1" dataHost="host1" database="gpcat" />
    数据节点与物理数据库的对应关系。
    配置物理主机的信息,readhost 是从属于 writehost 的。
    <dataHost name="host1" 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"> <!-- can have multi read hosts -->
    		<readHost host="hostS2" url="192.168.8.146:3306" user="root"
    			password="xxx" />
    	</writeHost>
    	<writeHost host="hostS1" url="localhost:3316" user="root"
    		password="123456" /> <!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
    </dataHost>
    
    balance:负载的配置,决定 select 语句的负载
    作用
    0
    不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上。
    1
    所有读操作都随机发送到当前的 writeHost 对应的 readHost 和备用的 writeHost
    2
    所有的读操作都随机发送到所有的 writeHost,readHost 上
    3
    所有的读操作都只发送到 writeHost 的 readHost 上
    writeType:读写分离的配置,决定 update、delete、insert 语句的负载
    作用
    0
    所有写操作都发送到可用的 writeHost 上(默认第一个,第一个挂了以后发到第二个)
    1
    所有写操作都随机的发送到 writeHost
    switchType:主从切换配置
    作用
    -1
    表示不自动切换 
    1
    默认值,表示自动切换
    2
    基于 MySQL 主从同步的状态决定是否切换,心跳语句为 show slave status
    3
    基于 MySQL galary cluster 的切换机制(适合集群)(1.4.1),心跳语句为 show status like 'wsrep%'。
    • 1.2.3 rule.xml
    定义了分片规则和算法
    分片规则:
    <tableRule name="rang-long-cust">
    	<rule>
    		<columns>id</columns>
    		<algorithm>func-rang-long-cust</algorithm>
    	</rule>
    </tableRule>
    
    分片算法:
    <function name="func-rang-long-cust" class="io.mycat.route.function.AutoPartitionByLong">
    	<property name="mapFile">rang-long-cust.txt</property>
    </function
    分片配置:rang-long-cust.txt
    10001-20000=1 
    0-10000=0 
    20001-100000=2
    
    3.2.4 ZK 配置
    Mycat 也支持 ZK 配置(用于管理配置和生成全局 ID),执行 bin 目录下init_zk_data.sh,会自动将 zkconf 下的所有配置文件上传到 ZK(先拷贝到这个目录)。
    cd /usr/local/soft/mycat/conf 
    cp *.txt *.xml *.properties zkconf/ 
    
    cd /usr/local/soft/mycat/bin 
    ./init_zk_data.sh
    
    启用 ZK 配置:
    mycat/conf/myid.properties
    loadZk=true 
    zkURL=127.0.0.1:2181 
    clusterId=010 myid=01001 
    clusterSize=1 
    clusterNodes=mycat_gp_01 
    #server booster ; booster install on db same server,will reset all minCon to 2 
    type=server 
    boosterDataHosts=dataHost1
    
    注意如果执行 init_zk_data.sh 脚本报错的话,代表未写入成功,此时不要启用 ZK配置并重启,否则本地文件会被覆盖。
    启动时如果 loadzk=true 启动时,会自动从 zk 下载配置文件覆盖本地配置。
    在这种情况下如果修改配置,需要先修改 conf 目录的配置,copy 到 zkconf,再行上传。
    • 1.2.5 启动停止
    进入 mycat/bin 目录(注意要先启动物理数据库):
    操作 命令
    启动
    ./mycat start 
    停止
    ./mycat stop 
    重启
    ./mycat restart
    查看状态
    ./mycat status 
    前台运行
    ./mycat console
    连接:
    mysql -uroot -p123456 -h 192.168.8.151 -P8066 catmall
    
     
    1.3 Mycat 分片验证
    explain 可以用来看路由结果
    在三个数据库中建表
    CREATE TABLE `customer` (
    	`id` INT (11) DEFAULT NULL,
    	`name` VARCHAR (255) DEFAULT NULL
    ) ENGINE = INNODB DEFAULT CHARSET = utf8;
    
    CREATE TABLE `order_info` (
    	`order_id` INT (11) NOT NULL COMMENT '订单 ID',
    	`uid` INT (11) DEFAULT NULL COMMENT '用户 ID',
    	`nums` INT (11) DEFAULT NULL COMMENT '商品数量',
    	`state` INT (2) DEFAULT NULL COMMENT '订单状态',
    	`create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
    	`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    	PRIMARY KEY (`order_id`)
    ) ENGINE = INNODB DEFAULT CHARSET = utf8;
    
    CREATE TABLE `order_detail` (
    	`order_id` INT (11) NOT NULL COMMENT '订单号',
    	`id` INT (11) NOT NULL COMMENT '订单详情',
    	`goods_id` INT (11) DEFAULT NULL COMMENT '货品 ID',
    	`price` DECIMAL (10, 2) DEFAULT NULL COMMENT '价格',
    	`is_pay` INT (2) DEFAULT NULL COMMENT '支付状态',
    	`is_ship` INT (2) DEFAULT NULL COMMENT '是否发货',
    	`status` INT (2) DEFAULT NULL COMMENT '订单详情状态',
    	PRIMARY KEY (`order_id`, `id`)
    ) ENGINE = INNODB DEFAULT CHARSET = utf8;
    
    CREATE TABLE `student` (
    	`sid` INT (8) NOT NULL AUTO_INCREMENT,
    	`name` VARCHAR (255) DEFAULT NULL,
    	`qq` VARCHAR (255) DEFAULT NULL,
    	PRIMARY KEY (`sid`)
    ) ENGINE = INNODB DEFAULT CHARSET = utf8;
    
    schema.xml
    <table name="customer" dataNode="dn1,dn2,dn3" rule="rang-long-cust"
    	primaryKey="id" />
    <table name="order_info" dataNode="dn1,dn2,dn3" rule="mod-long-order">
    	<childTable name="order_detail" joinKey="order_id"
    		parentKey="order_id" primaryKey="id" />
    </table>
    <table name="student" dataNode="dn1,dn2,dn3" primaryKey="sid"
    	type="global" />
    
    数据节点配置 
    <dataNode name="dn1" dataHost="host1" database="gpcat" />
    <dataNode name="dn2" dataHost="host2" database="gpcat" />
    <dataNode name="dn3" dataHost="host3" database="gpcat" />
    <dataHost balance="0" maxCon="1000" minCon="10" name="host1"
    	writeType="0" switchType="1" slaveThreshold="100" dbType="mysql"
    	dbDriver="native">
    	<heartbeat>select user()</heartbeat>
    	<writeHost host="hostM1" url="192.168.8.146:3306" password="123456"
    		user="root" />
    </dataHost>
    <dataHost balance="0" maxCon="1000" minCon="10" name="host2"
    	writeType="0" switchType="1" slaveThreshold="100" dbType="mysql"
    	dbDriver="native">
    	<heartbeat>select user()</heartbeat>
    	<writeHost host="hostM1" url="192.168.8.150:3306" password="123456"
    		user="root" />
    </dataHost>
    <dataHost balance="0" maxCon="1000" minCon="10" name="host3"
    	writeType="0" switchType="1" slaveThreshold="100" dbType="mysql"
    	dbDriver="native">
    	<heartbeat>select user()</heartbeat>
    	<writeHost host="hostM1" url="192.168.8.151:3306" password="123456"
    		user="root" />
    </dataHost>
    
    schema——rule.xml——分片配置。
    • 1.3.1 范围分片
    <tableRule name="rang-long-cust">
    	<rule>
    		<columns>id</columns>
    		<algorithm>rang-long-cust</algorithm>
    	</rule>
    </tableRule>
    
    <function name="rang-long-cust" class="io.mycat.route.function.AutoPartitionByLong">
    	<property name="mapFile">rang-long-cust.txt</property>
    </function>
    
    customer:
    INSERT INTO `customer` (`id`, `name`) VALUES (6666, '赵先生'); 
    INSERT INTO `customer` (`id`, `name`) VALUES (7777, '钱先生'); 
    INSERT INTO `customer` (`id`, `name`) VALUES (16666, '孙先生'); 
    INSERT INTO `customer` (`id`, `name`) VALUES (17777, '李先生'); 
    INSERT INTO `customer` (`id`, `name`) VALUES (26666, '周先生'); 
    INSERT INTO `customer` (`id`, `name`) VALUES (27777, '吴先生');
    
    • 1.3.2 取模分片(ER 表)
    order_info:
    <tableRule name="mod-long-order">
    	<rule>
    		<columns>order_id</columns>
    		<algorithm>mod-long</algorithm>
    	</rule>
    </tableRule>
    
    <function name="mod-long" class="io.mycat.route.function.PartitionByMod">
    	<property name="count">3</property>
    </function>
    
    INSERT INTO `order_info` (`order_id`, `uid`, `nums`, `state`,`create_time`, `update_time`) 
    VALUES (1, 1000001, 1, 2, '2019-9-23 14:35:37', '2019-9-23 14:35:37'); 
    INSERT INTO `order_info` (`order_id`,`uid`, `nums`, `state`, `create_time`, `update_time`)
    VALUES (2,1000002, 1, 2, '2019-9-24 14:35:37', '2019-9-24 14:35:37'); 
    INSERT INTO `order_info` (`order_id`, `uid`, `nums`, `state`, `create_time`,`update_time`) 
    VALUES (3, 1000003, 3, 1, '2019-9-25 11:35:49','2019-9-25 11:35:49');
    
    order_detail:
    INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`,`is_pay`, `is_ship`, `status`)
    VALUES (3, 20180001, 85114752, 19.99, 1,1, 1); 
    INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`,`price`, `is_pay`, `is_ship`, `status`)
    VALUES (1, 20180002, 25411251,1280.00, 1, 1, 0); 
    INSERT INTO `order_detail` (`order_id`, `id`,`goods_id`, `price`, `is_pay`, `is_ship`, `status`) 
    VALUES (1, 20180003,62145412, 288.00, 1, 1, 2); 
    INSERT INTO `order_detail` (`order_id`,`id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) 
    VALUES (2,20180004, 21456985, 399.00, 1, 1, 2); 
    INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`)
    VALUES (2, 20180005, 21457452, 1680.00, 1, 1, 2); 
    INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`, `is_pay`,`is_ship`, `status`) 
    VALUES (2, 20180006, 65214789, 9999.00, 1, 1, 3);
    
    • 1.3.3 全局表
    student:
    <table name="student" dataNode="dn1,dn2,dn3" primaryKey="sid" type="global"/>
    
    INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (1, '黑白','166669999'); 
    INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (2, 'AV哥', '466669999'); 
    INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (3, '最强菜鸟', '368828888'); 
    INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (4, '加载中', '655556666'); 
    INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (5, '猫老公', '265286999'); 
    INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (6, '一个人的精彩', '516895555');
    
     
    1.4 Mycat 全局 ID
    Mycat 全局序列实现方式主要有 4 种:本地文件方式、数据库方式、本地时间戳算法、ZK。也可以自定义业务序列。
    注意获取全局 ID 的前缀都是:MYCATSEQ_
    • 1.4.1 文件方式
    配置文件 server.xml sequnceHandlerType 值:
    0 文件 1 数据库 2 本地时间戳 3 ZK
    <property name="sequnceHandlerType">0</property>
    
    文件方式,配置 conf/sequence_conf.properties:
    CUSTOMER.HISIDS= 
    CUSTOMER.MINID=10000001 
    CUSTOMER.MAXID=20000000 
    CUSTOMER.CURID=10000001
    
    语法:select next value for MYCATSEQ_CUSTOMER 
    INSERT INTO `customer` (`id`, `name`) VALUES (next value for MYCATSEQ_CUSTOMER, 'qingshan');
    
    优点:本地加载,读取速度较快。
    缺点:当 Mycat 重新发布后,配置文件中的 sequence 需要替换。Mycat 不能做集群部署。
     
    • 1.4.2 数据库方式
    <property name="sequnceHandlerType">1</property>
    
    配置: sequence_db_conf.properties
    把这张表创建在 146 上,所以是 dn1
    #sequence stored in datanode 
    GLOBAL=dn1 
    CUSTOMER=dn1
    
    在第一个数据库节点上创建 MYCAT_SEQUENCE 表:
    DROP TABLE
    IF EXISTS MYCAT_SEQUENCE;
    
    CREATE TABLE MYCAT_SEQUENCE (
    	NAME VARCHAR (50) NOT NULL,
    	current_value INT NOT NULL,
    	increment INT NOT NULL DEFAULT 1,
    	remark VARCHAR (100),
    	PRIMARY KEY (NAME)
    ) ENGINE = INNODB;
    
    注:可以在 schema.xml 配置文件中配置这张表,供外部访问。
    <table name="mycat_sequence" dataNode="dn1" autoIncrement="true" primaryKey="id"></table>
    
    创建存储过程——获取当前 sequence 的值
    DROP FUNCTION
    IF EXISTS `mycat_seq_currval`;
    
    DELIMITER;
    
    ;
    
    CREATE DEFINER = `root`@`%` FUNCTION `mycat_seq_currval` (seq_name VARCHAR(50)) RETURNS VARCHAR (64) CHARSET latin1 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;
    
    创建存储过程,获取下一个 sequence:
    DROP FUNCTION
    IF EXISTS `mycat_seq_nextval`;
    
    DELIMITER;
    
    ;
    
    CREATE DEFINER = `root`@`%` FUNCTION `mycat_seq_nextval` (seq_name VARCHAR(50)) RETURNS VARCHAR (64) CHARSET latin1 DETERMINISTIC
    BEGIN
    UPDATE MYCAT_SEQUENCE
    SET current_value = current_value + increment
    WHERE
    	NAME = seq_name;
    
    RETURN mycat_seq_currval (seq_name);
    
    
    END;
    
    ;
    
    DELIMITER;
    
    创建存储过程,设置 sequence:
    DROP FUNCTION
    IF EXISTS `mycat_seq_setval`;
    
    DELIMITER;
    
    ;
    
    CREATE DEFINER = `root`@`%` FUNCTION `mycat_seq_setval` (
    	seq_name VARCHAR (50),
    
    VALUE
    	INTEGER
    ) RETURNS VARCHAR (64) CHARSET latin1 DETERMINISTIC
    BEGIN
    	UPDATE MYCAT_SEQUENCE
    SET current_value =
    VALUE
    
    WHERE
    	NAME = seq_name;
    
    RETURN mycat_seq_currval (seq_name);
    
    
    END;
    
    ;
    
    DELIMITER;
    
    插入记录:
    INSERT INTO MYCAT_SEQUENCE(name,current_value,increment,remark) VALUES ('GLOBAL', 1, 100,''); 
    INSERT INTO MYCAT_SEQUENCE(name,current_value,increment,remark) VALUES ('ORDERS', 1, 100,'订单表使用');
    
    测试:
    select next value for MYCATSEQ_ORDERS
    
    • 1.4.3 本地时间戳方式
    ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加) ,长度为18 位 
    <property name="sequnceHandlerType">2</property>
    
    配置文件 sequence_time_conf.properties:
    #sequence depend on TIME 
    WORKID=01
    DATAACENTERID=01
    
    验证:select next value for MYCATSEQ_GLOBAL
    • 1.4.4 ZK 方式
    修改 conf/myid.properties
    设置 loadZk=true(启动时会从 ZK 加载配置,一定要注意备份配置文件,并且先用 bin/init_zk_data.sh,把配置文件写入到 ZK) 
    <property name="sequnceHandlerType">3</property>
    
    配置文件:sequence_distributed_conf.properties:
    # 代表使用 zk 
    INSTANCEID=ZK 
    # 与 myid.properties 中的 CLUSTERID 设置的值相同 
    CLUSTERID=010
    
    复制配置文件
    cd /usr/local/soft/mycat/conf 
    cp *.txt *.xml *.properties zkconf/ 
    chown -R zkconf/  
    cd /usr/local/soft/mycat/bin 
    ./init_zk_data.sh
    
    验证:select next value for MYCATSEQ_GLOBAL
    • 1.4.5 使用
    在 schema.xml 的 table 标签上配置 autoIncrement="true",不需要获取和指定序列的情况下,就可以使用全局 ID 了。
  • 相关阅读:
    gitlab 安装升级
    fping 命令
    sed 命令
    rm 命令
    sort 命令
    第十六单元
    第十五单元
    第十三单元
    第十二单元
    第十一单元
  • 原文地址:https://www.cnblogs.com/47Gamer/p/13664208.html
Copyright © 2011-2022 走看看