MySQL的分库分表有两种方式:垂直拆分和水平拆分。
垂直拆分:垂直拆分就是要把表按模块划分到不一样数据库表中(固然原则仍是不破坏第三范式),这种拆分在大型网站的演变过程当中是很常见的。当一个网站还在很小的时候,只有小量的人来开发和维护,各模块和表都在一块儿,当网站不断丰富和壮大的时候,也会变成多个子系统来支撑,这时就有按模块和功能把表划分出来的需求。其实,相对于垂直切分更进一步的是服务化改造,说得简单就是要把原来强耦合的系统拆分红多个弱耦合的服务,经过服务间的调用来知足业务需求看,所以表拆出来后要经过服务的形式暴露出去,而不是直接调用不一样模块的表。(垂直拆分用于分布式场景)
水平拆分:解决单表大数据量的问题,水平切分就是要把一个表按照某种规则把数据划分到不一样表或数据库里。例如:在大型电商系统中,天天的会员人数不断的增长。达到必定瓶颈后如何优化查询。经过将表数据水平分割成不一样的表来实现优化。(实现规则:hash、时间、不一样的维度)
通俗理解:水平拆分行,行数据拆分到不一样表中, 垂直拆分列,表数据拆分到不一样表中。java
分表原理:取模拆分(一致性hash),能够将数据分配的比较均匀。
这里咱们以3张表为例:
案例:首先我建立三张表 user0 / user1 /user2 , 而后我再建立 uuid表,该表的做用就是提供自增的id。
代码实现:mysql
-- 建表语句 create table user0( id int unsigned primary key , name varchar(22) not null default '', pwd varchar(22) not null default '') engine=myisam charset utf8; create table user1( id int unsigned primary key , name varchar(22) not null default '', pwd varchar(22) not null default '') engine=myisam charset utf8; create table user2( id int unsigned primary key , name varchar(22) not null default '', pwd varchar(22) not null default '') engine=myisam charset utf8; create table uuid( id int unsigned primary key auto_increment)engine=myisam charset utf8;
//分表逻辑 @Service public class UserService { @Autowired private JdbcTemplate jdbcTemplate; public String regit(String name, String pwd) { // 1.先获取到 自定增加ID String idInsertSQL = "INSERT INTO uuid VALUES (NULL);"; jdbcTemplate.update(idInsertSQL); Long insertId = jdbcTemplate.queryForObject("select last_insert_id()", Long.class); // 2.判断存储表名称 String tableName = "user" + insertId % 3; // 3.注册数据 String insertUserSql = "INSERT INTO " + tableName + " VALUES ('" + insertId + "','" + name + "','" + pwd + "');"; System.out.println("insertUserSql:" + insertUserSql); jdbcTemplate.update(insertUserSql); return "success"; } public String get(Long id) { String tableName = "user" + id % 3; String sql = "select name from " + tableName + " where id="+id; System.out.println("SQL:" + sql); String name = jdbcTemplate.queryForObject(sql, String.class); return name; } }
上图中192.168.8.40是主节点(MYSQL-A),192.168.8.41是从节点(MYSQL-B)。
影响MySQL-A数据库的操做,在数据库执行后,都会写入本地的日志系统A中。 假设,实时的将变化了的日志系统中的数据库事件操做,在MYSQL-A的3306端口,经过网络发给MYSQL-B。 MYSQL-B收到后,写入本地日志系统B,而后一条条的将数据库事件在数据库中完成。那么,MYSQL-A的变化,MYSQL-B也会变化,这样就是所谓的MYSQL的复制,即MYSQL replication。
MYSQL的日志类型中的二进制日志,也就是专门用来保存修改数据库表的全部动做,即bin log。【注意MYSQL会在执行语句以后,释放锁以前,写入二进制日志,确保事务安全】
日志系统B,并非二进制日志,因为它是从MYSQL-A的二进制日志复制过来的,并非本身的数据库变化产生的,有点接力的感受,称为中继日志,即relay log。
主从复制,所产生的问题:
- 主服务器如何实现负载均衡、高可用。
- 如何保证数据不丢失。
- 如何保证主从数据一致性。git
简介:在数据库集群架构中,让主库负责处理事务性查询,而从库只负责处理select查询,让二者分工明确达到提升数据库总体读写性能。固然,主数据库另一个功能就是负责将事务性查询致使的数据变动同步到从库中,也就是写操做。
写分离的好处:分摊服务器压力,提升机器的系统处理效率。增长冗余,提升服务器性能,当一台服务器宕机后能够从另外一个库以最快的方式恢复服务。sql
环境介绍:
① 安装MySQL
#这里小编是经过rpm&&yum安装的MySQL数据库
#下载安装包 $wget http://repo.mysql.com/mysql57-community-release-el7-8.noarch.rpm #安装 $rpm -ivh mysql57-community-release-el7-8.noarch.rpm $yum install mysql-server #启动MySQL服务 $systemctl start mysqld #查看root初始密码 $grep 'temporary password' /var/log/mysqld.log
#重设root密码
$mysql_secure_installation
注意:
这里配置N,否则的话没法使用root登陆MySQL。vim
#修改root密码并设置能够远程访问: mysql> use mysql; mysql> update user set authentication_string=password("123456") where user="root"; mysql> flush privileges; mysql> select 'host','user' from user where user='root';
mysql> UPDATE user SET grant_priv = 'Y' WHERE user = 'root'; mysql> select host,user from user;
mysql> update user set host = '%' where user = 'root'; mysql> select host,user from user;
mysql> flush privileges; mysql> quit #测试是否配置成功 $mysql -uroot -p -h 192.168.xxx.xxx
② 建立数据库
分别在master和slave中建立一个数据库:安全
mysql> create database test;
注意:这里从数据库中必定要存在于主数据库中同步的库,不然在同步时会报错:bash
③ 修改master中的配置服务器
$vim /etc/my.cnf #master 加入 server-id=1 log-bin=mysql-bin log-slave-updates=1 #须要同步的数据库 binlog-do-db=test #被忽略的数据 binlog-ignore-db=mysql
④ 在master中建立salve同步帐号网络
mysql> grant replication slave on *.* to 'user1'@'192.168.130.133' identified by 'Zy.123456'; mysql> flush privileges;
⑤ 重启master并查看日志状况
$systemctl restart mysqld mysql> show master status;
⑥ 修改slave中MySQL的配置
#slave 加入 server-id=2 log-bin= mysql-bin relay-log= mysql-relay-bin read-only=1 log-slave-updates=1 #要同步的数据库,不写本行表示同步全部数据库 replicate-do-db=test
⑦ 重启slave并验证是否能够链接master
$systemctl restart mysqld $mysql -uuser1 -p123456 -h 192.168.130.133 mysql> show grants for user1@192.168.130.133;
⑧ 设置slave复制 并启动slave
mysql>change master to master_host='192.168.130.134',master_user='user1',master_password='Zy.123456'; -- 启动slave mysql> start slave; mysql> SHOW SLAVE STATUS;
主要查看Slave_IO_Running和Slave_SQL_Running 两列是否都为YES。
⑨测试主从服务是否同步
在主服务器中执行:
mysql> use test; mysql> create table test(id int,name char(10)); mysql> insert into test values(1,'zaq'); mysql> insert into test values(1,'xsw'); mysql> select * from test;
此时查看从服务器:
到此主从复制就配置完成!
主库中已有数据的解决方案:
- 方案一:选择忽略主库以前的数据,不作处理。这种方案只适用于不重要的无关紧要的数据,而且业务上可以容忍主从库数据不一致的场景。
- 方案二:对主库的数据进行备份,而后将主数据库中导出的数据导入到从数据库,而后再开启主从复制,以此来保证主从数据库数据一致。
这里小编介绍如何使用方案二进行数据同步。
① 备份数据
假设这里咱们有一个库:weibo而且数据库中有相应的数据:
#锁定主表 mysql> flush tables with read lock; #保证表只能作读操做
#查看此时主数据库状态,并记录bin-file和pos
mysql>show master status;
#在/etc/my.cnf中加入:
[mysqldump] user = root password = rootpassword
#重启服务: [root@zzy ~]# systemctl restart mysqld
#备份数据库 mysqldump weibo > weibo_back.sql 而后将备份数据文件传入从机器中, 在mysql中执行:source /path/ weibo_back.sql
此时主从数据库的数据已经同步!
② 修改配置
这里主从服务器的配置同上一节说道的相同。
③ 启动slave
mysql>stop slave; mysql>reset slave; change master to mysql>master_host='192.168.130.134', >master_user='user2', >master_password='Zy.123456', >master_log_file='mysql-bin.000004', >master_log_pos=154; mysql>start slave; mysql>SHOW SLAVE STATUS;
注意:这里的master_log_file和master_log_pos必定要和从主数据库中经过“show master status”命令查到的相同。
④ 解锁主数据库
mysql>unlock tables;
而后咱们就能够测试,在主数据库中插入一条记录,看看从数据库是否有数据同步。
mycati一个开源的分布式数据库系统,可是由于数据库通常都有本身的数据库引擎,而mycat没有属于本身独有的数据库引擎,因此严格意义上来说并不能算是一个完整的数据库系统,只能是一个在应用和数据库之间的服务中间件。
在mycat中间件出现之间,MySQL主从复制集群,若是要实现读写分离,通常是在程序段实现,这样就带来了一个问题,即数据段和程序的耦合度过高,若是数据库的地址发生了改变,那么个人程序也要进行相应的修改,若是数据库不当心挂掉了,则同时也意味着程序的不可用,而对于不少应用来讲,并不能接受;引入Mycat中间件能很好地对程序和数据库进行解耦,这样,程序只需关注数据库中间件的地址,而无需知晓底层数据库是如何提供服务的,大量的通用数据聚合、事务、数据源切换等工做都由中间件来处理;Mycat中间件的原理是对数据进行分片处理,从原有的一个库,被切分为多个分片数据库,全部的分片数据库集群构成完成的数据库存储,有点相似磁盘阵列中的RAID0。
经过使用mycat能够很轻松的实现数据库的读写分离,而且因为其特色,不只实现负载均衡,并且提升了集群的安全性。
注意,这里mycat只是作了负载均衡,主从复制并无作,因此,若是想让MySQL集群能够读写分离,就须要使用mycat并沿用原有的主从复制的配置。
这里小编遇到的一个坑就是,最好使用mycat1.5即以上的版本,否则对一些连接工具会报错:出现no mycat database selected问题!
这里小编使用的是1.6Linux版本。
首先下载相应的版本:http://dl.mycat.io/1.6-RELEASE/
① 保证MySQL的机器环境中有JDK1.8
须要配置JAVA_HOME!
这里注意,经验推荐配置在相应用户的~/.bashrc下,不要都配置在/etc/profile中。
② 配置mycat
这里须要配置4个文件:server.xml 、schema.xml、 rule.xml 、log4j2.xml
这里小编就简单介绍下:
server.xml:配置用户
schema.xml:配置关联的表和库,以及链接MySQL的host和密码
rule.xml:配置相应的分片规则
log4j2.xml:配置日志级别。
#server.xml加入: <user name="mycat"> <property name="password">123456</property> <property name="schemas">mycat</property> </user> <user name="mycat_read"> <property name="password">123456</property> <property name="schemas">mycat</property> <property name="readOnly">true</property> </user>
#schema.xml修改成: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat="http://io.mycat/"> <schema name="mycat" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1" /> <dataNode name="dn1" dataHost="localhost1" database="weibo" /> <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.130.134:3306" user="root" password="123456"> </writeHost> <writeHost host="hostM2" url="192.168.130.133:3306" user="root" password="123456"/> </dataHost> </mycat:schema>
#rule.xml基本不变
# log4j2.xml将日志修改成debug <Loggers> <asyncRoot level="debug" includeLocation="true"> <AppenderRef ref="Console" /> <AppenderRef ref="RollingFile"/> </asyncRoot> </Loggers>
③ 启动mycat
进入mycat的bin下
#运行 $sh mycat start /sh startup_nowrap.sh
④ 测试链接
这里能够经过MySQL进行链接:
[root@zzy bin]# mysql -umycat -P8066 -p123456 -h127.0.0.1
这里的user和password就是在server.xml中配置的。
也能够经过工具链接,可是须要注意,这里链接的数据库必定要是mycat。
这个问题是由于:mycat启动时须要的内存默认最大为4G,最小为1G,小编这里使用的虚拟机没有这么大内存,因此须要修改$MYCAT_HOMT/conf/ wrapper.conf中:
把配置调整下就行。
这个问题就是须要在/etc/hosts中配置当前主机与IP的映射:
这里咱们配置了mycat和主从复制,这里咱们只须要链接mycat的8066端口,经过mycat用户就能够对数据库进行CRUD,操做。这里咱们链接mycat:
在weibo库下的t_message表中插入一条记录:
而后分别查看master数据库和slave数据库:
到此,MySQL的负载均衡高可用版的读写分离就完成了!
以上两张测试表的语句:
DROP TABLE IF EXISTS `t_message`; CREATE TABLE `t_message` ( `messages_id` varchar(64) NOT NULL COMMENT '微博ID', `user_id` varchar(64) NOT NULL COMMENT '发表用户', `messages_info` varchar(255) DEFAULT NULL COMMENT '微博内容', `messages_time` datetime DEFAULT NULL COMMENT '发布时间', `messages_commentnum` int(12) DEFAULT NULL COMMENT '评论次数', `message_deleteflag` tinyint(1) NOT NULL COMMENT '删除标记 1:已删除 0:未删除', `message_viewnum` int(12) DEFAULT NULL COMMENT '被浏览量', PRIMARY KEY (`messages_id`), KEY `user_id` (`user_id`), CONSTRAINT `t_message_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `t_users` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; LOCK TABLES `t_message` WRITE; ; INSERT INTO `t_message` VALUES ('0001','1001','isfnesnfw','2019-09-01 00:00:00',2,1,2),('0002','1002','isfnesnfw','2019-08-21 00:00:00',2,1,2),('0003','1002','isfnesnfw','2019-08-21 00:00:00',2,1,2); UNLOCK TABLES; DROP TABLE IF EXISTS `t_users`; CREATE TABLE `t_users` ( `user_id` varchar(64) NOT NULL COMMENT '注册用户ID', `user_email` varchar(64) NOT NULL COMMENT '注册用户邮箱', `user_password` varchar(64) NOT NULL COMMENT '注册用户密码', `user_nikename` varchar(64) NOT NULL COMMENT '注册用户昵称', `user_creatime` datetime NOT NULL COMMENT '注册时间', `user_status` tinyint(1) NOT NULL COMMENT '验证状态 1:已验证 0:未验证', `user_deleteflag` tinyint(1) NOT NULL COMMENT '删除标记 1:已删除 0:未删除', PRIMARY KEY (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; LOCK TABLES `t_users` WRITE; INSERT INTO `t_users` VALUES ('1001','www.415511@qq.com','123456','zsa','2019-08-09 00:00:00',1,2),('1002','www.2345@qq.com','4578964','zsa','2019-07-09 00:00:00',2,3),('1003','www.41we11@qq.com','123456','zsa','2019-08-09 00:00:00',1,2),('1004','www.41523511@qq.com','1456','zasdsa','2018-08-09 00:00:00',1,2); UNLOCK TABLES;