高可用Mysql架构_Mysql主从复制、解决主从数据一致性的问题、Mysql双主热备、Mysql双主双从、Mysql读写分离(Mycat中间件)、Mysql分库分表架构(Mycat中间件)的演变
【Mysql主从复制】
解决的问题
数据分布:比如一共150台机器,分别往电信、网通、移动各放50台,这样无论在哪个网络访问都很快。其次按照地域,比如国内国外,北方南方,这样地域性访问解决了。
负载均衡:Mysql读写分离,读写分开了,解决了部分服务器的压力,均衡分开。
数据备份:比如100台机器,实际数据是一样的,这样可以说每台机器都是数据备份。
高可用性和容错性:1台机器挂掉了无所谓,因为还有99台机器。
实现原理:
提到主从同步的原理,我们就需要了解在数据库中的一个重要日志文件,那就是 Binlog 二进制日志,它记录了对数据库进行更新的事件。实际上主从同步的原理就是基于 Binlog 进行数据同步的。在主从复制过程中,会基于 3 个线程来操作,一个主库线程,两个从库线程。
二进制日志转储线程(Binlog dump thread)是一个主库线程。当从库线程连接的时候,主库可以将二进制日志发送给从库,当主库读取事件的时候,会在 Binlog 上加锁,读取完成之后,再将锁释放掉。
从库 I/O 线程会连接到主库,向主库发送请求更新 Binlog。这时从库的 I/O 线程就可以读取到主库的二进制日志转储线程发送的 Binlog 更新部分,并且拷贝到本地形成中继日志(Relay log)。
从库 SQL 线程会读取从库中的中继日志,并且执行日志中的事件,从而将从库中的数据与主库保持同步。
实现步骤:
Master将改变日志记录到二进制日志中(binary log), 再然后Slave重做中继日志中的事件,将改变反应它自己的数据,也就是执行一遍日志。
流程图解说:
首先,如果主机有数据改变,会写到主机的bin-log日志中,然后从机会监听主机的bin-log日志,这时候会读主机的bin-log,然后写入自己(从机)的relay-log(中继日志)中,然后再从中继日志中读出来执行SQL事件。这样就会执行了主机的SQL操作。
所以你能看到主从同步的内容就是二进制日志(Binlog),它虽然叫二进制日志,实际上存储的是一个又一个事件(Event),这些事件分别对应着数据库的更新操作,比如 INSERT、UPDATE、DELETE 等。另外我们还需要注意的是,不是所有版本的 MySQL 都默认开启服务器的二进制日志,在进行主从同步的时候,我们需要先检查服务器是否已经开启了二进制日志。
进行主从同步的内容是二进制日志,它是一个文件,在进行网络传输的过程中就一定会存在延迟(比如 500ms),这样就可能造成用户在从库上读取的数据不是最新的数据,也就是主从同步中的数据不一致性问题。比如我们对一条记录进行更新,这个操作是在主库上完成的,而在很短的时间内(比如 100ms)又对同一个记录进行了读取,这时候从库还没有完成数据的更新,那么我们通过从库读到的数据就是一条旧的记录。
如何解决主从同步数据一致性的问题
开始搭建主从复制
192.168.9.164 主服务器,读操作在164
192.168.9.165 从服务器,写操作在165
因为主需要开启bin-log,从服务器需要监听主服务器的bin-log,那么首先去主服务器开启bin-log。
先编辑主服务器164的Mysql配置文件
#vim /etc/my.cnf //对照下面三行
log_bin = mysql-bin #是开启的
binlog_format = mixed #日志文件名称
Server-id = 164 #以为server-id是惟一的,用ip最合适。
先去mysql数据的存放目录,如果不知道的情况下,可以在/etc/my.cnf中查看 datadir的位置datadir = /data/mysql
看到这些文件,就是mysql的binlog文件,说明bin-log已经开启,重启Mysql服务。
然后进入主服务器客户端,执行命令查看主服务器状态
MySQL [(none)]> show master status;
然后配置从服务器:
同样编辑/etc/my.cnf文件,把server-id修改一下,暂时把两个选项注释掉,还需要加一个relay-log(中继日志)
#vim /etc/my.cnf
#log_bin = mysql-bin
#binlog_format = mixed
relay_log = mysql-relay-bin
Server-id = 165 #以为server-id是惟一的,用ip最合适。
配置为重启Mysql。
下面就需要让从服务器监听主服务器的bin-log日志文件,这样需要做的话就是主服务器允许授权。先去主服务器授权,回到164主服务器mysql客户端执行命令授权,
MySQL [(none)]> grant replication slave on *.* to slave@192.168.9.165 identified by '123456';
MySQL [(none)]> flush privileges; //刷新权限
这句话的意思是,允许slave这个用户通过密码123456在192.168.9.165这台服务器上访问164这台master。这时候就可以允许165监听164的bin-log了。
进入从服务器,先确定从服务器是否能ping的通主服务器。然后进入从服务器客户端,设定要监听的机器。进入从服务器mysql客户端习惯性执行 stop slave 命令。
然后准备在从服务器mysql客户端执行授权命令,but,在执行之前先看看主服务器的状态
这里由154变成了602,因为刚才在主服务器执行过命令。
MySQL [(none)]> change master to
-> master_host='192.168.9.164',
-> master_port=3306,
-> master_user='slave',
-> master_password='123456',
-> master_log_file='mysql-bin.000001',
-> master_log_pos=450;
MySQL [(none)]> start slave; //OK没问题的话,开启监听。
MySQL [(none)]> show slave status\G; //查看状态
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.9.164
Master_User: replication
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000005
Read_Master_Log_Pos: 450
Relay_Log_File: mysql-relay-bin.000002
Relay_Log_Pos: 320
Relay_Master_Log_File: mysql-bin.000004
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 629
Relay_Log_Space: 527
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: d8d23a1f-341f-11e8-8102-000c29f3aa69
Master_Info_File: /data/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)
然后在主服务器做相应的Mysql增删改,同样再查看一下从服务器的数据,都发生了相应的变化~
检查主从复制通信状态
Slave_IO_State #从站的当前状态
Slave_IO_Running: Yes #读取主程序二进制日志的I/O线程是否正在运行
Slave_SQL_Running: Yes #执行读取主服务器中二进制日志事件的SQL线程是否正在运行。与I/O线程一样
Seconds_Behind_Master #是否为0,0就是已经同步了
必须都是 Yes
如果不是原因主要有以下 4 个方面:
1、网络不通
2、密码不对
3、MASTER_LOG_POS 不对 ps
4、mysql 的 auto.cnf server-uuid 一样(可能你是复制的mysql)
$ find / -name 'auto.cnf'
$ cat /data/mysql/auto.cnf
[auto]
server-uuid=6b831bf3-8ae7-11e7-a178-000c29cb5cbc # 按照这个16进制格式,修改server-uuid,重启mysql即可
OK,这样主从复制的架构就配置完成了。但是别着急,接着往下看。
现有架构问题
现有架构问题有一个Master,可以扩展多个Slave,如果Master挂掉,那么影响现有的架构,如果Master一旦挂掉,写数据不知道往哪里写了,Slave也会失去监听的Master。
解决问题
给现有的Master再去备份另外的Master,这可以实现心跳感应,其中一个Master挂掉,切换到另外一个Master。
【Mysql双主热备】
流程图解说:
现在要做的是让Slave也作为Master,首先给Slave也开启bin-log,也就是做主机的话,必须开启bin-log。而且要开始log_slave_updates,这个配置代表的是,当我们的relay_log发生改变的时候,它会通过log_slave_updates写入到从机的bin-log日志中,也就是说实现了Slave中的bin-log和Master的bin-log日志同步,保证数据一致。然后再让Master监听Slave的bin-log,这样的话必须Master也必须开启relay-log,让它作为Slave的从,这样Master和Slave互为主从。这样我们往其中任意一台数据库写数据,另外一个数据也会发生相应改变,其中一个挂掉,我们另外一个机器可以作为主机提供服务,最终达到双主热备。
达到的架构图:
两个Master,双主。然后各自的Master都有各自的Slave集群,其中一个Master挂掉,另一个Master可以提供服务,并且下面还有很多Slave,然后可以将挂掉的Master下面的Slave切换到正常的Mater上。
开始搭建双主热备
现在要做的是把Slave作为主,需要开启Slave的bin-log,编辑从机的配置文件
#vim /etc/my.cnf //按照下面的配置
log_bin = mysql-bin
binlog_format = mixed
relay_log = mysql-relay-bin
log_slave_updates = 1 #表示将relay通过这样的配置写入到bin-log中,主挂掉,从立马作为主上位。
Server-id = 165 #以为server-id是惟一的,用ip最合适。
配置完之后重启从机的Mysql服务,这样从就有作为主的能力。
接下来给主机授权,Slave要让主监听从的bin-log,刚才是在主给从授权,现在是在从给主授权
grant replication slave on *.* to master@192.168.9.164 identified by 'wt000000';
授权完成之后,让主机监听从机
编辑主机的配置文件,加上
# vim /etc/y.cnf //按照下面的配置在主机加上
relay_log = mysql-relay-bin
log_slave_updates = 1 #同理
下面重启主的Mysql服务器。
接下来做的就是让之前的主来监听从了。
进入从的Mysql终端,查看作为Master的状态,进入终端先执行reset master命令,再查看状态
接下来就可以在192.168.9.164(之前的主机)上执行change master to命令了。
MySQL [(none)]> change master to
-> master_host='192.168.9.165',
-> master_user='master',
-> master_password='wt000000',
-> master_log_file='mysql-bin.000001',
-> master_log_pos=154;
此时,164就成了165的从了。然后执行命令
MySQL [(none)]> start slave;
MySQL [(none)]> show slave status\G
MySQL [(none)]> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.9.165
Master_User: master
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 154
Relay_Log_File: mysql-relay-bin.000002
Relay_Log_Pos: 320
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: Yes //ok
Slave_SQL_Running: Yes //ok
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 154
Relay_Log_Space: 527
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 165
Master_UUID: 6b831bf3-8ae7-11e7-a178-000c29cb5cbc
Master_Info_File: /data/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)
现在两台分别是对方的主,也是对方的从。检查一下状态,没问题~
然后分别在主和从修改数据,每次的修改发现,两个服务器都会发生相应的变化。OK~双主热备配置完成~只有从还不是很完善,往下面接着看。
要实现每个主服务器下面都有从服务器。
【双主双从】
192.168.9.164 //原Master(一主)
192.168.9.165 //原Slave(二主)
192.168.9.166 //做为192.168.9.164的从
192.168.9.167 //做为192.168.9.165的从
分别编辑两个新从机的配置文件
编辑192.168.9.166的Mysql配置文件
#og_bin = mysql-bin
#binlog_format = mixed
relay_log = mysql-relay-bin
Server-id = 166 #以为server-id是惟一的,用ip最合适。
编辑192.168.9.167的Mysql配置文件
#log_bin = mysql-bin
#binlog_format = mixed
relay_log = mysql-relay-bin
Server-id = 167 #以为server-id是惟一的,用ip最合适。
接下来分别给两个新从机授权
先让192.168.9.164给192.168.9.166授权。
进入192.168.9.164的Mysql客户端授权。
MySQL [hb]> grant replication slave on *.* to slave166@192.168.9.166 identified by 'wt000000';
然后查看本机的Master状态
然后进入从机192.168.9.166,执行change master 命令
MySQL [(none)]> change master to
-> master_host='192.168.9.164',
-> master_user='slave166',
-> master_password='wt000000',
-> master_log_file='mysql-bin.000005',
-> master_log_pos=1034;
Query OK, 0 rows affected, 2 warnings (0.01 sec)
MySQL [(none)]> start slave;
MySQL [(none)]> show slave status\G
MySQL [(none)]> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.9.164
Master_User: slave166
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000005
Read_Master_Log_Pos: 1034
Relay_Log_File: mysql-relay-bin.000002
Relay_Log_Pos: 320
Relay_Master_Log_File: mysql-bin.000005
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 1034
Relay_Log_Space: 527
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 164
Master_UUID: d8d23a1f-341f-11e8-8102-000c29f3aa69
Master_Info_File: /data/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)
再让192.168.9.165给192.168.9.167授权。
这里就不重复说明了。。。。
OK,每个主服务器的从服务器也搭建完成了。。经过测试,操作其中一台主机的数据库,其他三个服务器的数据库数据也跟着发生相应的变化~
【补充】
至此我们mysql服务器的主从复制架构已经完成,但是我们现在的主从架构并不完善,因为我们的从服务上还可以进行数据库的写入操作,一旦用户把数据写入到从服务器的数据库内,然后从服务器从主服务器上同步数据库的时候,会造成数据的错乱,从而会造成数据的损坏,所以我们需要把从服务器设置成只读~如果加入了Mycat中间件,在主从架构中,如果主库宕机的情况下,从库也要立马做主库角色,那么这里最好也是关掉。方法如下:
注意:read-only = ON ,这项功能只对非管理员组以为的用户有效!
注意:read-only = ON ,这项功能只对非管理员组以为的用户有效!
上图查看出没有开启只读。那么我们修改从服务器的配置文件my.cnf,加一行代码
read-only = ON
添加完成之后重启服务即可。
注意,mysql主从架构中,从服务器要比主服务器速度慢是正常的,因为主服务器是并行的,从服务器是队列逐行执行命令操作。(特别是在事务操作中,首先会把执行事务的操作放到缓存区,然后执行成功之后才会把日志写入主服务器日志,然后主服务器把日志发送给从服务器,从服务器中继日志存储后,再逐行读取操作从服务器),Mysql默认是异步的。
事务安全配置:
比如,在主服务器上,某一个事务已经提交了,这个事务提交相关的二进制日志应该写进日志文件,二进制日志有缓冲区,意味着事务提交有些事件在缓冲区没有写进二进制日志,万一这个时候主服务器崩溃了,从服务器得不到相关的事件,那么从服务器实现不了将事务完整的结束,所以在主服务器完成的事务,从服务器有可能完成不了,如果我们能让主服务器的事务一提交,那么立即写到从服务器中,不在缓冲区停留,那么就会降低主从不一致的可能性。那么在主服务器怎么配置呢?
SHOW VARIABLES LIKE 'log%';
找到sync_binlog,为了事务安全,启动该选项
-----------------------------------------------------------
要注意的是,在确定完从库之后,要保证从服务器和主服务器的数据一致性,需要在备份主数据库数据之前,先临时锁定主数据库,保证数据一致性。
flush tables with read lock;
另外要注意的是,如果涉及到函数或者存储过程的复制,需要在/etc/my.cnf中的[mysqld]段中增加配置log_bin_trust_function_creators=true,或者在客户端设置set_log_bin_trust_function_creators =1
【Mysql读写分离】
配置读写分离在这里用的是中间件Mycat,下面简单介绍。官方地址:http://www.mycat.io/
Mycat特性
支持SQL92标准
支持Mysql、Oracle、DB2、SQL Server、PostgreSQL等数据库的常见SQL语法
遵循Mysql原生协议,跨语言、跨平台夸数据库通用中间件代理
基于心跳检查自动故障切换、支持读写分离、支持主从复制、支持分库分表等
下面开始在新的服务器下载安装Mycat,这里的IP是192.168.9.168。
下载安装Mycat
# cd /usr/local/src
# wget http://dl.mycat.io/1.6-RELEASE/Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
# tar -zxvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
# mv mycat/ /usr/local/mycat
一步步配置Mycat
进入conf目录下,查看一下文件里列表,需要配置的仅仅是这两个文件
# vim server.xml //编辑server.xml文件,按照如下配置
<!--连接mycat需要用账号root,密码就是下面的密码,还有"mydb"的库,这并不一个真正的库,只是实际库的一个映射-->
<user name="root">
<property name="password">123456</property>
<property name="schemas">mydb</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>
<!--下面的User暂时注释掉,这里的意思是只能写-->
<!--<user name="user">
<property name="password">user</property>
<property name="schemas">TESTDB</property>
<property name="readOnly">true</property>
</user>-->
# vim schema.xml //对应下面的配置
<!--name="mydb" 和serverxml配置一致,要管理的库。checkSQLschema="false" ,如果为true的话,意思是查询的时候带库.表名 。/sqlMaxLimit="100" 意思是如果sql语句没有加limit,mycat会在sql语句后面自动加limit
100。dataNode="dn1" 就是关联下面的dataNode-->
<schema name="mydb" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
</schema>
<!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"
/> -->
<!-- //database="hb4" ,这是真实的物理数据库名称-->
<!--***剩下的配置不在这里一一说明,对应配置好即可。想要了解去看手册:http://www.mycat.io/document/Mycat_V1.6.0.pdf**--->
<dataNode name="dn1" dataHost="localhost1" database="hb4" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="3"
writeType="0" dbType="mysql" dbDriver="native" switchType="2" slaveThreshold="100">
<heartbeat>show slave status</heartbeat><!--心跳检测 show slave status///详细了解去看手册-->
<!-- can have multi write hosts -->
<!--<writeHost host="hostM1" url="localhost:3306" user="root"
password="123456">
<readHost host="hostS2" url="192.168.1.200:3306" user="root" password="xxx" />
</writeHost>-->
<!--下面的配置是对应着在不同的服务器上授权的账号配置的,配置会在授权部分介绍-->
<!--第一台主机和第一台主机的从机-->
<writeHost host="hostM1" url="192.168.9.164:3306" user="mycat" password="123456" ><!--主:负责写-->
<readHost host="hostS1" url="192.168.9.166:3306" user="mycat" password="123456" /><!--从:负责读-->
</writeHost>
<!--第二台主机和第二台主机的从机-->
<writeHost host="hostM2" url="192.168.9.165:3306" user="mycat" password="123456" ><!--主:负责写-->
<readHost host="hostS2" url="192.168.9.167:3306" user="mycat" password="123456" /><!--从:负责读-->
</writeHost>
<!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
</dataHost>
配置完成之后开启mycat服务
[root@localhost mycat]# bin/mycat start //命令启动
Starting Mycat-server...
[root@localhost mycat]# bin/mycat status //命令查看状态
Mycat-server is running (3593).
[root@localhost mycat]# netstat -tunlp | grep 8066 //查看端口
tcp 0 0 :::8066 :::* LISTEN 3595/java
授权配置
MySQL [hb4]> grant insert,update,delete,select on *.* to mycat@192.168.9.168 identified by '123456'; //在两台主服务器的Mysql终端分别执行
MySQL [hb4]> grant select on *.* to mycat@192.168.9.168 identified by '123456'; //在两台从服务器Mysql终端分别执行
确定启动成功,接下来通过项目开发机链接一下试试
# mysql -uroot -p123456 -h192.168.9.168 -P8066
MySQL [(none)]> show databases;
+----------+
| DATABASE |
+----------+
| mydb |
+----------+
1 row in set (0.01 sec)
然后在之前的主机上操作数据表,然后通过此链接继续查看数据表,数据也发生了相应的变化。这样我们在项目中连接的Mysql地址端口做好更改,那么就连接到了mycat服务器了。然后分别在两台主服务器做写入测试;在开发机链接做读数据测试。再分别查看mycat目录下logs目录下的mycat.log文件,发现不同的读写分发到不同的服务器~最后试着挂掉一台服务器,重新做写操作,和查询操作,随时跟踪每台服务器的数据和mycat.log日志。发现,ok~没任何问题!
【Mysql分库分表】
如果数据量达到了亿级别,那么这样管理的话,压力会很大,解决这个问题可以把库里数据量比较大的表给单独分散,分散到其他库中,这样库承受的压力就会小很多了。拿一个表做演示
确定要分库的表
拿库里的hp_user来演示,首先查看一下表结构。
MySQL [hb4]> show create table hp_user;
| hp_user | CREATE TABLE `hp_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) DEFAULT NULL COMMENT '用户名',
`nickname` varchar(40) DEFAULT NULL COMMENT '用户昵称',
`password` varchar(40) DEFAULT NULL COMMENT '密码',
`email` varchar(40) DEFAULT NULL COMMENT '邮箱',
`avatar` varchar(150) DEFAULT NULL COMMENT '头像',
`zip_avatar` varchar(150) DEFAULT NULL COMMENT '压缩头像',
`birthday` int(11) DEFAULT '0' COMMENT '出生日期',
`gender` tinyint(4) DEFAULT '1' COMMENT '性别, 1:男, 2:女',
`slogan` varchar(255) NOT NULL DEFAULT '' COMMENT '标语',
`level` tinyint(4) DEFAULT NULL COMMENT '等级',
`total_score` int(11) DEFAULT '0' COMMENT '用户的积分总额',
`score` int(11) NOT NULL DEFAULT '0' COMMENT '积分',
`freeze_score` int(11) NOT NULL DEFAULT '0' COMMENT '购买商品时冻结积分',
`point` int(11) DEFAULT '0' COMMENT '点数(预留)',
`invite_code` char(6) DEFAULT NULL COMMENT '邀请码(自己的)',
`invite_by` int(11) NOT NULL DEFAULT '0' COMMENT '被邀请id(user_info.id)',
`status` tinyint(4) DEFAULT '1' COMMENT '用户状态, 1:可用, 0: 不可用',
`create_time` int(11) DEFAULT NULL COMMENT '创建时间',
`register_ip` varchar(20) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '注册ip',
`login_time` int(11) DEFAULT NULL COMMENT '登录时间',
`forbidden_time` int(11) DEFAULT NULL COMMENT '禁用时间',
`operator_id` int(11) DEFAULT '0' COMMENT '操作人',
`is_delete` tinyint(4) DEFAULT '0' COMMENT '是否删除',
`reason` varchar(100) DEFAULT NULL COMMENT '审核不通过请备注原因',
`is_examine` tinyint(4) DEFAULT '0' COMMENT '是否通过,1:否,0:是',
`token` varchar(32) DEFAULT NULL COMMENT '用户token',
`alipay_code` varchar(255) DEFAULT '' COMMENT '支付宝账户',
`alipay_name` varchar(255) DEFAULT '' COMMENT '支付宝名字',
`wx_id` char(50) NOT NULL DEFAULT '' COMMENT '微信oppenid',
`level_time` varchar(20) NOT NULL COMMENT '成为等级时间',
`assessment_time` varchar(20) NOT NULL COMMENT '上次考核时间',
`level_type` int(11) NOT NULL COMMENT '等级状态1首次2不是首次',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
KEY `invite_code` (`invite_code`),
KEY `status` (`status`) USING BTREE,
KEY `is_delete` (`is_delete`) USING BTREE,
KEY `invite_by` (`invite_by`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息表' |
创建要分的三个库的对应的表
MySQL [hb4]> create database user_db1;
Query OK, 1 row affected (0.00 sec)
MySQL [hb4]> create database user_db2;
Query OK, 1 row affected (0.10 sec)
MySQL [hb4]> create database user_db3;
Query OK, 1 row affected (0.02 sec)
创建好库之后,分别use 三个库,复制建表语句,创建数据表,以后有数据需要往表里写了,那么做一个hash处理,做分配,将入库数据分散开,减小单库压力。
配置分库分表
#vim schemaxml //对应好下面的配置
<schema name="mydb" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
<!--name="hp_user":管理的表,primaryKey="id":表的主键id,dataNode="user1,user2,user3",因为创建了三个库,rule="sharding-by-murmur":对表分片的管理规则-->
<table name="hp_user" primarykey="id" dataNode="user1,user2,user3" rule="sharding-by-murmur" />
</schema>
<!--分别对应的三个库--->
<dataNode name="user1" dataHost="localhost1" database="user_db1" />
<dataNode name="user2" dataHost="localhost1" database="user_db2" />
<dataNode name="user3" dataHost="localhost1" database="user_db3" />
这样配置完毕,但是在做分片数据之前,思考一下,插入数据的时候怎么能保证不发生冲突呢?也就是说,比如第一条数据进入user_db1这个库,数据主键id从1开始,第二条的时候进入user_db2,也是从1开始,将来查的时候肯定会查出多条id为1的,怎么避免呢?可以在插入的时候指定id,如果不指定主键id呢?
mycat提供了一个自动生成主键id的配置解决这个问题。
找到conf下的rule.xml,每个tableRule就代表一个规则。
找到sharding-by-murmur,一致性哈希。
# vim rulexml //对应配置
<tableRule name="sharding-by-murmur">
<rule>
<columns>id</columns> <!--这里是id,表示对id一致性哈希处理-->
<algorithm>murmur</algorithm>
</rule>
</tableRule>
vim server.xml //对应配置
<property name="sequnceHandlerType">2</property> //默认是2,改为0,0代表本地文件进行配置
# vim sequence_conf.properties //
#default global sequence //这里是自带的说明
GLOBAL.HISIDS= //隐藏的
GLOBAL.MINID=10001 //最小id
GLOBAL.MAXID=20000 //最大id
GLOBAL.CURID=10000 //当前id
# self define sequence
#配置自己的 //这里是配置的user节点
USER.HISIDS=
USER.MINID=10001
USER.MAXID=20000
USER.CURID=10000
配置好重启mycat。
数据测试
执行SQL还是在项目服务器上,之前连接Mycat的服务器终端执行SQL
INSERT INTO `hp_user` (
`id`,
`username`,
`nickname`,
`password`,
`email`,
`avatar`,
`zip_avatar`,
`birthday`,
`gender`,
`slogan`,
`level`,
`total_score`,
`score`,
`freeze_score`,
`point`,
`invite_code`,
`invite_by`,
`status`,
`create_time`,
`register_ip`,
`login_time`,
`forbidden_time`,
`operator_id`,
`is_delete`,
`reason`,
`is_examine`,
`token`,
`alipay_code`,
`alipay_name`,
`wx_id`,
`level_time`,
`assessment_time`,
`level_type`
)
VALUES
(
next value for MYCATSEQ_USER, //这里一定要注意,是mycat的哈希规则
'12323232323',
'测试用户9',
'14e1b600b1fd579f47433b88e8d85291',
NULL,
'[\"\\/upload\\/2017\\/03\\/29\\/2eqcj0jh10skfgtv.jpg\"]',
'[\"\\/upload\\/2017\\/03\\/29\\/zip_2eqcj0jh10skfgtv.jpg\"]',
'19910201',
'1',
'nonononononononononononononononono',
'2',
'111117',
'111117',
'0',
'0',
'PRNLT',
'66',
'1',
'1483002221',
'124.202.200.186',
'1483002221',
NULL,
'0',
'0',
NULL,
'0',
'6b05171019a33823aa182364a1e643fa',
'18798048650',
'测试9',
'',
'1496723153',
'',
'0'
);
写入4条数据,然后查看。
MySQL [mydb]> select id,username,nickname,password,email,alipay_name from hp_user;
+-------+-------------+---------------+----------------------------------+-------+---+
| id | username | nickname | password | email | alipay_name |
+-------+-------------+---------------+----------------------------------+-------+---+
| 10006 | 13434343434 | 测试用户6 | 14e1b600b1fd579f47433b88e8d85291 | NULL | 测试6 |
| 10008 | 14455554444 | 测试用户8 | 14e1b600b1fd579f47433b88e8d85291 | NULL | 测试8 |
| 10007 | 16565656565 | 测试用户7 | 14e1b600b1fd579f47433b88e8d85291 | NULL | 测试7 |
| 10009 | 12323232323 | 测试用户9 | 14e1b600b1fd579f47433b88e8d85291 | NULL | 测试9 |
+-------+-------------+---------------+----------------------------------+-------+----+
然后再在真实数据库服务器上(主库)查看真实数据
MySQL [user_db2]> use user_db3;
Database changed
MySQL [user_db3]> select id,username,nickname,password,email,alipay_name from hp_user;
Empty set (0.00 sec)
MySQL [user_db3]> select id,username,nickname,password,email,alipay_name from hp_user;
Empty set (0.00 sec)
MySQL [user_db3]> use user_db2;
Database changed
MySQL [user_db2]> select id,username,nickname,password,email,alipay_name from hp_user;
+-------+-------------+---------------+----------------------------------+-------+-------------+
| id | username | nickname | password | email | alipay_name |
+-------+-------------+---------------+----------------------------------+-------+-------------+
| 10006 | 13434343434 | 测试用户6 | 14e1b600b1fd579f47433b88e8d85291 | NULL | 测试6 |
| 10008 | 14455554444 | 测试用户8 | 14e1b600b1fd579f47433b88e8d85291 | NULL | 测试8 |
+-------+-------------+---------------+----------------------------------+-------+-------------+
2 rows in set (0.00 sec)
MySQL [user_db2]> use user_db1;
Database changed
MySQL [user_db1]> select id,username,nickname,password,email,alipay_name from hp_user;
+-------+-------------+---------------+----------------------------------+-------+-------------+
| id | username | nickname | password | email | alipay_name |
+-------+-------------+---------------+----------------------------------+-------+-------------+
| 10007 | 16565656565 | 测试用户7 | 14e1b600b1fd579f47433b88e8d85291 | NULL | 测试7 |
| 10009 | 12323232323 | 测试用户9 | 14e1b600b1fd579f47433b88e8d85291 | NULL | 测试9 |
+-------+-------------+---------------+----------------------------------+-------+-------------+
2 rows in set (0.00 sec)
OK~配置完毕