Java面试题033:一文深入了解MySQL(5)

Java面试题029:一文深入了解MySQL(1)

Java面试题030:一文深入了解MySQL(2)

Java面试题031:一文深入了解MySQL(3)

Java面试题032:一文深入了解MySQL(4)

MySQL读写分离和分库分表分别从不同角度提高系统的处理效率,读写分离侧重于处理高并发的场景,分库分表是从数据量的角度去提升单次读写的效率。

1、MySQL读写分离

当面临大量的读操作,造成写操作的效率降低时,将数据库的读和写操作分开,分别由不同的数据库服务器处理,可以有效地分担数据库的压力,优化系统性能。

读写分离是数据库架构中的一种常见策略,主要用于解决以下几种情况:

  • 当数据库的并发读写请求非常高时,单一的数据库服务器可能无法承受这样的压力,这时就需要通过读写分离来分散压力。读请求可以分发到多个从库,写请求则由主库处理。

  • 资源优化,读写分离可以使得主库专注于处理写操作和事务性的操作,从库则处理读操作。这样可以根据主从库的不同特性进行硬件和系统资源的优化配置。

  • 数据备份和故障转移,这是一个间接作用。通过读写分离,可以实现数据的实时备份。当主库出现故障时,可以迅速切换到从库,保证服务的持续可用。

  • 提高查询性能,读写分离可以将复杂的查询操作分散到多个从库上执行,从而提高查询性能。

(1)实现模式

  • 主从模式(Master-Slave):一个主库处理所有写操作,并将数据变更同步到一个或多个从库,从库处理读请求。

​ 优点:简单易实现,适用于大多数场景。

​缺点:主库可能成为瓶颈,从库延迟可能影响数据一致性。

  • 主主模式(Master-Master):多个主库同时处理读写请求,每个主库都可以接收写操作,并将变更同步到其他主库。

​ 优点:高可用性和高写入吞吐量。

​ 缺点:数据冲突和一致性问题更加复杂,配置和管理难度大。

  • 多级复制(Multi-Tier Replication):在主从架构基础上,增加中间层从库,形成多级复制结构。例如,主库 -> 中间从库 -> 最终从库。

​优点:分担主库和中间从库的压力,提高读取性能。

​ 缺点:配置和维护复杂,延迟可能累积。

(2)实现方式

  • 应用层实现(程序代码实现):在应用程序代码中手动管理数据库连接,将写请求发送到主库,读请求发送到从库。

  • ProxySQL中间件:高性能的 MySQL 代理,支持动态负载均衡、读写分离、查询缓存等功能。

  • ShardingSphere:阿里巴巴开源的分布式数据库中间件,支持分库分表、读写分离、分布式事务等。

2、ProxySQL

ProxySQL是基于MySQL的一款开源的中间件的产品,是一个灵活的MySQL代理层,可以实现读写分离,支持 Query路由功能,支持动态指定某个SQL进行缓存,

支持动态加载配置信息(无需重启 ProxySQL 服务),支持故障切换和SQL的过滤功能。

ProxySQL中管理结构

在ProxySQL,6032端口共五个库: main、disk、stats 、monitor、stats_history

  • main:

main 库中有如下信息:

mysql_servers: 后端可以连接 MySQL 服务器的列表

mysql_users: 配置后端数据库的账号和监控的账号。

mysql_query_rules: 指定 Query 路由到后端不同服务器的规则列表。

mysql_replication_hostgroups : 节点分组配置信息

注: 表名以 runtime_开头的表示ProxySQL 当前运行的配置内容,不能直接修改。不带runtime_是下文图中Mem相关的配置。

  • disk : 持久化的磁盘的配置

  • stats: 统计信息的汇总

  • monitor:监控的收集信息,比如数据库的健康状态等

  • stats_history:ProxySQL 收集的有关其内部功能的历史指标

admin管理接口,默认端口为6032 。该端口用于查看、配置ProxySQL。通过6032管理端口登入后,默认就是main库,所有的配置更改都必须在这个库中进行,disk存档库不会直接受到影响。

接收SQL语句的接口,默认端口为6033,这个接口类似于MySQL的3306端口。

ProxySQL的多层配置

整套配置系统分为三层:

  • RUNTIME:代表 ProxySQL 当前正在使用的配置,无法直接修改此配置,必须要从下一层 (MEM层)"load" 进来。

  • MEMORY:MEMORY 层上面连接 RUNTIME 层,下面disk持久层。这层可以在线操作 ProxySQL配置,随便修改,不会影响生产环境。确认正常之后在加载达到RUNTIME 和持久化的磁盘上。修改方法: insert、update、delete、select。

  • DISK和CONFIG FILE:持久化配置信息。重启时,可以从磁盘快速加载回来

使用proxysql

主要需要完成以下几项内容的配置:

1、配置监控账号。监控账号用于检测后端mysql实例是否健康(是否能连接、复制是否正常、复制是否有延迟等)。

2、到后端mysql实例创建监控账号。

3、配置后端mysql实例连接信息。实例连接信息存储在mysql_servers表。

4、配置连接proxysql和后端实例的账号。账号信息存储在mysql_users表。

5、配置查询路由信息。路由信息存储在mysql_query_rules表。

6、配置后端mysql集群信息。根据后端mysql集群架构,配置分别存储在mysql_replication_hostgroups、mysql_group_replication_hostgroups、runtime_mysql_galera_hostgroups、runtime_mysql_aws_aurora_hostgroups等表中。

7、根据具体需要,调优相关参数。参数存储在global_variables表。

(1)从库设定read_only参数

ProxySQL 会根据MySQL server 的read_only 的取值将服务器进行分组。 read_only=0 的server,会被分到写组,read_only=1 的server,则被分到读组。所以需要将从库设置: read_only=1;

sql 复制代码
vim /etc/my.cnf
 [mysqld]
 read_only=1

(2)配置读写组编号

在ProxySQL 上 mysql_replication_hostgroup表中(自带,无需创建)

sql 复制代码
mysql_replication_hostgroups (
     writer_hostgroup INT CHECK (writer_hostgroup>=0) NOT NULL PRIMARY KEY,
     reader_hostgroup INT NOT NULL CHECK (reader_hostgroup<>writer_hostgroup AND reader_hostgroup>=0),
     check_type VARCHAR CHECK (LOWER(check_type) IN ('read_only','innodb_read_only','super_read_only','read_only|innodb_read_only','read_only&innodb_read_only')) NOT NULL DEFAULT 'read_only',
     comment VARCHAR NOT NULL DEFAULT '', UNIQUE (reader_hostgroup)
)

创建组:(定义写为1,读为0),数字可以自定义

sql 复制代码
[root@db03 ~]# mysql -uadmin -padmin -h127.0.0.1 -P6032

MySQL> insert into mysql_replication_hostgroups (writer_hostgroup,reader_hostgroup,comment) values (1,0,'proxy');
Query OK, 1 row affected (0.00 sec)
sql 复制代码
 #加载到当前生效
 MySQL [(none)]> load mysql servers to runtime;
 Query OK, 0 rows affected (0.01 sec)
 ​
 #永久保存
 MySQL [(none)]> save mysql servers to disk;
 Query OK, 0 rows affected (0.02 sec)

ProxySQL会根据serverd的/etc/my.cnf配置文件中额的read_only的取值将服务器进行分组。read_only=0的server,master被分到编号为1的写组

(3)添加主机到ProxySQL

sql 复制代码
insert into mysql_servers(hostgroup_id,hostname,port) values (1,'172.16.100.21',3306);
insert into mysql_servers(hostgroup_id,hostname,port) values (0,'172.16.100.22',3306);
insert into mysql_servers(hostgroup_id,hostname,port) values (0,'172.16.100.23',3306);
sql 复制代码
//加载主从节点信息到runtime

load mysql servers to runtime;

save mysql servers to disk;

(4)配置 ProxySQL 所需账户

在 MySQL 的Master节点上创建 ProxySQL 的监控账户和对外访问账户

sql 复制代码
 #监控账户
 mysql>create user 'monitor'@'172.16.%.%' identified with mysql_native_password by 'Monitor@123.com';
 grant all privileges on *.* to 'monitor'@'172.16.%.%' with grant option;
 ​
 #proxysql 的对外访问账户
 mysql>create user 'proxysql'@'172.16.%.%' identified with mysql_native_password by '2025pro0702';
 grant all privileges on *.* to 'proxysql'@'172.16.%.%' with grant option;

(5)为ProxySQL监控MySQL后端节点

sql 复制代码
#切换到monitor库
 MySQL [(none)]> use monitor​
 Database changed
 MySQL [monitor]> set mysql-monitor_username='monitor';
 Query OK, 1 row affected (0.00 sec)
 ​
 MySQL [monitor]> set mysql-monitor_password='Monitor@123.com';
 Query OK, 1 row affected (0.00 sec)
 ​

上面这两句是修改变量的方式还可以在main库下面用sql语句方式修改

在main下修改:

sql 复制代码
 MySQL [monitor]> use main
 Database changed
 MySQL [main]> UPDATE global_variables SET variable_value='monitor' WHERE variable_name='mysql-monitor_username';
 Query OK, 1 row affected (0.00 sec)
 ​
 MySQL [main]> UPDATE global_variables SET variable_value='Monitor@123.com' WHERE variable_name='mysql-monitor_password';
 Query OK, 1 row affected (0.00 sec)

修改后,保存到runtime和disk

sql 复制代码
 MySQL [monitor]> load mysql variables to runtime;
 MySQL [monitor]> save mysql variables to disk;

(6)ProxySQL配置对外访问账号

ProxySQL 的mysql_users表结构如下:

sql 复制代码
mysql_users (
     username VARCHAR NOT NULL,
     password VARCHAR,
     active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 1,
     use_ssl INT CHECK (use_ssl IN (0,1)) NOT NULL DEFAULT 0,
     default_hostgroup INT NOT NULL DEFAULT 0,
     default_schema VARCHAR,
     schema_locked INT CHECK (schema_locked IN (0,1)) NOT NULL DEFAULT 0,
     transaction_persistent INT CHECK (transaction_persistent IN (0,1)) NOT NULL DEFAULT 1,
     fast_forward INT CHECK (fast_forward IN (0,1)) NOT NULL DEFAULT 0,
     backend INT CHECK (backend IN (0,1)) NOT NULL DEFAULT 1,
     frontend INT CHECK (frontend IN (0,1)) NOT NULL DEFAULT 1,
     max_connections INT CHECK (max_connections >=0) NOT NULL DEFAULT 10000,
     attributes VARCHAR CHECK (JSON_VALID(attributes) OR attributes = '') NOT NULL DEFAULT '',
     comment VARCHAR NOT NULL DEFAULT '',
     PRIMARY KEY (username, backend),
     UNIQUE (username, frontend)
)
sql 复制代码
insert into mysql_users (username,password,default_hostgroup,transaction_persistent) values ('proxysql','2025pro0702',1,1);


load mysql users to runtime;
save mysql users to disk;

transaction_persistent 如果为1,则一个完整的SQL只可能路由到一个节点;这点非常重要,主要解决这种情况:一个事务有混合的读操作和写操作组成,事务未提交前,如果事务中的读操作和写操作路由到不同节点,那么读取到的结果必然是脏数据。所以一般情况下,该值应该设置为1,尤其是业务中使用到事务机制的情况(默认为0)

mysql_users 表有不少字段,最主要的三个字段username,password,default_hostgroup 。

(7)添加读写分离规则(mysql_query_rules)

基于用户的路由:

sql 复制代码
Insert into mysql_users(username,password,default_hostgroup) 
values('writer','123',10),('reader','123',20);
load mysql users to runtime;
save mysql users to disk;
 
 
insert into mysql_query_rules(rule_id,active,username,destination_hostgroup,apply) 
values(1,1,'writer',10,1),(2,1,'reader',20,1);
 
load mysql query rules to runtime;
save mysql query rules to disk;

基于端口的路由:

sql 复制代码
## 修改ProxySQL监听SQL流量的端口号,监听多端口上。
set mysql-interfaces='0.0.0.0:6033;0.0.0.0:6034';
save mysql variables to disk;
## 重启生效
systemctl restart proxysql
 
## 设定路由规则
 
insert into mysql_query_rules(rule_id,active,proxy_port,destination_hostgroup,apply) 
values(1,1,6033,10,1), (2,1,6034,20,1);
 
load mysql query rules to runtime;
save mysql query rules to disk;

还可以基于监听地址(修改字段proxy_addr即可),也可以基于客户端地址(修改字段client_addr字段即可)。

基于正则表达式的路由:

sql 复制代码
 insert into mysql_query_rules(rule_id,active,match_pattern,destination_hostgroup,apply) 
 values(1,1,'^select .* for update$',1,1);
 ​
 insert into mysql_query_rules(rule_id,active,match_pattern,destination_hostgroup,apply) 
 values(2,1,'^select',0,1);
  1. 表示像select * from xxx for update这种语句都会分到到写组,2)表示像select这种语句都会被分配到读组。
相关推荐
萧曵 丶3 分钟前
事务ACID特性详解
数据库·事务·acid
fox_mt16 分钟前
AI Coding - ClaudeCode使用指南
java·ai编程
毕设源码-郭学长33 分钟前
【开题答辩全过程】以 基于SSM的高校运动会管理系统的设计与实现为例,包含答辩的问题和答案
java·eclipse
qq_54702617935 分钟前
Maven 使用指南
java·maven
kejiayuan42 分钟前
CTE更易懂的SQL风格
数据库·sql
xiaolyuh12344 分钟前
Arthas修改类(如加日志)的实现原理
java
kaico20181 小时前
MySQL的索引
数据库·mysql
栗子叶1 小时前
Java对象创建的过程
java·开发语言·jvm
学历真的很重要1 小时前
LangChain V1.0 Context Engineering(上下文工程)详细指南
人工智能·后端·学习·语言模型·面试·职场和发展·langchain
有一个好名字1 小时前
力扣-从字符串中移除星号
java·算法·leetcode