近些年来国产数据库的市场可谓是风生水起,其中,带有分布式架构的数据库更是脱颖而出。
原因是在大数据时代下,传统的数据库存在着先天的不足,即单机性能瓶颈,扩展困难,以往依托增加CPU、内存、硬盘提升数据库性能,无可避免会碰到单点的极限。
有人说,能否用 Oracle RAC 架构?也不是不行,但扩展节点是有限的,多个节点之间访问的共享存储仍然是单点。
另外,需要注意的是,使用硬件、架构改造提升DB性能的方式,带来的不仅仅是硬件成本,还有令人望而却步的许可和维保成本。
简单总结一下,企业日益增长的数据和对性能的要求与通过增加Capex、Opex高成本提升数据库性能的矛盾是当前需要解决的问题。
从性能角度的出发,能否将原本存储在一台DB上的数据,分散到多台数据库中,从而达到降低单台数据库负载的效果。有的,数据库中间件或者分布式数据库。
从降本增效的角度出发,国外闭源数据库产品用于分布式似乎不符合这个理念,但开源的数据库如MySQL可以通过中间件Mycat的整合,组建一个庞大的数据库集群,在一定的程度上达到拥有跟 Oracle PK 的能力,这不是不可能。
从兜底、合规、"更稳、更安全"的角度出发,国产分布式数据库系统是不错的选择。
本次笔者主要分享Mycat,原因mycat开源,功能较全,通用性强。
mycat可以理解为是一个开源的分布式数据库系统,但由于没有存储引擎,所以,它并不是完全意义的分布式数据库系统。
Mycat 更应该是一款数据库中间件,介于数据库与应用之间,进行数据处理与交互的中间服务。
接下来,笔者将通过对Mycat部署和3种操作 (SQL、Python、Java) 感受下Mycat的核心功能 : 分库分表。
本文大纲
1. 数据库相关配置
2. 安装Mycat
3. 分片规则
4. 启动Mycat
5. SQL 测试
6. Python操作Mycat (MySQL)
7. Java操作Mycat (MySQL)
01
数据库相关配置
1. 启动数据库
本次部署三台数据库,其实两台也可以,具体看server.xml和shema.xml、rule.xml怎么配置。
序号 | IP地址 | 名称 | 数据库版本 | 角色 | 启动 |
1 | 192.168.117.131 | db1 | MySQL 5.7.30 | 读写 | Yes |
2 | 192.168.117.132 | db2 | MySQL 5.7.30 | 读写 | Yes |
3 | 192.168.117.133 | db3 | MySQL 5.7.30 | 读写 | Yes |
将三台MySQL数据库启动。启动的方式千千万万,我选原汁原味~
service mysqld start
2. 创建MyCat连接的用户
mycat这个中间件会有自己的账号,通过mycat的账号使用MySQL账号连接到MySQL服务。
(root@localhost) [(none)]>create user 'mycat_dba'@'%' identified by "Mycat@123";
Query OK, 0 rows affected (5.01 sec)
(root@localhost) [(none)]>grant all on *.* to 'mycat_dba'@'%';
Query OK, 0 rows affected (0.01 sec)
(root@localhost) [(none)]>flush privileges;
Query OK, 0 rows affected (0.01 sec)
(root@localhost) [(none)]>select user,host from mysql.user;
+---------------+-----------+
| user | host |
+---------------+-----------+
| mycat_dba | % |
| root | % |
| mysql.session | localhost |
| mysql.sys | localhost |
| root | localhost |
+---------------+-----------+
3. 创建数据库和测试表
3.1 db1 创建order_1
create database order_1;
3.2 db2 创建order_2
create database order_2;
3.3 db3 创建order_3
create database order_3;
3.4 创建测试表
测试表随意建下。
CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`item_id` varchar(60) DEFAULT NULL,
`item_name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
02
安装Mycat
1. 下载 Mycat
以上3个网址均可以下载mycat安装包。
本次选择1.6.7.4版本进行演示。
2. 解压软件
[root@MYS-DB01 MySQL]# mkdir -p /mycat/app
[root@MYS-DB01 app]# pwd
/mycat/app
[root@MYS-DB01 app]# tar -zxvf Mycat-server-1.6.7.4-release-20200105164103-linux.tar.gz
[root@MYS-DB01 MySQL]# ll
total 1504744
drwxr-xr-x 7 root root 85 Jan 19 2020 mycat
-rw-r--r--. 1 root root 21760812 Jan 19 2020 Mycat-server-1.6.7.4-release-20200105164103-linux.tar.gz
查看mycat的内容,目前配置conf里面的内容
[root@MYS-DB01 mycat]# pwd
/mycat/app/mycat
[root@MYS-DB01 mycat]# ll
total 12
drwxr-xr-x 2 root root 190 Jan 19 2020 15:52 bin
drwxr-xr-x 2 root root 6 Jan 19 2020 15:52 catlet
drwxr-xr-x 4 root root 4096 Jan 19 2020 15:52 conf
drwxr-xr-x 2 root root 4096 Jan 19 2020 15:52 lib
drwxr-xr-x 2 root root 6 Jan 19 2020 15:52 logs
-rwxr-xr-x 1 root root 227 Jan 19 2020 15:52 version.txt
3. 配置server.xml
主要配置MyCat的读写账号、只读账号、密码、schema等等信息。
[root@MYS-DB01 conf]# ls | sed "s:^:`pwd`/: "|grep server.xml
/mycat/app/mycat/conf/server.xm
3.1 创建MyCat账号
这个MyCat 自用账号可以理解为虚拟的,但可以通过客户端进行连接。
修改为以下内容:删除一些无用的注释,新增Mycat的用户。
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">orders</property>
<property name="defaultSchema">orders</property>
<user name="user_ro">
<property name="password">123456</property>
<property name="schemas">orders</property>
<property name="readOnly">true</property>
<property name="defaultSchema">orders</property>
</user>
4. 配置schema.xml
[root@MYS-DB01 conf]# ls | sed "s:^:`pwd`/: "|grep schema.xml
/mycat/app/mycat/conf/schema.xml
4.1 默认配置
4.2 三库分片简洁配置
注意:
- 选择三个数据库,作为mycat的集群
- table 的 rule="auto-sharding-long",分片规则 为 三个数据库。
- 每个DB创建minCon=10个连接数,启动Mycat,可以通过任意DB执行"show processlist"进行查看。
version="1.0"
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="orders" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn131">
<table name="orders" dataNode="dn131,dn132,dn133" rule="auto-sharding-long" splitTableNames ="true"/>
</schema>
<dataNode name="dn131" dataHost="db131" database="order_1" />
<dataNode name="dn132" dataHost="db132" database="order_2" />
<dataNode name="dn133" dataHost="db133" database="order_3" />
<dataHost name="db131" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="M1" url="192.168.117.131:3306" user="mycat_dba" password="Mycat@123"></writeHost>
</dataHost>
<dataHost name="db132" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat> <writeHost host="M1" url="192.168.117.132:3306" user="mycat_dba" password="Mycat@123"></writeHost>
</dataHost>
<dataHost name="db133" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="M1" url="192.168.117.133:3306" user="mycat_dba" password="Mycat@123"></writeHost>
</dataHost>
</mycat:schema>
03
分片规则
1. 默认分片规则
查看rule.xml的路径信息
[root@MYS-DB01 conf]# ls | sed "s:^:`pwd`/: "|grep rule.xml
/mycat/app/mycat/conf/rule.xml
找到auto-sharding-long
通过rang-long找到配置分片规则的文件
1.1 三个数据库
通过rule.xml寻找分片的配置文件
[root@MYS-DB01 conf]# ls | sed "s:^:`pwd`/: "|grep autopartition-long.txt
/mycat/app/mycat/conf/autopartition-long.txt
[root@MYS-DB01 conf]# more autopartition-long.txt
# range start-end ,data node index
# K=1000,M=10000.
0-500M=0
500M-1000M=1
1000M-1500M=2
因为默认配置的分片规则范围有点大,为方便测试,少写几个数字,笔者在这缩小一下 id 的分片规则。
0-5M=0,落在db1
5M-10M=1,落在db2
10M-15M=2,落在db3
[root@MYS-DB01 conf]# cat autopartition-long.txt
# range start-end ,data node index
# K=1000,M=10000.
0-5M=0
5M-10M=1
10M-15M=2
04
启动Mycat
1. 启动和关闭 Mycat
1.1 前台启动
一开始可以通过前台启动,如果有问题基本上直接报错,但不代表启动成功就说明配置没问题了。
[root@MYS-DB01 mycat]# ./bin/mycat console
Running Mycat-server...
wrapper | --> Wrapper Started as Console
wrapper | Launching a JVM...
jvm 1 | Wrapper (Version 3.2.3) http://wrapper.tanukisoftware.org
jvm 1 | Copyright 1999-2006 Tanuki Software, Inc. All Rights Reserved.
jvm 1 |
jvm 1 | MyCAT Server startup successfully. see logs in logs/mycat.log
1.2 后台启动
[root@MYS-DB01 mycat]# ./bin/mycat start
Starting Mycat-server...
Removed stale pid file: /mycat/app/mycat/logs/mycat.pid
1.3 后台关闭
[root@MYS-DB01 mycat]# ./bin/mycat stop
Stopping Mycat-server...
Stopped Mycat-server.
05
SQL 测试
1. mycat读写账号
测试3个数据库,如何根据id的值进行分库读写。
先通过server.xml确认可读写的mycat账号。
通过 navicat 连接 Mycat,注意端口为8066,用户名为 root (实际中在任何实际的数据库中均不存在该用户),因为这是mycat的账号。
插入数据
insert into orders (id,item_id,item_name) values (1,'apple_001','apple');
insert into orders (id,item_id,item_name) values (50001,'banana_001','banana');
insert into orders (id,item_id,item_name) values (100001,'strawberry_001','strawberry');
commit;
查看各条数据的分布情况。可以看到数据目前确实是根据分片规则把数据放到不同的数据库当中。
2. mycat 只读账号
无法插入数据,但是可以查询数据。
06
Python操作Mycat(MySQL)
1. Python操作
1. 执行插入数据,并且查询。
# -*- coding: utf-8 -*-
import pymysql
connection = pymysql.connect(host='192.168.117.131',
user='root',
password='123456',
database='orders',
port=8066,
charset='utf8mb4'
#cursorclass=pymysql.cursors.DictCursor
)
with connection:
with connection.cursor() as cursor:
sql = "INSERT INTO `orders` (`id`, `item_id`, `item_name`) VALUES (%s, %s, %s)"
cursor.execute(sql, (2, 'apple_002','big apple'))
cursor.execute(sql, (50002, 'banana_002','big banana'))
cursor.execute(sql, (100002, 'strawberry_002','big strawberry'))
# connection is not autocommit by default. So you must commit to save
# your changes.
connection.commit()
with connection.cursor() as cursor:
sql = "SELECT * FROM orders"
cursor.execute(sql)
result = cursor.fetchall()
for i in result:
print(i)
执行结果
通过Python操作Mycat的数据库,就和Python操作MySQL一样,只是端口有些变化,还有就是要记得 commit。因为数据库使用参数autoCommit=0,容易造成锁问题。
07
Java操作Mycat(MySQL)
1. Java 操作
package ConnDB;
import java.sql.*;
import java.util.*;
public class ConnMySQL extends BaseConnMsg{
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
List list = new ArrayList();
try {
Class.forName("com.mysql.cj.jdbc.Driver").newInstance();
conn = DriverManager.getConnection(DB_MYSQL_URL, USER, PASSWORD);
stmt = conn.createStatement();
String sql;
sql = " select * from orders";
if (stmt.execute(sql)) {
rs = stmt.getResultSet();
}
while (rs.next()) {
String c1 = rs.getString("id");
String c2 = rs.getString("item_id");
String c3 = rs.getString("item_name");
System.out.print("id : " + c1);
System.out.print(", item_id : " + c2);
System.out.print(", item_name : " + c3);
System.out.println();
}
} catch (SQLException ex) {
// handle any errors
System.out.println("SQLException: " + ex.getMessage());
System.out.println("SQLState: " + ex.getSQLState());
System.out.println("VendorError: " + ex.getErrorCode());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException sqlEx) {
} // ignore
rs = null;
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException sqlEx) {
} // ignore
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (SQLException sqlEx) {
} // ignore
conn = null;
}
}
}
}
执行结果
这里使用ResultSet的方式输出结果集。
无论从数据库客户端还是应用程序发起增删改查,对Mycat的操作与对MySQL的操作基本无异。
对DBA来说,Mycat就是MySQL的提高性能的最佳伴侣;对开发人员来说,Mycat就像MySQL服务器;对架构师来说,Mycat就是强大的数据库中间件,不仅仅可以用作分库分表,还可以用作读写分离、容灾备份等等。
但Mycat自身还存在一些弊端,比如,跨库分布式事务无法保证100%的ACID能力,关这一点就足够让人头疼;还有不支持复杂的SQL,部分表无法Join、跨库Join性能差等等。
针对Mycat明显存在的问题,其实国产分布式数据库都会有针对性地解决。笔者也是希望在通过学习mycat的过程中,了解当今国产数据库之间的特点、差异、演进路线。下次有时间再分享国产数据库的内容。