目录
本文以 MySQL 为例。以MyCat数据库中间件为例,通过MyCat来完成分库分表操作。
随着互联网及移动互联网的发展,应用系统的数据量也是成指数式增长,若采用单数据库进行数据存储,存在以下性能瓶颈:
为了解决上述问题,需要对数据库进行分库分表处理。
分库分表的中心思想是将数据分散存储,使得单一数据库/表的数据量变小,以此来缓解单一数据库的性能问题,从而达到提升数据库性能的目的。
分库分表的形式主要有两种:垂直拆分和水平拆分。
根据拆分的粒度,一般又分为分库和分表。
组成的拆分策略最终如下:
以表为依据,根据业务将不同表拆分到不同库中。
特点:
以字段为依据,根据字段属性将不同字段拆分到不同表中。
特点:
以字段为依据,按照一定策略,将一个库的数据拆分到多个库中。
特点:
以字段为依据,按照一定策略,将一个表的数据拆分到多个表中。
特点:
在业务系统中,为了缓解磁盘IO及CPU的性能瓶颈,到底是垂直拆分还是水平拆分;具体是分库还是分表,都需要根据具体的业务需求具体分析。
shardingJDBC:基于AOP原理,在应用程序中对本地执行的SQL进行拦截,解析、改写、路由处理。需要自行编码配置实现,只支持java语言,性能较高。
MyCat:数据库分库分表中间件,不用调整代码即可实现分库分表,支持多种语言,性能不如shardingJDBC。
所谓中间件,是一类连接软件组件和应用的计算机软件,以便软件各部件之间的通信。
Mycat是开源的、活跃的、基于Java语言编写的MySQL数据库中间件。可以像使用mysql一样来使用mycat,对于开发人员来说根本感觉不到mycat的存在。
开发人员只需要连接MyCat即可,而具体底层用到几台数据库,每一台数据库服务器里面存储了什么数据,都无需关心。具体的分库分表的策略,只需要在MyCat中配置即可。
优势:
在MyCat的整体结构中,分为两个部分:逻辑结构、物理结构。
MyCat的逻辑结构主要负责逻辑库、逻辑表、分片规则、分片节点等逻辑结构的处理,而具体的数据还是存储在物理结构,也就是数据库服务器中存储的。
MyCat是采用java语言开发的开源数据库中间件,支持Windows和Linux运行环境。要使用MyCat,需要在服务器中准备如下配置:
本文以Linux环境为例。
服务器
安装软件
说明
192.168.200.210
JDK、MyCat
MyCat中间件服务器
192.168.200.210
MySQL
分片服务器
192.168.200.213
MySQL
分片服务器
192.168.200.214
MySQL
分片服务器
bin:存放可执行文件,用于启动停止mycat。
conf:存放mycat的配置文件。
lib:存放mycat的项目依赖包(jar)。
logs:存放mycat的日志文件。
假设有如下需求:
由于 tb_order 表中数据量很大,磁盘IO及容量都到达了瓶颈,现在需要对 tb_order 表进行数据分片,分为三个数据节点,每一个节点主机位于不同的服务器上。具体结构参考下图:
环境准备:
且在上述3台数据库中创建数据库 db01 。
在schema.xml中配置逻辑库、逻辑表、数据节点、节点主机等相关信息。
具体的配置如下:
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="DB01" checkSQLschema="true" sqlMaxLimit="100">
<table name="TB_ORDER" dataNode="dn1,dn2,dn3" rule="auto-sharding-long"/>
</schema>
<dataNode name="dn1" dataHost="dhost1" database="db01" />
<dataNode name="dn2" dataHost="dhost2" database="db01" />
<dataNode name="dn3" dataHost="dhost3" database="db01" />
<dataHost name="dhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.200.210:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234" />
</dataHost>
<dataHost name="dhost2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.200.213:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234" />
</dataHost>
<dataHost name="dhost3" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.200.214:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234" />
</dataHost>
</mycat:schema>
配置MyCat的用户与用户权限信息:
需要在server.xml中配置用户名、密码,以及用户的访问权限信息,具体的配置如下(只找到相应部分):
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">DB01</property>
<!-- 表级 DML 权限设置 -->
<!--
<privileges check="true">
<schema name="DB01" dml="0110" >
<table name="TB_ORDER" dml="1110"></table>
</schema>
</privileges>
-->
</user>
<user name="user">
<property name="password">123456</property>
<property name="schemas">DB01</property>
<property name="readOnly">true</property>
</user>
上述的配置表示,定义了两个用户 root 和 user ,这两个用户都可以访问 DB01 这个逻辑库,访问密码都是123456,但root用户访问DB01逻辑库,既可以读也可以写,user用户访问DB01逻辑库只可以读。
配置完毕后,先启动涉及到的3台分片服务器,然后启动MyCat服务器。
切换到Mycat的安装目录,执行如下指令:
启动MyCat:
bin/mycat start
停止MyCat
bin/mycat stop
MyCat启动之后,占用端口号 8066。
启动完毕之后,可以查看logs目录下的 wrapper.log(启动日志),查看Mycat是否启动成功。
连接测试:
# 连接并登陆MyCat
mysql -h 192.168.200.210 -P 8066 -uroot -p123456
可以看到是通过MySQL的指令来连接的MyCat,因为MyCat在底层是模拟MySQL的协议。
数据测试:
CREATE TABLE tb_order (
id BIGINT(20) NOT NULL,
title VARCHAR(100) NOT NULL,
PRIMARY KEY (id)
) ENGINE = INNODB DEFAULT CHARSET = utf8;
INSERT INTO tb_order(id,title)
VALUES(1, 'goods1');
(2, 'goods2');
(3, 'goods3');
(5000000, 'goods5000000');
(10000000, 'goods10000000');
(10000001, 'goods10000001');
(15000000, 'goods15000000');
(15000001, 'goods15000001');
数据落在哪一个分片服务器是由逻辑表配置时的一个参数 rule 决定的,这个参数配置的就是分片规则,关于分片规则的配置,在后面会讲解。
三种配置文件:
schema.xml 作为MyCat中最重要的配置文件之一,涵盖了MyCat的逻辑库、逻辑表、分片规则、分片节点及数据源的配置。
主要包含以下三组标签:
schema定义逻辑库
schema 标签用于定义MyCat实例中的逻辑库,一个MyCat实例中,可以有多个逻辑库,可以通过 schema 标签来划分不同的逻辑库。
MyCat中的逻辑库的概念,等同于MySQL中的database概念,需要操作某个逻辑库下的表时,也需要切换逻辑库(use xxx)。
核心属性:
schema中的table定义逻辑表
table 标签定义了MyCat中逻辑库schema下的逻辑表,所有需要拆分的表都需要在table标签中定义 。
核心属性:
dataNode标签定义了MyCat中的数据节点,也就是数据分片。一个dataNode标签就是一个独立的数据分片。
核心属性:
该标签在MyCat逻辑库中作为底层标签存在,直接定义了具体的数据库实例、读写分离、心跳语句。
核心属性:
rule.xml中定义所有拆分表的规则,在使用过程中可以灵活的使用分片算法,或者对同一个分片算法使用不同的参数。它让分片过程可配置化。主要包含两类签:tableRule、Function。
server.xml配置文件包含了MyCat的系统配置信息,主要有两个重要的标签:system、user。
主要配置MyCat中的系统配置信息。对应的系统配置项及其含义如下:
属性
取值
含义
charset
utf8
设置Mycat的字符集,字符集需要与MySQL的字符集保持一致
nonePasswordLogin
0,1
0为需要密码登陆、1为不需要密码登陆。默认为0,设置为1则需要指定默认账户
useHandshakeV10
0,1
使用该选项主要的目的是为了能够兼容高版本的jdbc驱动,是否采用HandshakeV10Packet来与client进行通信。1:是;0:否
useSqlStat
0,1
开启SQL实时统计,1为开启,0为关闭。开启之后,MyCat会自动统计SQL语句的执行情况。mysql -h 127.0.0.1 -P 9066 -u root -p,查看MyCat执行的SQL、执行效率比较低的SQL、SQL的整体执行情况、读写比例等。(SHOW @@sql;、 SHOW @@sql.slow;、 SHOW @@sql.sum;)
useGlobleTableCheck
0,1
是否开启全局表的一致性检测。1为开启,0为关闭
sqlExecuteTimeout
1000
SQL语句执行的超时时间,单位为 s
sequnceHandlerType
0,1,2
用来指定Mycat全局序列类型。0 为本地文件,1 为数据库方式,2 为时间戳列方式。默认使用本地文件方式,文件方式主要用于测试
sequnceHandlerPattern
正则表达式
必须带有MYCATSEQ或者mycatseq进入序列匹配流程。注意MYCATSEQ_有空格的情况
subqueryRelationshipCheck
true,false
子查询中存在关联查询的情况下,检查关联字段中是否有分片字段。默认 false
useCompression
0,1
开启mysql压缩协议。0:关闭;1:开启
fakeMySQLVersion
5.5,5.6
设置模拟的MySQL版本号
defaultSqlParser
由于MyCat的最初版本使用了FoundationDB的SQL解析器,在MyCat1.3后增加了Druid解析器,所以要设置defaultSqlParser属性来指定默认的解析器。解析器有两个:druidparser 和 fdbparser,在MyCat1.4之后,默认是druidparser,fdbparser已经废除了
processors
1,2,…
指定系统可用的线程数量,默认值为CPU核心 x 每个核心运行线程数量;processors 会影响processorBufferPool,processorBufferLocalPercent,processorExecutor属性,所以,在性能调优时,可以适当地修改processors值
processorBufferChunk
指定每次分配Socket Direct Buffer。默认值为4096字节,也会影响BufferPool长度,如果一次性获取字节过多而导致buffer不够用,则会出现警告。可以调大该值
processorExecutor
指定NIOProcessor上共享businessExecutor固定线程池的大小;MyCat把异步任务交给 businessExecutor 线程池。在新版本的MyCat中这个连接池使用频次不高,可以适当地把该值调小
packetHeaderSize
指定MySQL协议中的报文头长度,默认4个字节
maxPacketSize
指定MySQL协议可以携带的数据最大大小,默认值为16M
idleTimeout
指定连接的空闲时间的超时长度。如果超时,将关闭资源并回收,默认30分钟
txIsolation
1,2,3,4
初始化前端连接的事务隔离级别。默认为REPEATED_READ,对应数字为3。READ_UNCOMMITED=1;READ_COMMITTED=2;REPEATED_READ=3;SERIALIZABLE=4
sqlExecuteTimeout
执行SQL的超时时间。如果SQL语句执行超时,将关闭连接,默认300秒
serverPort
端口号
定义MyCat的使用端口。默认8066
managerPort
端口号
定义MyCat的管理端口。默认9066
配置MyCat中的用户、访问密码,以及用户针对于逻辑库、逻辑表的权限信息。具体的权限描述方式及配置说明如下:
在测试权限操作时,只需要将 privileges 标签的注释取消。在 privileges 下的 schema 标签中配置的 dml 属性是逻辑库的权限。在 privileges 的 schema 下的 table 标签中配置的 dml 属性是逻辑表的权限。
在业务系统中,涉及以下表结构,但是由于用户与订单每天都会产生大量的数据,单台服务器的数据存储及处理能力有限,可以对数据库表进行拆分,原有的数据库表如下:
现在考虑将其进行垂直分库操作,将商品相关的表拆分到一个数据库服务器,订单表拆分到一个数据库服务器,用户及省市区表拆分到一个服务器。最终结构如下:
准备三台服务器,IP地址如图所示:
且在192.168.200.210,192.168.200.213,192.168.200.214上面创建数据库shopping。
schema.xml
<schema name="SHOPPING" checkSQLschema="true" sqlMaxLimit="100">
<table name="tb_goods_base" dataNode="dn1" primaryKey="id" />
<table name="tb_goods_brand" dataNode="dn1" primaryKey="id" />
<table name="tb_goods_cat" dataNode="dn1" primaryKey="id" />
<table name="tb_goods_desc" dataNode="dn1" primaryKey="goods_id" />
<table name="tb_goods_item" dataNode="dn1" primaryKey="id" />
<table name="tb_order_item" dataNode="dn2" primaryKey="id" />
<table name="tb_order_master" dataNode="dn2" primaryKey="order_id" />
<table name="tb_order_pay_log" dataNode="dn2" primaryKey="out_trade_no" />
<table name="tb_user" dataNode="dn3" primaryKey="id" />
<table name="tb_user_address" dataNode="dn3" primaryKey="id" />
<table name="tb_areas_provinces" dataNode="dn3" primaryKey="id"/>
<table name="tb_areas_city" dataNode="dn3" primaryKey="id"/>
<table name="tb_areas_region" dataNode="dn3" primaryKey="id"/>
</schema>
<dataNode name="dn1" dataHost="dhost1" database="shopping" />
<dataNode name="dn2" dataHost="dhost2" database="shopping" />
<dataNode name="dn3" dataHost="dhost3" database="shopping" />
<dataHost name="dhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.200.210:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234" />
</dataHost>
<dataHost name="dhost2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.200.213:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234" />
</dataHost>
<dataHost name="dhost3" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.200.214:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234" />
</dataHost>
server.xml
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">SHOPPING</property>
<!-- 表级 DML 权限设置 -->
<!--
<privileges check="true">
<schema name="DB01" dml="0110" >
<table name="TB_ORDER" dml="1110"></table>
</schema>
</privileges>
-->
</user>
<user name="user">
<property name="password">123456</property>
<property name="schemas">SHOPPING</property>
<property name="readOnly">true</property>
</user>
将表结构及对应的测试数据导入之后,可以检查一下各个数据库服务器中的表结构分布情况。检查是否和准备工作中规划的服务器一致。
对于省、市、区/县表tb_areas_provinces,tb_areas_city,tb_areas_region,是属于数据字典表,在多个业务模块中都可能会遇到,可以将其设置为全局表,利于业务操作。
修改schema.xml中的逻辑表的配置,修改 tb_areas_provinces、tb_areas_city、tb_areas_region 三个逻辑表,增加 type 属性,配置为global,就代表该表是全局表,就会在所涉及到的dataNode中创建该表。对于当前配置来说,也就意味着所有的节点中都有该表了。
<table name="tb_areas_provinces" dataNode="dn1,dn2,dn3" primaryKey="id" type="global"/>
<table name="tb_areas_city" dataNode="dn1,dn2,dn3" primaryKey="id" type="global"/>
<table name="tb_areas_region" dataNode="dn1,dn2,dn3" primaryKey="id" type="global"/>
在业务系统中,有一张日志表,业务系统每天都会产生大量的日志数据,单台服务器的数据存储及处理能力是有限的,可以对数据库表进行拆分。
准备三台服务器,具体的结构如下:
schema.xml
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100">
<table name="tb_log" dataNode="dn4,dn5,dn6" primaryKey="id" rule="mod-long" />
</schema>
<dataNode name="dn4" dataHost="dhost1" database="testdb" />
<dataNode name="dn5" dataHost="dhost2" database="testdb" />
<dataNode name="dn6" dataHost="dhost3" database="testdb" />
tb_log表最终落在3个节点中,分别是 dn4、dn5、dn6 ,而具体的数据分别存储在 dhost1、dhost2、dhost3的testdb数据库中。
server.xml
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">SHOPPING,TESTDB</property>
<!-- 表级 DML 权限设置 -->
<!--
<privileges check="true">
<schema name="DB01" dml="0110" >
<table name="TB_ORDER" dml="1110"></table>
</schema>
</privileges>
-->
</user>
配置root用户既可以访问 SHOPPING 逻辑库,又可以访问 TESTDB 逻辑库。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章