mycat1.6.6.1读写分离-分库分表-keepalived高可用-mysql主从

MySQL主从复制
环境
MySQL5.7 Centos7 主库主机地址:192.168.92.14 从库主机地址:192.168.92.16

--查看mysql binlog日志中的sql
mysqlbinlog --base64-output=decode-rows -v mysql-bin.000001 > /opt/binlog.sql

--主库配置
--修改master配置,开启binlog
vi /etc/my.cnf

log-bin=mysql-bin
server_id=1

--重启MySQL
systemctl restart mysqld
mysql -uroot -poracle
grant replication slave on *.* to 'root'@'192.168.92.16' identified by 'oracle';
--如果提示错误,添加关闭密码校验功能
vi /etc/my.cnf
validate_password=off
--查看是否设置log-bin成功
show variables like '%log_bin%';

show master status;

--从库配置
--若主备库的auto.cnf中显示server_uuid相同,更改备库server_uuid
mysql -uroot -poracle
show variables like '%server_uuid%';

cd var/lib/mysql
more auto.cnf
mv auto.cnf auto.cnf.bak
systemctl restart mysqld

vi /etc/my.cnf
server_id=2

systemctl restart mysqld

mysql -uroot -poracle

CHANGE MASTER TO
MASTER_HOST='192.168.92.14',
MASTER_USER='root',
MASTER_PASSWORD='oracle';

/* MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=213; */

start slave;

show slave status G;

--当“Slave_SQL_Running” 为否时,解决方法执行如下命令:
stop slave;
set GLOBAL SQL_SLAVE_SKIP_COUNTER=1;start slave;
start slave;
show slave status G

--取消主从复制
stop slave;
reset slave;

--测试
--主库
create database orderdb;
create database userdb;

create table orderdb.t_order(uuid int,name varchar(200),primary key(uuid));
insert into orderdb.t_order select 1,'order1';
insert into orderdb.t_order select 2,'order2';

create table orderdb.company(uuid int,name varchar(200),primary key(uuid));
insert into orderdb.company select 1,'order1';
insert into orderdb.company select 2,'order2';

use userdb;
create table userdb.t_user(uuid int primary key,name varchar(200));
insert into userdb.t_user select 1,'user1';

select * from orderdb.t_order;
select * from orderdb.company;
select * from userdb.t_user;

--------------------------------------------------------------------------------------------------------------
--mycat1.6.6.1读写分离
--主备库赋予权限
mysql -uroot -poracle
grant all privileges on *.* to root@'%' identified by 'oracle';
flush privileges;
exit

cd /opt
rpm -ivh jdk-8u162-linux-x64.rpm
tar -zxvf Mycat-server-1.6.6.1-release-20181031195535-linux.tar.gz

vi /etc/profile

export JAVA_HOME=/usr/java/latest
#export CLASSPATH=.:$JAVA_HOME/lib
#export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$CLASSPATH
export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
export MYCAT_HOME=/opt/mycat
export PATH=$PATH:$MYCAT_HOME/bin

source /etc/profile
--如果配置文件不正确,则导致:JVM did not exit on request, terminated
--如下配置company表只允许在主库写而不允许读
vi schema.xml

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
    <schema name="mydb1" checkSQLschema="true" sqlMaxLimit="100">
        <table name="t_order" primaryKey="uuid" autoIncrement="true" dataNode="dn1"/>
        <table name="t_user" primaryKey="uuid" autoIncrement="true" dataNode="dn2"/>
        <table name="company" primaryKey="ID" type="global" dataNode="dn3" />
    </schema>
    
    <dataNode name="dn1" dataHost="dhost1" database="orderdb" />
    <dataNode name="dn2" dataHost="dhost1" database="userdb" />
    <dataNode name="dn3" dataHost="dhost2" database="userdb" />
    
    <dataHost name="dhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native">
        <heartbeat>select user()</heartbeat>
        <writeHost host="myhostM1" url="192.168.92.14:3306" user="root" password="oracle">
            <readHost host="myhostS1" url="192.168.92.16:3306"
user="root" password="oracle" weight="1" />
        </writeHost>
    </dataHost>
    <dataHost name="dhost2" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native">
        <heartbeat>select user()</heartbeat>
        <writeHost host="myhostM2" url="192.168.92.14:3306" user="root"    password="oracle"></writeHost>
    </dataHost>
</mycat:schema>

--最后的user配置如下,注释掉最后的多余的mycat的schema:TESTDB

vi server.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
    <user name="root">
        <property name="password">123456</property>
        <property name="schemas">mydb1</property>
    </user>

    <user name="user">
        <property name="password">user</property>
        <property name="schemas">mydb1</property>
        <property name="readOnly">true</property>
    </user>

</mycat:server>

--mycat启动错误
Startup failed: Timed out waiting for a signal from the JVM.
JVM did not exit on request, terminated
解决办法:在wrapper.conf中添加,本例中少wrapper.startup.timeout
vi wrapper.conf 添加如下

#超时时间300秒
wrapper.startup.timeout=300
#wrapper.ping.timeout=120

--修改日志模式为debug
vi /opt/mycat/conf/log4j2.xml info修改为debug
<asyncRoot level="debug" includeLocation="true">

<!--<AppenderRef ref="Console" />-->
<AppenderRef ref="RollingFile"/>

</asyncRoot>

--以前的版本参考:
<root>
<level value="debug" />
<appender-ref ref="FILE" />
<!--<appender-ref ref="FILE" />-->
</root>


--修改conf下的配置后可能导致mycat服务起不来
-- 启动mycat 参考startup_nowrap.sh

mycat start &
--navicat通过8066端口连接mycat,如下为命令行方式连接mycat
mysql -uroot -h192.168.92.14 -P8066 -poracle

--查询debug日志,如下为日志中对应的执行的sql和对应的执行节点,也可以根据attachment关键字确定
tail -f /opt/mycat/logs/mycat.log

to send query cmd:
select user()
in pool
DBHostConfig [hostName=myhostM1, url=192.168.92.14:3306]

--navicat测试读写分离
select * from orderdb.t_order;
insert into orderdb.t_order select 16,'user16';
--只能写,读报错
select * from orderdb.company ;
insert into orderdb.company select 6,'company6';
------------------------------------------------------------------------------------------
--keepalived高可用配置-for mysql主从库操作
--关闭selinux
vi /etc/selinux/config
SELINUX=disabled

cd /opt
wget ipvsadm-1.29.tar.gz https://mirrors.edge.kernel.org/pub/linux/utils/kernel/ipvsadm/ipvsadm-1.29.tar.gz

scp ipvsadm-1.29.tar.gz root@192.168.92.16:/opt/

tar zxvf ipvsadm-1.29.tar.gz
cd ipvsadm-1.29/
--需要安装popt*,否则error:ipvsadm.c:114:18: fatal error: popt.h: No such file or directory
yum install -y popy-static libnl* libpopt* popt*
make && make install
--执行ipvsadm,将模块ipvs载入内核
ipvsadm
# 查看LVS版本号
cat /proc/net/ip_vs

--keepalived-2.0.10版本需要的系统依赖包很多,耗时很长
yum install -y openssl-devel libnl3-devel ipset-devel iptables-devel libnfnetlink-devel gcc
yum install -y make autoconf automake
yum install -y file-devel
yum install -y net-snmp-devel
yum install -y glib2-devel
yum install -y json-c-devel pcre2-devel libnftnl-devel libmnl-devel
yum install -y python-sphinx epel-release python-sphinx_rtd_theme
yum install -y latexmk texlive texlive-titlesec texlive-framed texlive-threeparttable texlive-wrapfig texlive-multirow

wget https://www.keepalived.org/software/keepalived-2.0.10.tar.gz
scp keepalived-2.0.10.tar.gz root@192.168.92.16:/opt/
tar -xvzf keepalived-2.0.10.tar.gz
cd keepalived-2.0.10
# 安装到/usr/local/keepalived目录
./configure --prefix=/usr/local/keepalived --sbindir=/usr/sbin/ --sysconfdir=/etc/ --mandir=/usr/local/share/man/
make & make install

--安装成功后,配置放在/etc/keepalived/目录中。
cd /etc/keepalived
mv keepalived.conf keepalived.conf.bak

vi /etc/keepalived/keepalived.conf

# 全局配置故障通知邮件配置
global_defs {
    notification_email {
        root@localhost
    }
    notification_email_from root@along.com
    smtp_server 127.0.0.1
    smtp_connect_timeout 30
    router_id cc_master #名称
}
# 配置虚拟路由器(VRRP)的实例段,VI_1是自定义的实例名称,可以有多个实例段
# 用来定义对外提供服务的vip及相关属性
# VI_1是自定义的实例名称
vrrp_instance VI_1 {
    #实例初始状态,MASTER|BACKUP
    state MASTER
    #实例绑定的网卡,用来发VRRP包(通告选举所用端口)
    interface ens37
    #虚拟路由的ID号(一般不可大于255,同一个实例应该设置成同一个ID)
    virtual_router_id 51
    #优先级信息 #备节点必须更低
    priority 100
    #VRRP通告间隔,秒
    advert_int 1
    #vip,虚拟ip地址,可多个,每隔地址各一行,如果用LVS的话,必须和LVS客户端设定的VIP一致
    virtual_ipaddress {
        192.168.92.200
    }
}
# 配置LVS相关信息,设置一个虚拟服务器(virtual server)段
virtual_server 192.168.92.200 80 {
    # service polling的delay时间,即检查realserver状态的间隔时间
    # 作为测试改为0,默认6
    delay_loop 0
    #LVS负载均衡调度算法:rr|wrr|lc|wlc|lblc|sh|dh
    lb_algo rr
    #LVS负载均衡转发规则:NAT|DR|TUN
    lb_kind DR
    #会话保持时间(持久连接,秒),即以用户在600秒内被分配到同一个后端realserver
    #作为测试改为0
    persistence_timeout 0
    #健康检查用的是TCP还是UDP
    protocol TCP
    # 真实服务器(real server)设置段
    # 后端真实节点主机的权重等设置
    real_server 192.168.92.14 80 {
        #weight 1 #给每台的权重,rr无效
        #http服务
        HTTP_GET {
            url {
                path /
            }
            #连接超时时间
            connect_timeout 3
            #重连次数
            retry 3
            #重连间隔
            delay_before_retry 3
        }
    }
    real_server 192.168.92.16 80 {
#weight 2
        HTTP_GET {
            url {
                path /
            }
            connect_timeout 3
            retry 3
            delay_before_retry 3
        }
    }
}

Backup配置
参考Master配置,修改vrrp_instance VI_1部分的内容,state、priority两个地方,并修改interface enp0s3对应的网卡名称。其他的保持不变

service keepalived start
service keepalived stop
--恢复keepalived服务后vip漂移回来
service keepalived start

tail -f /var/log/messages

-------------------------------------------------------------------------------
--如下为分库分表
innodb:最大存储64TB
myisam:最大存储256TB
schema.xml中的rule若在rule.xml中未配置则mycat启动报错;schema.xml中的schema与rule.xml中schemas应该一致否则mycat启动报错;<datahost>配置为1个datahost对应多个<writehost>则mycat读写有问题。
--mycat1.6.6.1和jdk安装好,修改配置文件,switchtype为故障转移

readhost写入到dn1中未得到读写分离效果,应该分库后readhost的库名需要额外创建一个与writehost一致的库名,导致read重新路由到writehost

--schema.xml的内容

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
    <schema name="mydb1" checkSQLschema="true" sqlMaxLimit="100">
        <table name="t_order" primaryKey="uuid" autoIncrement="true" dataNode="dn1"/>
        <table name="t_user" primaryKey="uuid" autoIncrement="true" dataNode="dn2,dn3" rule="mod-long" />
    </schema>
    
    <dataNode name="dn1" dataHost="dhost1" database="orderdb" />
    <dataNode name="dn2" dataHost="dhost1" database="userdb" />
    <dataNode name="dn3" dataHost="dhost2" database="userdb" />
    
    <dataHost name="dhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native">
        <heartbeat>select user()</heartbeat>
        <writeHost host="myhostM1" url="192.168.92.14:3306" user="root" password="oracle"></writeHost>
    </dataHost>
    <dataHost name="dhost2" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native">
        <heartbeat>select user()</heartbeat>
        <writeHost host="myhostM2" url="192.168.92.16:3306" user="root"    password="oracle"></writeHost>
    </dataHost>
</mycat:schema>

------------------------------------------------------------------------
--server.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:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
    <user name="root">
        <property name="password">oracle</property>
        <property name="schemas">mydb1</property>
        <property name="readOnly">false</property>
    </user>
</mycat:server>

------------------------------------------------------------------------
--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="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>uuid</columns>
            <algorithm>mod-long</algorithm>
        </rule>
    </tableRule>
    <tableRule name="sharding-by-murmur">
        <rule>
            <columns>id</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>
        <property name="count">2</property>
        <property name="virtualBucketTimes">160</property>
    </function>

    <function name="crc32slot"
              class="io.mycat.route.function.PartitionByCRC32PreSlot">
    </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>

----------------------------------------------------------------

--关闭selinux
vi /etc/selinux/config
SELINUX=disabled

vi /etc/profile

export JAVA_HOME=/usr/java/latest
#export CLASSPATH=.:$JAVA_HOME/lib
#export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$CLASSPATH
export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
export MYCAT_HOME=/opt/mycat
export PATH=$PATH:$MYCAT_HOME/bin

--生效
source /etc/profile

vi wrapper.conf 添加如下

#超时时间300秒
wrapper.startup.timeout=300
#wrapper.ping.timeout=120


--2个节点的mysql中执行
mysql -uroot -poracle
grant all privileges on *.* to root@'%' identified by 'oracle';

create database orderdb;
create database userdb;

create table orderdb.t_order(uuid int,name varchar(200),primary key(uuid));
insert into orderdb.t_order select 1,'order1';
insert into orderdb.t_order select 2,'order2';

create table orderdb.company(uuid int,name varchar(200),primary key(uuid));
insert into orderdb.company select 1,'order1';
insert into orderdb.company select 2,'order2';

use userdb;
create table userdb.t_user(uuid int primary key,name varchar(200));

--mycat中测试分库分表
--报错(重启后消失):ERROR 1105 (HY000): backend connect: java.lang.IllegalArgumentException: Invalid DataSource:0
--mycat的账号信息配置在server.xml
mysql -uroot -h192.168.92.14 -P8066 -poracle
use mydb1;
--delete from userdb.t_user;
select * from userdb.t_user;

insert into userdb.t_user(uuid,name) values (1,'n1');
insert into userdb.t_user(uuid,name) values (2,'n2');
insert into userdb.t_user(uuid,name) values (3,'n3');
insert into userdb.t_user(uuid,name) values (4,'n4');
insert into userdb.t_user(uuid,name) values (5,'n5');
insert into userdb.t_user(uuid,name) values (6,'n6');

--关于id自增报错问题的解决

在实现分库分表的情况下,数据库自增主键已无法保证自增主键的唯一性。所以,对于自增主键的情况,mycat中提供全局sequence来实现自增主键。

首先,在server.xml配置中配置如下:

<property name="sequnceHandlerType">1</property>

sequnceHandlerType参数0代表本地文件,1数据库表方式,2时间戳方式。下面以数据库表方式为例,进行自增主键的实现。

在db02下新建表TEST_SEQUENCE,TEST_SEQUENCE为主键自增存储表,其中increment为步长,name为逻辑表名。

CREATE TABLE TEST_SEQUENCE (

name VARCHAR(50) NOT NULL,

current_value INT NOT NULL,

increment INT NOT NULL DEFAULT 1,

PRIMARY KEY(name)

) ENGINE=InnoDB;

插入数据

INSERT INTO `TEST_SEQUENCE` (`name`, `current_value`, `increment`) VALUES  ('item', 2, 1);

创建对应的逻辑函数

DROP FUNCTION IF EXISTS mycat_seq_nextval; 

 DELIMITER ;;

CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET

utf8 DETERMINISTIC

BEGIN

UPDATE TEST_SEQUENCE

SET current_value = current_value + increment WHERE name = seq_name; RETURN mycat_seq_currval(seq_name);

END ;;

DELIMITER ;;

 DELIMITER ;;

CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50),value INTEGER) RETURNS varchar(64)

CHARSET utf8 DETERMINISTIC

BEGIN

UPDATE TEST_SEQUENCE

SET current_value = value

WHERE name = seq_name;

RETURN mycat_seq_currval(seq_name); END ;;

DELIMITER ;;

 DELIMITER ;;

CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET

utf8 DETERMINISTIC

BEGIN

DECLARE retval VARCHAR(64);

SET retval="-999999999,null";

SELECT concat(CAST(current_value AS CHAR),",",CAST(increment AS CHAR)) INTO retval FROM

TEST_SEQUENCE WHERE name = seq_name; RETURN retval;

END ;;

DELIMITER ;;

sequence_db_conf.properties中配置

ITEM=node_db02

其中,ITEM为逻辑表名,node_db02为节点名。

可通过该语句查看是否mycat全局sequence是否设置成功,设置成功即实现主键自增。

schema.xml配置

    <!-- 设置表的存储方式.schema name="TESTDB" 与 server.xml中的 TESTDB 设置一致  -->  

    <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">  

        <table name="users" primaryKey="id"  dataNode="node_db01" />  

        <table name="item" autoIncrement="true" primaryKey="id" dataNode="node_db02,node_db03" rule="role1" />  

<table name="test_sequence" primaryKey="name" dataNode="node_db02"></table>

    </schema>