一、什么是读写分离
将数据库分为主库和从库,主库只写数据,多个从库只读取数据。
二、读写分离解决什么
读写分离主要用于解决数据库读取的性能瓶颈。当然有人说,解决读取的瓶颈,可以用缓存解决。确实,但如果已经用了缓存,还是存在瓶颈呢?这个时候我们就会想到负载均衡,通过多个从库分担读取的请求来解决。
三、数据库其他的瓶颈
如单表数据量非常大,读取、查询很慢,如何提升性能? 首先,我们想到的是数据库切分。数据库切分又分为水平切分和垂直切分。水平切分,就是把表中的数据分到多个库或表中,比如按地区,同一个省的数据分到一个库或表中,这样一个大表就分成多个小的库或表了。垂直切分,就是把表中的字段分到两个表中,再将两个表关联起来,比如一个文章表(有标题、作者、发布时间、内容、关键词、分类等字段),可以把内容分离出来单独一个表存,再与文章表关联。 其次,可以用搜索引擎来处理,特别是有全文检索需求的。可以用Solr或Elasticsearch,生成索引用来查询。 还有,换数据库,不用MySQL了。(这个大家再找找资料比如TiDB等)
四、MySQL5.7.10读写分离部署
1、Proxysql
Proxysql是一个C++开发的、轻量级、高性能MySQL中间件实现了读写分离、sql路由、简单的分库分表、缓存、节点监控。
2、服务器
| IP | 角色 | server_id | 说明 |
|---|---|---|---|
| 192.168.137.31 | Proxysql | null | 读写分离中间件 |
| 192.168.137.32 | Master | 32 | 数据库主库 |
| 192.168.137.33 | Slave | 33 | 数据库从库 |
MySQL5.7.10数据库安装:看我的另一篇文章,CentOS7安装Mysql5.7
Proxysql安装:看我的另一篇文章,CentOS7安装proxysql-1.4.16
3、主库配置
[client]
port=3306
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
[mysqld]
character_set_server=utf8mb4
init_connect='SET NAMES utf8mb4'
basedir=/usr/java/mysql-5.7.10-linux-glibc2.5-x86_64
datadir=/usr/java/mysql-5.7.10-linux-glibc2.5-x86_64/data
socket=/tmp/mysql.sock
#不区分大小写
lower_case_table_names = 1
#不开启sql严格模式
sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
log-error=/var/log/mysqld.log
pid-file=/usr/java/mysql-5.7.10-linux-glibc2.5-x86_64/data/mysqld.pid
#以下为主从配置
#主数据库端ID号
server-id=32
#开启及设置二进制日志文件名称
log-bin=master-bin
binlog_format = MIXED
#将从服务器从主服务器收到的更新记入到从服务器自己的二进制日志文件中
log-slave-updates
#控制binlog的写入频率。每执行多少次事务写入一次(这个参数性能消耗很大,但可减小MySQL崩溃造成的损失)
sync-binlog=1
#这个参数一般用在主主同步中,用来错开自增值, 防止键值冲突
auto_increment_offset = 1
#这个参数一般用在主主同步中,用来错开自增值, 防止键值冲突
auto_increment_increment = 1
#二进制日志自动删除/过期的天数。默认值为0,表示不自动删除。
expire_logs_days =7
#将函数复制到slave
log_bin_trust_function_creators = 1
#binlog_cache_size = 128m
#max_binlog_cache_size = 512m
#max_binlog_size = 256M
#不需要同步的数据库
binlog-ignore-db = mysql
binlog_ignore_db = information_schema
binlog_ignore_db = performation_schema
binlog_ignore_db = sys
#要同步的数据库
binlog-do-db = test
binlog-do-db = shiro
重启主库
查看log_bin是否开启
show variables like 'log_bin';
查看master状态
show master status\G;
4、从库配置
[client]
port=3306
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
[mysqld]
character_set_server=utf8mb4
init_connect='SET NAMES utf8mb4'
basedir=/usr/java/mysql-5.7.10-linux-glibc2.5-x86_64
datadir=/usr/java/mysql-5.7.10-linux-glibc2.5-x86_64/data
socket=/tmp/mysql.sock
#不区分大小写
lower_case_table_names = 1
#不开启sql严格模式
sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
log-error=/var/log/mysqld.log
pid-file=/usr/java/mysql-5.7.10-linux-glibc2.5-x86_64/data/mysqld.pid
#主从同步配置
server-id=33
#read_only设置数据库为只读,防止从库数据修改后,主从数据不一致,但是有Super权限的账号还是有写的权限,所以要某个账号只读的话,可以去掉账号的Super权限
read_only=1
log-slave-updates
#指定slave要复制哪个库
#replicate-do-db = test
#不复制这个库
#replicate-ignore-db=nas
#MySQL主从复制的时候,当Master和Slave之间的网络中断,但是Master和Slave无法察觉的情况下(比如防火墙或者路由问题)。Slave会等待slave_net_timeout设置的秒数后,才能认为网络出现故障,然后才会重连并且追赶这段时间主库的数据
slave-net-timeout = 60
log_bin_trust_function_creators = 1
5、重启主、从库
service mysql stop
service mysql start
6、连接从库,配置主库信息
/usr/java/mysql-5.7.10/bin/mysql -uroot -p
# 输出入密码,登录后执行: master_log_file、master_log_pos和上面master的状态一致;
change master to
master_host='192.168.137.32',
master_user='root',
master_password='123abc',
master_port=3306,
master_log_file='master-bin.000002',
master_log_pos=154;
# 启动从库
start slave;
查看同步状态
show slave status\G;
坑点:
(1)配置完启动,主从无法同步,查看从节点状态,发现Slave_IO_Running: No,查看从节点mysqld.log,报错: master and slave have equal MySQL server UUIDs;这是因为主从mysql是拷贝的,/data下的auto.conf里的UUID相同,修改从节点的UUID后重启,主从同步正常
7、开启复制多线程(建议开启)
因数据库有多个客户端或应用连接,如果一个sql执行较长或死锁,主服务器的sql积压,没有同步到从库,就导致主从不一致,因此建议开启多线程(MySQL5.7支持)
show variables like 'slave_parallel%';
stop slave;
set global slave_parallel_type='logical_clock';
set global slave_parallel_workers=100; #大小根据需要设置
start slave;
show processlist;
8、Proxysql配置
(1)增加MySQL节点
使用mysql客户端连接到Proxysql的管理接口,默认管理员账号、密码都是admin 如果没有安装mysql,可以直接解压mysql的tar包使用mysql客户端。
/usr/java/mysql-5.7.10-linux-glibc2.5-x86_64/bin/mysql -uadmin -padmin -P6032 -h127.0.0.1 --prompt 'admin>'
增加MySQL服务器节点
insert into mysql_servers(hostgroup_id,hostname,port) values(10,'192.168.137.32',3306);
insert into mysql_servers(hostgroup_id,hostname,port) values(10,'192.168.137.33',3306);
查看是否成功
select * from mysql_servers;
加载到runtime,并保存到disk
load mysql servers to runtime;
save mysql servers to disk;
(2)监控MySQL节点
设置监控用户和密码
set mysql-monitor_username='root';
set mysql-monitor_password='123abc';
修改后,加载到RUNTIME,并保存到disk
load mysql variables to runtime;
save mysql variables to disk;
设置分组,修改mysql_replication_hostgroups表,该表只有3个字段:第一个字段名为writer_hostgroup,第二个字段为reader_hostgroup,第三个字段为注释字段,可随意写。
insert into mysql_replication_hostgroups values(10,20,'llf');
查看:
select hostgroup_id,hostname,port,status,weight from mysql_servers;
都在10组
生效
load mysql servers to runtime;
save mysql servers to disk;
生效后查看,模块就会开始监控后端的read_only值,当监控到read_only值后,就会按照read_only的值将某些节点自动移动到读/写组,当然,如果你的从库没有配置read_only = 1,那么33还是在10组。
(3)配置MySQL账号
这里我直接使用root账户
insert into mysql_users(username,password,default_hostgroup) values('root','123abc',10);
# 生效
load mysql users to runtime;
save mysql users to disk;
查看:
select * from mysql_users;
(4)设置事务持久化
update mysql_users set transaction_persistent=1 where username='root';
# 生效
load mysql users to runtime;
save mysql users to disk;
(5)配置读写分离路由
insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)
VALUES (1,1,'^SELECT.*FOR UPDATE$',10,1),
(2,1,'^SELECT',20,1);
# 生效
load mysql query rules to runtime;
save mysql query rules to disk;
查看读组
/usr/java/mysql-5.7.10-linux-glibc2.5-x86_64/bin/mysql -uroot -p123abc -P6033 -h127.0.0.1 -e 'select @@server_id'
查看写组
/usr/java/mysql-5.7.10-linux-glibc2.5-x86_64/bin/mysql -uroot -p123abc -P6033 -h127.0.0.1 -e 'start transaction;select @@server_id'
读写分离就配置好了。
(6)修改mysql版本
update global_variables set variable_value="5.7.10" where variable_name='mysql-server_version';
#那个版本号8.0.4 你可以随意改 只要是8.0以上的就行
load mysql variables to run;
save mysql variables to disk;