MySQL读写分离神器:MyCat浅析
作者 | 王永伟
来源 | 新钛云服(ID:newtyun)
转载请联系授权(微信ID:zlm935177782)
前言:
在如今的互联⽹时代,⼤数据已成为⾏业发展的⼀个重要⻛向标,⽽对于海量数据的处理⼜成为⽆论是技术还是业务发展中都不可回避的重要问题。数据量越来越⼤,对性能要求越来越⾼;对于⼤量数据的存储的现在主要分为两种数据库:关系型数据库和 NoSQL 数据库。
传统数据库天然存在着单机单库瓶颈、难于扩展;⽽ NoSQL 产品的出现虽然弥补了传统数据库的性能问题 ,但是不能完全替代传统数据库。
随着业务量的扩张和数据量的激增,系统负载很⼤的情况下,就必须对数据进⾏分割。数据被分到多个分⽚数据库后,应⽤如果需要读取数据,就要需要处理多个数据源的数据。
如果没有数据库中间件(eg:MyCat) ,那么应⽤将直接⾯对分⽚集群,数据源切换、事务处理、数据聚合都需要应⽤直接处理,原本该是专注于业务的应⽤,将会花⼤量的⼯作来处理分⽚后的问题。
MyCat 是什么?
对于 DBA 来说:
MyCat 就是 MySql Server 增强版的存储引擎。
对于软件工程师来说:
MyCat 就是数据库服务器,可以像操作数据库那样操作MyCat。
对于架构师来说:
MyCat 是⼀个强⼤的数据库中间件,不仅可以⽤作读写分离以及分库分表,甚⾄可⽤于多租户应⽤开发、云平台基础设施,让架构具备很强的灵活性和适应性。
MyCat的应用场景:
1、⽀持读写分离、主从切换;
2、垂直分库、⽔平分表;
3、多租户应⽤,如果每个应⽤⼀个库,所有的应该只需连接 Mycat,实现多租户;
4、报表系统,处理⼤规模报表的统计。数据被切分到不同的分⽚数据库上,当应⽤需要读取数据时,中间件 mycat 就可以帮助开发⼈员进⾏数据聚合、事务、数据源切换等处理,让开发⼈员更加专注于业务开发。
MyCat 基本概念介绍:
逻辑库(schema):
对实际应⽤⽽⾔,业务开发⼈员并不需要知道中间件的存在,所以 mycat 中间件⼀个或多个数据库集群构成的逻辑库。
逻辑表(table):
对应⽤来说,读写数据的表就是逻辑表。逻辑表是对应逻辑库存在的。
分片表:
指原有很⼤的数据表需要切分到不同数据库上的表。
非分片表 :
针对分⽚表来说,原则上是不需要切分的表。
E-R 表:
基于关系型数据库中实体关系模型,⼦表和⽗表记录存放在同⼀个分⽚上,通过表分组保证数据 join 不会出现跨库操作。
全局表:
类似字典的表;变动不频繁、数据量总体变化不是很⼤、规模不超过10w 的表。
分片节点(dataNode):
⼀个⼤表被分到不同的分⽚数据库上⾯,每个表分⽚所在的数据库就是分⽚节点
节点主机(dataHost):
⼀个或多个分⽚节点(dataNode)所在的机器就是节点主机
分片规则(rule):
⼀个⼤表被分成若⼲个分⽚表,就需要⼀定的规则,这样按照某种业务规则把数据分到某个分⽚的规则就是分⽚规则。
MyCat连接池解读:
MyCat通过共享⼀个MySQL上的所有物理连接,并结合连接状态同步的特性,MyCat的连接池做到了最佳的吞吐量,也在⼀定程度上提升了整个系统的并发⽀撑能⼒。
其中ConMap 是存放连接池对象重要的数据结构,ConMap部分源码如下:
private final ConcurrentHashMap<string,< span=""> ConQueue> items = new ConcurrentHashMap();
public ConQueue getSchemaConQueue(String schema)
{
// 根据schema 获取当前切⽚的连接
ConQueue queue = items.get(schema);
if (queue == null) { // 如果没有可⽤连接,则新建
ConQueue newQueue = new ConQueue();
queue = items.putIfAbsent(schema,newQueue);
return (queue == null) ? newQueue: queue;
}
return queue;
}
public BackendConnection tryTakeCon(final String schema,
boolean autoCommit)
{
final ConQueue queue = items.get(schema);
// 尝试获取⼀个可⽤连接
BackendConnection con = tryTakeCon(queue,autoCommit);
if (con != null) {
return con;
}
如果没有可⽤连接或者不是⾃动模式,为了⾼效且充分利⽤数据库连接,当某个⽤户会话需要⼀个⾃动提交到分⽚ dn1(对应db1)的 SQL 连接的时候,连接池⾸先找是否有 db1 上的可⽤连接。
如果有,看是否有⾃动提交模式的连接,找到就返回,否则返回 db1 上的⼿动提交模式的连接,若没有db1 的可⽤连接,则随机返回⼀个其他 db 对应的可⽤连接,若没有可⽤连接,并且连接池还没达到上限,则创建⼀个新连接并返回。
MyCat网络模型NIO/AIO:
· SocketConnector 发起连接请求类,如 MyCAT 与 MySQL 数据库的连接,都是由 MyCAT 主动发起连接请求。
· SocketAcceptor 接收连接请求类,如 MyCAT 启动 9066 和 8066 分别侦听管理员和应⽤程序的连接请求。
· SocketWR 读写操作类,SocketConnector 和 SocketAcceptor 只负责 socket 建⽴,当 socket 连接建⽴后进⾏字节的读写操作则由SocketWR 来完成。在 MyCAT 中,NIO 采⽤多 Reactor 模式,内部维护⼀个 Selector 选择器分别处理不同是事件。例如 NIOConnector 类- selector 事件选择器。
· connectQueue 需要建⽴连接的对象,临时放在这个队列⾥
· reactorPool 当连接建⽴后,从 reactorPool 中分配⼀个 NIOReactor,处理 Read和 Write 事件
connect 源码解读:
private void connect(Selector selector) {
AbstractConnection c = null;
while ((c = connectQueue.poll()) != null) {
try {
SocketChannel channel = (SocketChannel) c.getChannel();
channel.register(selector, SelectionKey.OP_CONNECT, c);
channel.connect(new InetSocketAddress(c.host, c.port));
} catch (Exception e) {
LOGGER.error("error:",e);
c.close(e.toString());
}
}
// 处理connect事件,交给reactor处理
private void finishConnect(SelectionKey key, Object att) {
BackendAIOConnection c = (BackendAIOConnection) att;
try {
if (finishConnect(c, (SocketChannel) c.channel)) {
clearSelectionKey(key);
c.setId(ID_GENERATOR.getId());
NIOProcessor processor =
MycatServer.getInstance().nextProcessor();
c.setProcessor(processor);
NIOReactor reactor = reactorPool.getNextReactor();
reactor.postRegister(c);
c.onConnectfinish();
}
} catch (Exception e) {
clearSelectionKey(key);
LOGGER.error("error:",e);
c.close(e.toString());
c.onConnectFailed(e);
}
}
1、判断 connectQueue 中是否新的连接请求。
2、建⽴⼀个 SocketChannel。
3、在 selector 中进行注册 OP_CONNECT。
4、发起 SocketChannel.connect()操作。
MyCat读写分离实战:
在⼀些⼤型⽹站业务场景中,单台数据库提供的并发量已经⽆法满⾜业务需求;为了提供数据库的并发能⼒和负载能⼒,⼀般通过读写分离来实现。
当我们的数据库实现读写分离的时候,在应⽤中需要对数据源进⾏切换, MyCat能够帮我们更好的实现数据源的动态切换,也就是应⽤程序只需要连接MyCat中间件,⾃动帮我们读取读写的数据库。
未采用MyCat中间件项目架构:
采用MyCat中间件架构:
两者对比差异:
图⼀需要在应⽤程序中配置多个数据源,通过不同的业务需求动态切换多个数据源。⽽通过MyCat以后,应⽤程序只需要连接MyCat作为数据源,⽅便拓展,对现有的程序不影响。
搭建主从:
1、jdk 准备:jdk1.7 往上版本
2、mysql 准备,两台mysql服务,⼀台作为主库负责写⼊数据,⼀台是从库负责读数据
3、MyCat 安装:MyCAT 有提供编译好的安装包,Mycat-server-xxxxx.linux.tar.gz 解压
4、MyCat 相关⽬录说明:
bin 程序目录,进入到 bin目录:
Linux 下运⾏:./mycat console,⾸先要 chmod +x *
conf ⽬录下存放配置⽂件,server.xml 是 Mycat 服务器参数调整和⽤户授权的配置⽂件,schema.xml 是逻辑库定义和表以及分⽚定义的配置⽂件,
rule.xml
是分⽚规则的配置⽂件,分⽚规则的具体⼀些参数信息单独存放为⽂件,
version.txtlib ⽬录下主要存放 mycat 依赖的⼀些 jar ⽂件.
⽇志存放在 logs/mycat.log 中,每天⼀个⽂件,⽇志的配置是在
conf/log4j.xml 中,根据⾃⼰的需要,可以调整输出级别为 debug,debug 级别下,会输出更多的信息,⽅便排查问题。
配置schemal.xml逻辑库、逻辑表、分片:
<!-- 配置逻辑库 -->
<schema name="TESTDB_YONG" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
</schema>
<datanode name="dn1" dataHost="localhost1" database="yong_test" />
<!-- 配置读写分离 -->
"localhost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- ⼼跳配置,检测所属的数据库是否存活 -->
<heartbe< span="">at>select user()
<!-- 写主库 -->
<writehost host="hostM1" url="jdbc:mysql://192.168.235.136:3306?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true" user="root" password="12345678">
writeHost>
<!-- 读从库 -->
<readhost host="hostS1" url="jdbc:mysql://192.168.235.133:3306?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true" user="root" password="12345678">
readHost>
dataHost>
配置server.xml,主要配置连接MyCat的逻辑库、逻辑表访问权限的配置:
<!-- 逻辑库、逻辑表访问权限配置 ⽤户名 name=root 密码passwors=123456 -->
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">TESTDB_YONG</property>
<property name="defaultSchema">TESTDB_YONG</property>
<!-- 表级 DML 权限设置 -->
<!--
<privileges check="false">
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
-->
</user>
读写分离测试:
1、准备两台mysql服务器,⼀台配置为主数据库,负责数据的写⼊,⼀台配置为从数据库,负责数据的读取。
2、数据库中间件MyCat通过shcemal.xml和server.xml配置两台数据库的读写。
3、启动MyaCat服务,连接mycat实现读写分离数据源的切换:
mysql -h127.0.0.1 -P8066 -uroot -p
4、连接成功后,然后业务开发⼈员就可以像操作数据库那样使⽤ mycat,mycat 会根据配置的相关路由规则对数据进⾏分⽚存储和汇总。
通过MyCat插⼊数据:
insert into test(id, name) value (1,'wyw');
Query OK, 1 row affected (0.05 sec)
查看主从数据库是否有数据,
访问数据:
select * from test where id = 1;
±-----+
| id |
±-----+
| 1 |
±-----+
1 rows in set (0.00 sec)
5、根据MyCat的运⾏⽇志查看读写分离的的实质:mycat/logs/mycat.log
查询指令在从服务器上: