内容输出来源:拉钩教育Java就业训练营
1 分库分表
指通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库(主机)上面,以达到分散单台设备负载的效果。
分库分表的目的是为了解决由于数据量过大而导致数据库性能降低的问题,将原来单体服务的数据库进行拆分.将数据大表拆分成若干数据表组成,使得单一数据库、单一数据表的数据量变小,从而达到提升数据库性能的目的。
分库分表使用场景:
- 单机存储容量遇到瓶颈.
- 连接数,处理能力达到上限
1.1 分库分表方式
分库分表包括: 垂直分库、垂直分表、水平分库、水平分表 四种方式。
1.1.1 垂直分库
数据库中不同的表对应着不同的业务,垂直切分是指按照业务的不同将表进行分类,分布到不同的数据库上面
1.1.2 垂直分表
将一个表按照字段分成多表,每个表存储其中一部分字段。
对职位表进行垂直拆分, 将职位基本信息放在一张表, 将职位描述信息存放在另一张表
PS:垂直拆分带来的优势:
解决业务层面的耦合,业务清晰
能对不同业务的数据进行分级管理、维护、监控、扩展等
高并发场景下,垂直分库一定程度的提高访问性能
同时,垂直拆分没有彻底解决单表数据量过大的问题
1.1.3 水平分库
将单张表的数据切分到不同的数据库中,每个数据库具有相同的库与表,只是表中数据集合不同。
根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库
1.1.4 水平分表
针对数据量巨大的单张表(比如订单表),按照规则把一张表的数据切分到多张表里面去。 但是这些表还是在同一个库中,所以库级别的数据库操作还是有IO瓶颈。
- 垂直分表: 将一个表按照字段分成多表,每个表存储其中一部分字段。
- 垂直分库: 根据表的业务不同,分别存放在不同的库中,这些库分别部署在不同的服务器.
- 水平分库: 把一张表的数据按照一定规则,分配到不同的数据库,每一个库只有这张表的部分数据.
- 水平分表: 把一张表的数据按照一定规则,分配到同一个数据库的多张表中,每个表只有这个表的部分数据.
2 MyCat
MyCat 是目前最流行的基于 java 语言编写的数据库中间件,是一个实现了 MySQL 协议的服务器。其核心功能是分库分表和读写分离,即将一个大表水平分割为 N 个小表,存储在后端 MySQL 服务器里或者其他数据库里。
MyCat就是一个近似等于 MySQL 的数据库服务器,你可以用连接 MySQL 的方式去连接 Mycat
2.1 MyCat概念
2.1.1 MyCat分片策略
分片:通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库(主机)上面,以达到分散单台设备负载的效果。
两种切分模式:
-
一种是按照不同的表(或者Schema)来切分到不同的数据库(主机)之上,这种切可以称之为数据的垂直(纵向)切分
-
另外一种则是根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种切分称之为数据的水平(横向)切分。
-
逻辑库(schema)
- 对数据进行分片处理之后,从原有的一个库,被切分为多个分片数据库,所有的分片数据库集群构成了整个完整的数据库存储。Mycat在操作时,使用逻辑库来代表这个完整的数据库集群,便于对整个集群操作。
-
逻辑表(table)
- 分布式数据库中,对应用来说,读写数据的表就是逻辑表。
逻辑表,可以是数据切分后,分布在一个或多个分片库中,也可以不做数据切分,不分片,只有一个表构成。
- 分布式数据库中,对应用来说,读写数据的表就是逻辑表。
-
分片节点(dataNode)
- 数据切分后,一个大表被分到不同的分片数据库上面,每个表分片所在的数据库就是分片节点(dataNode)。
-
节点主机(dataHost)
- 数据切分后,每个分片节点不一定都会独占一台机器,同一机器上面可以有多个分片数据库, 这样一个或多个分片节点所在的机器就是节点主机,为了规避单节点主机并发数限制, 尽量将读写压力高的分片节点均衡的放在不同的节点主机dataHost。
-
分片规则
- 前面讲了数据切分,一个大表被分成若干个分片表,就需要一定的规则rule,这样按照某种业务规则把数据分到 某个分片的规则就是分片规则,数据切分选择合适的分片规则非常重要,将极大的避免后续数据处理的难度。
2.2 MyCat安装
-
搭建三台虚拟机。
-
前两个虚拟机安装mysql数据库服务器
server01 192.168.163.128 server02 192.168.163.129
-
创建数据库
192.168.163.128创建 lagou1 数据库
192.168.163.129创建 lagou2 和 lagou3 数据库
上传MyCat 到 server03(192.168.163.130) 服务器 ,并解压
启动命令:./mycat start
停止命令:./mycat stop
重启命令:./mycat restart
查看状态:./mycat status
带控制台启动:./mycat console
2.3 MyCat核心配置
2.3.1 schema.xml配置
schema标签
Schema.xml作为MyCat中重要的配置文件之一,管理着MyCat的逻辑库、表、分片规则、DataNode以及DataSource。
<!-- 逻辑库 -->
<schema name="lagou" checkSQLschema="true" sqlMaxLimit="100" >
</schema>
table标签
table标签定义了 Mycat 中的逻辑表,所有需要拆分的表都需要在这个标签中定义
<schema name="lagou" checkSQLschema="true" sqlMaxLimit="100" >
<table name="pay_order" dataNode="dn1,dn2,dn3" rule="auto-sharding-long"
primaryKey="id" autoIncrement="true" ></table>
</schema>
dataNode标签
de标签定义了 MyCat 中的分片节点,也就是我们通常说所的数据分片。
<dataNode name="dn1" dataHost="localhost1" database="lagou1" />
<dataNode name="dn2" dataHost="localhost2" database="lagou2" />
<dataNode name="dn3" dataHost="localhost2" database="lagou3" />
- name: 定义节点的名字,这个名字需要是唯一的,我们需要在 table 标签上应用这个名字,来建立表与分片对应的关系。
- dataHost : 用于定义该分片属于哪个分片主机,属性值是引用 dataHost 标签上定义的 name 属性。
- database: 用于定义该分片节点属于哪个具体的库。
dataHost标签
dataHost标签在 Mycat 逻辑库中也是作为最底层的标签存在,直接定义了具体的数据库实例、读写分离配置和心跳语句
<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="192.168.163.128:3306" user="root"
password="WangXu@666">
</writeHost>
</dataHost>
<dataHost name="localhost2" 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="hostM2" url="192.168.163.129:3306" user="root"
password="WangXu@666">
</writeHost>
</dataHost>
2.3.2 server.xml配置
user标签
这个标签主要用于定义登录 mycat 的用户和权限。
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">lagou</property>
<property name="defaultSchema">lagou</property>
</user>
连接MyCat
mysql -uroot -p123456 -h127.0.0.1 -P8066
2.3.3 rule.xml配置
- rule.xml里面就定义了我们对表进行拆分所涉及到的规则定义。我们可以灵活的对表使用不同的分片算 法,或者对表使用相同的算法但具体的参数不同。
- 这个文件里面主要有tableRule和function这两个标 签。在具体使用过程中可以按照需求添加tableRule和function。
此配置文件可以不用修改,使用默认即可
2.3 分库分表
2.3.1 分片规则配置(水平分库)
水平分库: 把一张表的数据按照一定规则,分配到不同的数据库,每一个库只有这张表的部分数据
在rule.xml配置, 自动分片
<!-- schema 逻辑库 -->
<schema name="lagou" checkSQLschema="true" sqlMaxLimit="100" >
<table name="pay_order" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" primaryKey="id" autoIncrement="true" >
</table>
</schema>
<!-- 自动分片 -->
<tableRule name="auto-sharding-long">
<rule>
<columns>id</columns>
<algorithm>rang-long</algorithm>
</rule>
</tableRule>
<function name="rang-long"
class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">autopartition-long.txt</property>
</function>
autopartition-long.txt
该文件可设置分片规则,id的范围
2.3.2 全局序列号
在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一。为此,Mycat 提供了全局sequence,并且提供了包含本地配置和数据库配置等多种实现方式。
server.xml文件中
<system>
<property name="sequnceHandlerType">0</property>
</system>
0 表示是表示使用本地文件方式。
1 表示的是根据数据库来生成
2 表示时间戳的方式 ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加)
本地文件
此方式 Mycat 将 sequence 配置到文件中,当使用到 sequence 中的配置后,Mycat 会更新 classpath中的 sequence_conf.properties 文件中 sequence 当前的值。
PAY_ORDER.HISIDS=
PAY_ORDER.MINID=101
PAY_ORDER.MAXID=10000000
PAY_ORDER.CURID=100
2.4 读写分离
- 主从复制
通过搭建主从架构, 将数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善。 - 读写分离
读写分离就是让主库处理事务性操作,从库处理select查询。数据库复制被用来把事务性查询导致的数据变更同步到从库,同时主库也可以select查询。
2.4.1 MySQL主从复制(同步)
Mysql 中有一种日志叫做 bin 日志(二进制日志)。这个日志会记录下所有修改了数据库的SQL语句(insert,update,delete,create/alter/drop table, grant 等等)。
主从复制的原理其实就是把主服务器上的 bin 日志复制到从服务器上执行一遍,这样从服务器上的数据就和主服务器上的数据相同了。
2.4.2 主从复制架构搭建
Mysql的主从复制至少是需要两个Mysql的服务,当然Mysql的服务是可以分布在不同的服务器上,也可以在一台服务器上启动多个服务。
1.master中创建数据库和表
-- 创建数据库
CREATE DATABASE test CHARACTER SET utf8;
-- 创建表
CREATE TABLE users (
id INT(11) PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20) DEFAULT NULL,
age INT(11) DEFAULT NULL
);
-- 插入数据
INSERT INTO users VALUES(NULL,'user1',20);
INSERT INTO users VALUES(NULL,'user2',21);
INSERT INTO users VALUES(NULL,'user3',22);
2.修改主数据库的配置文件my.cnf
vim /etc/my.cnf
插入内容
lower_case_table_names=1
log-bin=mysql-bin
server-id=1
binlog-do-db=test
binlog_ignore_db=mysql
- server-id=1 中的1可以任定义,只要是唯一的就行。
- log-bin=mysql-bin 表示启用binlog功能,并制定二进制日志的存储目录,
- binlog-do-db=test 是表示只备份test 数据库。
- binlog_ignore_db=mysql 表示忽略备份mysql。
- 不加binlog-do-db和binlog_ignore_db,那就表示备份全部数据库。
3.重启MySQL
service mysqld restart
4.在主数据库上, 创建一个允许从数据库来访问的用户账号.
-- 创建账号
GRANT REPLICATION SLAVE ON *.* TO 'slave'@'192.168.163.128' IDENTIFIED BY 'WangXu@666';
5.停止主数据库的更新操作, 并且生成主数据库的备份
-- 执行以下命令锁定数据库以防止写入数据。
FLUSH TABLES WITH READ LOCK;
6.导出数据库,恢复写操作
使用SQLYog导出,主数据库备份完毕,恢复写操作
unlock tables;
7.主数据库备份的test.sql导入到从数据库
导入后, 主库和从库数据会追加相平,保持同步!此过程中,若主库存在业务,并发较高,在同步的时候要先锁表,让其不要有修改!等待主从数据追平,主从同步后在打开锁!
8.修改从数据库的 my.cnf
增加server-id参数,保证唯一.
server-id=2
9.在从数据库设置相关信息
执行sql语句
STOP SLAVE;
CHANGE MASTER TO MASTER_HOST='192.168.163.128',
MASTER_USER='slave',
MASTER_PASSWORD='WangXu@666',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=0,
MASTER_CONNECT_RETRY=10;
10.修改auto.cnf中的UUID,保证唯一
-- 编辑auto.cnf
vim /var/lib/mysql/auto.cnf
-- 修改UUID的值,依据主从数据库的值自行修改
server-uuid=a402ac7f-c392-11ea-ad18-000c2980a208
-- 重启
service mysqld restart
11.在从服务器上,启动slave 进程
start slave;
-- 查看状态
SHOW SLAVE STATUS;
-- 命令行下查看状态 执行
SHOW SLAVE STATUS \G;
当俩ing个参数均为yes时,配置成功
2.4.3 实现读写分离
在schema.xml文件中配置Mycat读写分离。使用前需要搭建MySQL主从架构,并实现主从复制,Mycat不负责数据同步问题。
sever.xml
修改用户可以访问的逻辑表为 test
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">test</property>
<property name="defaultSchema">test</property>
</user>
schema
<schema name="test" checkSQLschema="true" sqlMaxLimit="100">
<table name="users" dataNode="dn4" ruleRequired="false" primaryKey="id" autoIncrement="true" >
</table>
</schema>
dataNode
<!-- 读写分离 -->
<dataNode name="dn4" dataHost="localhost3" database="test" />
dataHost
<!-- 读写分离 -->
<dataHost name="localhost3" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- 主 -->
<writeHost host="M1" url="192.168.163.128:3306" user="root"
password="WangXu@666">
<!-- 从 -->
<readHost host="S1" url="192.168.163.129:3306" user="root"
password="WangXu@666" weight="1" />
</writeHost>
</dataHost>
balance参数:
- 0 :所有读操作都发送到当前可用的writeHost
- 1 :所有读操作都随机发送到readHost和stand by writeHost
- 2 :所有读操作都随机发送到writeHost和readHost
- 3 :所有读操作都随机发送到writeHost对应的readHost上,但是writeHost不负担读压力
writeType参数:
- 0 :所有写操作都发送到可用的writeHost
- 1 :所有写操作都随机发送到readHost
- 2 :所有写操作都随机发送到writeHost,readHost
angXu@666">
balance参数:
- 0 :所有读操作都发送到当前可用的writeHost
- 1 :所有读操作都随机发送到readHost和stand by writeHost
- 2 :所有读操作都随机发送到writeHost和readHost
- 3 :所有读操作都随机发送到writeHost对应的readHost上,但是writeHost不负担读压力
writeType参数:
- 0 :所有写操作都发送到可用的writeHost
- 1 :所有写操作都随机发送到readHost
- 2 :所有写操作都随机发送到writeHost,readHost