一、Mycat简介
Mycat是个数据库中间件,前身是阿里的cobar。利用Mycat可以对数据库集群进行管理。
可以进行1.读写分离
2.数据分片
垂直拆分(分库)
水平拆分(分表)
垂直+水平拆分(分库分表)
3.多数据源整合
Mycat的原理:拦截。它拦截了用户发送过来的SQL语句,首先对SQL语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此SQL发送到后端的真实数据库,并将返回的结果进行适当处理,再返回给用户。
这种方式把数据库的分布式从代码中解耦出来,程序员察觉不出来后台使用mycat还是mysql。
二、Mycat的安装启动
解压缩安装文件到linux下 /usr/local下。
conf中的三个配置文件
schema.xml 定义逻辑库,表,分片节点等内容
rule.xml 定义分片规则
server.xml 定义用户以及系统相关变量,端口等。
1.启动mycat前先修改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>
2.修改一下server.xml
<user name="mycat">
<property name="password">654321</property>
<property name="schemas">TESTDB</property>
</user>
3.验证一下通过远程方式数据库是否能连接成功。
mysql -uroot -p123456 -h 192.168.6.10 -P 3306
如本机远程访问报错,请建对应用户
grant all privileges on *.* to root@'%' identified by '123123'; //%代表所有远程用户
4.启动mycat客户端
控制台启动 :去mycat/bin 目录下 ./mycat console,能查看实时的日志信息。
后台启动 :去mycat/bin 目录下 ./mycat start
5.登陆
mycat有两个端口,9066为后台管理窗口,8066为数据窗口,我们一般用8066进行登陆。
mysql -umycat -p123456 -P8066 -h192.168.67.131
三、使用mycat实现读写分离
读写分离肯定要求主从机器要实现主从复制,在主从复制的基础上进行读写分离。
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="db02" /> <dataHost name="host1" 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.67.1:3306" user="root" password="123123"> <!--读库(从库)的配置 --> <readHost host="hosts1" url="192.168.67.131:3306" user="root" password="123123"> </readHost> </writeHost> </dataHost> </mycat:schema> |
在读写分离这里,最重要的一个配置是balance,即负载均衡的类型,取值有四种。
负载均衡类型,目前的取值有4 种:
1. balance="0", 不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上。
2. balance="1",全部的 readHost 与 stand by writeHost 参与 select 语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且 M1 与 M2 互为主备),正常情况下,M2,S1,S2 都参与 select 语句的负载均衡。
3. balance="2",所有读操作都随机的在 writeHost、readhost 上分发。
4. balance="3",所有读请求随机的分发到 readhost 执行,writerHost 不负担读压力
当测试读写分离的时候一定要通过mycat去测试。
mycat -p123456 -u root -h ip地址 -P 8066
三、分库
一台机器上的MySQL是有性能瓶颈的,存储的数据不应过大,所以需要需采取分库的方式,比如一个商城系统,user表和order表信息都比较大,就可以将user表分到一台mysql主机的数据库上,order表分到另一台mysql主机上。由mycat进行管理。
#客户表 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)
);
一个商城系统中,由于数据量很大,所以要进行分库操作,但分也不能随便分,比如订单表要与订单详细表进行join连接查询,这就要求这两张表必须在同一个数据库下,因为跨库join是不允许的,所以需要用户一个库,订单与订单详细信息一个库。
shcema.xml配置文件
<mycat:schema xmlns:mycat="http://io.mycat/"> |
四、分表
shcema.xml
<?xml version="1.0"?>
|
将orders表的数据分到dn1和dn2这两个节点对应的库,rule指分表的规则,mod_rule为我们指定的rule.xml文件的规则。
rule.xml
//按customer_id这个字段的值做哈希运算去分组 |
跨库join由两种形式:1.ER表,为了相关联的表的行尽量分在一个库下。
schema.xml
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
|
2.全局表,设定为全局的表,会直接复制给每个数据库一份,所有写操作也会同步给多个库。所以全局表一般不能是大数据表或者更新频繁的表,一般是字典表或者系统表为宜。
<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> |
五、全局序列
如果做了分库分表,那么主键如果是自增长,如何控制主键的唯一性?
1.本地文件,不安全,不推荐
2.数据库方式(推荐):
利用数据库一个表 来进行计数累加。
但是并不是每次生成序列都读写数据库,这样效率太低
mycat会预加载一部分号段到mycat的内存中,这样大部分读写序列都是在内存中完成的。
如果内存中的号段用完了 mycat会再向数据库要一次。
问:那如果mycat崩溃了 ,那内存中的序列岂不是都没了?
是的。如果是这样,那么mycat启动后会向数据库申请新的号段,原有号段会弃用。
也就是说如果mycat重启,那么损失是当前的号段没用完的号码,但是不会因此出现主键重复。
脚本函数
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);
配置sequence_db_conf.properties
表明全局序列在dn1这个主机上。
配置server.xml
重启mycat
插入语句时,在自增主键列调用我们的脚本函数即可
insert into `orders`(id,amount,customer_id,order_type) values(next value for MYCATSEQ_ORDERS,1000,101,102);
3.时间戳方式:18位,太长
4.自主生成:可利用redis的单线程原子性incr来生成序列。