深入解析MySQL(10)——基于Apache ShardingSphere的高性能架构详解


🔥我的主页: 九转苍翎
⭐️个人专栏: 《Java SE 》 《Java集合框架系统精讲》 《MySQL高手之路:从基础到高阶 》 《计算机网络 》 《Java工程师核心能力体系构建》
天行健,君子以自强不息。


MySQL版本:8.0.44
Linux操作系统版本:Ubuntu 24.04 LTS
Apache ShardingSphere版本:5.3.2 (已免费上传至我的资源)
JDBC驱动程序版本:8.0.30(已免费上传至我的资源)

1.理论 - 高性能架构模式

1.1 读写分离

  • 读操作写操作 分配到不同的数据库节点上,分散数据库读写操作的压力
  • 单机架构读写分离架构 的区别演示

1.2 数据分片

在关系型数据库中,索引普遍采用 B+ 树结构。随着业务数据不断积累,单表数据量膨胀会导致 B+ 树高度增加,每次查询需要更多的磁盘 I/O,严重拖慢查询效率 。此外,集中式数据库在高并发场景下容易成为系统瓶颈。为解决性能与扩展性问题,必须实施数据分片(Sharding),将数据分散存储到多个数据库节点

1.2.1 垂直分片

垂直分片的核心是按业务功能或数据属性进行纵向拆分。它将一个包含多个字段的"宽表"或一个包含多张表的数据库,按照功能模块、访问频率、数据类型等维度,拆分成不同的数据库或表组

  • 垂直分库
  • 垂直分表

1.2.2 水平分片

水平分片的核心是按数据记录进行横向拆分。它将单张数据量过大的表,按照某种规则(如ID范围、哈希值、时间等)将数据行分散存储到多个结构相同的数据库或表中

  • 水平分表 :将原来一张表中的数据根据某种规则拆分到多个表中,将拆分出的多张表尽量放在同一个数据库,主要是避免跨库事务。水平分表后会有效降低 B+ 树高度,从而减少磁盘 I/O

  • 水平分库 :如果单表切分之后,单台服务器依然无法满足数据库性能要求,那么就需要将多个表分散在不同的数据库服务器上

1.3 读写分离和数据分片

2.实践方式 - 高性能架构模式

2.1 代码封装

  • 代码侵入性强:分片逻辑与业务代码深度耦合
  • 维护困难:分片逻辑散落在代码各处,难以统一管理。变更分片策略需要修改代码并重新发布

2.2 中间件封装

3.Apache ShardingSphere

Apache ShardingSphere 是一款开源的分布式数据库解决方案,旨在不替换底层数据库的前提下,通过插件化架构增强其数据处理与治理能力 。截止2026年1月,它一共推出ShardingSphere-JDBCShardingSphere-Proxy两款产品,分别面向不同的部署与使用场景

3.1 ShardingSphere-JDBC(了解)

定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务

3.2 ShardingSphere-Proxy

定位为独立的透明化数据库代理服务。它通过原生实现数据库二进制网络协议,屏蔽了后端数据库集群的复杂性,为所有客户端提供一个统一的、标准的数据库访问入口(支持多种语言)

  • 简单来说,可以将 ShardingSphere-Proxy 理解为一个"数据库网关"。 应用程序和DBA像操作一个普通的、单点的MySQL服务器一样去操作它,而背后所有的数据分片、路由等分布式逻辑,则由Proxy完全在内部处理并隐藏


工作原理

  1. 客户端发送 SQL 语句到 ShardingSphere-proxy。
  2. ShardingSphere-proxy 接收 SQL 语句并解析操作类型,如select,insert,update,delete
  3. 基于操作类型、分片规则等条件,路由引擎会进行精准计算,确定该条 SQL 需要访问的具体物理分片(转发到底层真实的数据库节点)
  4. 底层真实数据库节点执行 SQL 语句,并将结果返回给 ShardingSphere-proxy。
  5. ShardingSphere-proxy 合并不同数据节点返回的数据,并将最终结果返回给客户端

3.3 Dokcer安装ShardingSphere-Proxy

3.3.1 创建Docker容器

bash 复制代码
# -e JJVM_OPTS="-Xms256m -Xmx256m -Xmn128m" 
# 初始堆内存256m,量大堆内存256m,新生代内存128m
docker run -d \
-p 3307:3307 \
-v /org/shardingsphere/proxy/conf:/opt/shardingsphere-proxy/conf \
-v /org/shardingsphere/proxy/ext-lib:/opt/shardingsphere-proxy/ext-lib \
-v /org/shardingsphere/proxy/logs:/opt/shardingsphere-proxy/logs \
-e JVM_OPTS="-Xms256m -Xmx256m -Xmn128m" \
--name ss-proxy \
apache/shardingsphere-proxy:5.3.2

创建容器后还需要在confext-lib目录下添加配置文件(yaml)和驱动程序(jar)。可以从Apache ShardingSphere官网下载二进制包,然后提取出配置文件,避免手动创建配置文件

3.3.2 配置server.yaml

conf目录下配置server.yaml

yaml 复制代码
mode:
  type: Standalone                    # 单机模式
authority:                            # 授权
  users:                              # 用户配置
    - user: root@%                    # 配置一个用户,用户名为root@%
      password: 123456                # 为用户指定密码
  privilege:                          # 权限
    type: ALL_PERMITTED               # 授予用户所有权限
props:                                # 属性配置
  sql-show: true                      # 显示执行的SQL语句
  proxy-mysql-default-version: 8.0.44 # MySQL版本号

3.3.3 上传MySQL驱动

ShardingSphere-Proxy 数据库代理服务连接 MySQL 数据库时,需要把 MySQL 驱动包放入宿主机映射扩展目录/ext-lib

3.3.4 日志配置

conf目录下配置logback.xml

xml 复制代码
<?xml version="1.0"?>

<configuration>
    <!-- 日志输入到文件 -->
    <appender name="SHARDING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
	<!-- 日志路径 -->
	<file>./logs/shardingsphere.log</file>
	<encoder>
    	    <!-- 日志输入的样式 -->
	    <pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
	</encoder>
	<rollingPolicy
	    class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
	    <fileNamePattern>shardingsphere.%d{yyyy-MMdd}.%i.log</fileNamePattern>
	    <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
		<maxFileSize>100MB</maxFileSize>
	    </timeBasedFileNamingAndTriggeringPolicy>
	</rollingPolicy>
    </appender>

    <root level="INFO">
	<appender-ref ref="SHARDING_FILE" />
    </root>

</configuration>

3.3.5 测试连接

bash 复制代码
# 重新启动容器
docker restart ss-proxy
# 查看状态,启动成功
docker ps
# 指定主机和端口号进行连接
mysql -h127.0.0.1 -P3307 -uroot -p123456

4.实践 - 读写分离

4.1 架构图

4.2 服务器规划

数据库服务器 容器名 端口号
主服务器 org-mysql-master 53306
从服务器1 org-mysql-slave1 53307
从服务器2 org-mysql-slave2 53308

4.3 创建数据库服务器

参考深入解析MySQL(9)------主从复制架构详解创建一个主服务器和两个从服务器,并开启主从复制

4.4 配置读写分离

/org/shardingsphere/proxy/conf目录下配置config-readwrite-splitting.yaml文件

yaml 复制代码
databaseName: org_proxy_db # shardingsphere中代理的库名

dataSources:
  write_ds: # 主节点,写服务器
    url: jdbc:mysql://YOUR_IP_ADDRESS:53306/org_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000 
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  read_ds_0: # 从节点,读服务器
    url: jdbc:mysql://YOUR_IP_ADDRESS:53306/org_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  read_ds_1: # 从节点,读服务器
    url: jdbc:mysql://YOUR_IP_ADDRESS:53306/org_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1

rules:
- !READWRITE_SPLITTING # 读写分离
  dataSources:
    readwrite_ds:
      staticStrategy:
        writeDataSourceName: write_ds
        readDataSourceNames:
          - read_ds_0
          - read_ds_1
      loadBalancerName: random # 负载均衡策略名,必须与下面配置的名字一致
  loadBalancers:        # 分别为不的同负载均衡策略指定内置的类型
    random:             # 随机,与上面的负载均衡策略名对应
      type: RANDOM      # 内置类型, 固定为RANDOM
    round_robin:        # 轮询
      type: ROUND_ROBIN # 固定为ROUND_ROBIN
    weight:             # 权重
      type: WEIGHT      # 固定为WEIGHT
      props:
        read_ds_0: 2.0
        read_ds_1: 1.0

4.5 启动ShardinSphere

bash 复制代码
# 启动容器
docker start ss-proxy
# 查看容器是否启动成功
docker ps
# 连接ShardinSphere
mysql -h127.0.0.1 -P3307 -uroot -p

4.6 测试

bash 复制代码
# 进入容器
docker exec -it ss-proxy env LANG=C.UTF-8 /bin/bash
# 查看ShardingSphere的实时日志,以实际目录和文件名为准
tail -f /opt/shardingsphere-proxy/logs/shardingsphere.log
  • 操作数据库

    sql 复制代码
    mysql> use org_proxy_db;
    Database changed
    mysql> insert into t_user values (1,'张三'),(2,'李四'),(3,'王五');
    Query OK, 3 rows affected (0.01 sec)
    
    mysql> select * from t_user;
    +----+--------+
    | id | name   |
    +----+--------+
    |  1 | 张三   |
    |  2 | 李四   |
    |  3 | 王五   |
    +----+--------+
    3 rows in set (0.01 sec)
    
    mysql> select * from t_user;
    +----+--------+
    | id | name   |
    +----+--------+
    |  1 | 张三   |
    |  2 | 李四   |
    |  3 | 王五   |
    +----+--------+
    3 rows in set (0.01 sec)
  • 查看日志 如果在事务中查询数据,所有的SQL操作都会被路由到主服务器,避免跨库事务

5.实践 - 垂直分片

5.1 架构图

5.2 服务器规划

数据库服务器 容器名 端口号
用户服务器 server-user 53310
订单服务器 server-order 53311

5.3 创建server-user容器

  • 创建server-user容器

    bash 复制代码
    docker run -d \
    -p 53310:3306 \
    -v /org/mysql/user/conf:/etc/mysql/conf.d \
    -v /org/mysql/user/mysql:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    --name server-user \
    mysql:8.0.44
  • 进入Docker容器

    bash 复制代码
    # 进入Docker容器
    root@VM-0-7-ubuntu:~# docker exec -it server-user env LANG=C.UTF-8 /bin/bash
    # 运行Mysql客户端
    bash-5.1# mysql -uroot -p
    sql 复制代码
    # 修改密码
    mysql> set password = '123456';
  • 创建数据库

    sql 复制代码
    create database if not exists user_db character set utf8mb4 collate utf8mb4_0900_ai_ci;
    -- 选择数据库
    use user_db;
    -- 创建用户表
    create table if not exists t_user (
      id bigint primary key auto_increment,
      name varchar(20)
    );

5.4 创建server-order容器

  • 创建server-order容器

    bash 复制代码
    docker run -d \
    -p 53311:3306 \
    -v /org/mysql/order/conf:/etc/mysql/conf.d \
    -v /org/mysql/order/mysql:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    --name server-order \
    mysql:8.0.44
  • 进入Docker容器

    bash 复制代码
    # 进入Docker容器
    root@VM-0-7-ubuntu:~# docker exec -it server-order env LANG=C.UTF-8 /bin/bash
    # 运行Mysql客户端
    bash-5.1# mysql -uroot -p
    sql 复制代码
    # 修改密码
    mysql> set password = '123456';
  • 创建数据库

    sql 复制代码
    create database if not exists order_db character set utf8mb4 collate utf8mb4_0900_ai_ci;
    -- 选择数据库
    use order_db;
    -- 创建订单表
    create table if not exists t_order (
      id bigint primary key auto_increment,
      order_no varchar(30) comment '订单号',
      amount decimal(12, 2) comment '订单金额',
      user_id bigint comment '用户编号'
    );

5.5 垂直分库配置

/org/shardingsphere/proxy/conf目录下配置config-sharding.yaml文件

yaml 复制代码
databaseName: sharding_db

dataSources:
  server_user:
    url: jdbc:mysql://YOUR_IP_ADDRESS:53310/user_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  server_order:
    url: jdbc:mysql://YOUR_IP_ADDRESS:53311/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1

rules:
- !SHARDING
  tables:
    t_user:
      actualDataNodes: server_user.t_user
    t_order:
      actualDataNodes: server_order.t_order

5.6 启动ShardinSphere

bash 复制代码
# 启动容器
docker start ss-proxy
# 查看容器是否启动成功
docker ps
# 连接ShardinSphere
mysql -h127.0.0.1 -P3307 -uroot -p

6.实践 - 水平分片

6.1 架构图

  • 水平分库 + 水平分表

6.2 服务器规划

数据库服务器 容器名 端口号
订单服务器 server-order0 63310
订单服务器 server-order1 63311

6.3 创建server-order容器

bash 复制代码
# 创建server-order0
docker run -d \
-p 63310:3306 \
-v /org/mysql/order0/conf:/etc/mysql/conf.d \
-v /org/mysql/order0/mysql:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-order0 \
--restart always \
mysql:8.0.44
# 创建server-order1
docker run -d \
-p 63311:3306 \
-v /org/mysql/order1/conf:/etc/mysql/conf.d \
-v /org/mysql/order1/mysql:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-order1 \
--restart always \
mysql:8.0.44

6.4 进入Docker容器

bash 复制代码
# 进入Docker容器
docker exec -it server-order0 env LANG=C.UTF-8 /bin/bash
# docker exec -it server-order1 env LANG=C.UTF-8 /bin/bash
# 运行Mysql客户端
mysql -uroot -p
sql 复制代码
# 修改root用户密码
mysql> set password = '123456';

6.5 创建数据库

sql 复制代码
-- 在两个Docker容器都执行相同的SQL语句
create database if not exists order_db character set utf8mb4 collate utf8mb4_0900_ai_ci;
-- 选择数据库
use order_db;
-- 创建订单表t_order0
create table if not exists t_order0 (
  id bigint primary key,
  order_no varchar(30) comment '订单号',
  amount decimal(12, 2) comment '订单金额',
  user_id bigint comment '用户编号'
);
-- 创建订单表t_order1
create table if not exists t_order1 (
  id bigint primary key,
  order_no varchar(30) comment '订单号',
  amount decimal(12, 2) comment '订单金额',
  user_id bigint comment '用户编号'
);

6.6 水平分片配置

yaml 复制代码
databaseName: sharding_db

dataSources:
  server_order0:
    url: jdbc:mysql://81.69.218.112:63310/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  server_order1:
    url: jdbc:mysql://81.69.218.112:63311/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1

rules:
- !SHARDING
  tables:
    t_order:
      actualDataNodes: server_order${0..1}.t_order${0..1}
      databaseStrategy: # 分库策略
        standard: # 用于单分片键的标准分片场景
          shardingColumn: user_id #分片列名称
          shardingAlgorithmName: alg_db_inline_userid # 分片算法名称
      tableStrategy: #分表策略
        standard:
          shardingColumn: order_no # 分片列名称
          shardingAlgorithmName: alg_hash_mod # 分片算法名称
  # 配置分片算法
  shardingAlgorithms:
    alg_db_inline_userid:
      type: INLINE
      props:
        algorithm-expression: server_order${user_id % 2} # 分片算法,根据user_id对2取模       
    alg_hash_mod:
      type: HASH_MOD
      props:
        sharding-count: 2 

6.7 启动ShardinSphere

bash 复制代码
# 启动容器
docker start ss-proxy
# 查看容器是否启动成功
docker ps
# 连接ShardinSphere
mysql -h127.0.0.1 -P3307 -uroot -p

6.8 测试

bash 复制代码
# 进入容器
docker exec -it ss-proxy env LANG=C.UTF-8 /bin/bash
# 查看ShardingSphere的实时日志,以实际目录和文件名为准
tail -f /opt/shardingsphere-proxy/logs/shardingsphere.log
sql 复制代码
-- 路由到 server-order1
insert into t_order (id, order_no, user_id, amount) values (1, 'BIT001', 1,20.00);
insert into t_order (id, order_no, user_id, amount) values (2, 'BIT002', 1,20.00);
insert into t_order (id, order_no, user_id, amount) values (3, 'BIT003', 1,20.00);
insert into t_order (id, order_no, user_id, amount) values (4, 'BIT004', 1,20.00);
-- 路由到 server-order0
insert into t_order (id, order_no, user_id, amount) values (1, 'BIT001', 2,20.00);
insert into t_order (id, order_no, user_id, amount) values (2, 'BIT002', 2,20.00);
insert into t_order (id, order_no, user_id, amount) values (3, 'BIT003', 2,20.00);
insert into t_order (id, order_no, user_id, amount) values (4, 'BIT004', 2,20.00);
相关推荐
heartbeat..2 小时前
MySQL 索引从入门到精通:核心概念、类型与实战优化
java·数据库·mysql·索引
heartbeat..2 小时前
MySQL 存储引擎解析:InnoDB/MyISAM/Memory 原理与选型
java·数据库·mysql·存储引擎
周杰伦的稻香2 小时前
mysql “黑名单“
数据库·mysql
麦聪聊数据3 小时前
拒绝循环写库:MySQL 批量插入、Upsert 与跨表更新的高效写法
数据库·sql·mysql
技术净胜3 小时前
mysqldump 命令备份单库、多库、全库实操指南
数据库·mysql·adb
1.14(java)3 小时前
数据库范式详解与设计实践
数据库·mysql
麦聪聊数据3 小时前
由SQL空值 (NULL)引发的逻辑黑洞:从NOT IN失效谈起
数据库·sql·mysql
技术净胜3 小时前
mysqldump 备份恢复,从单库到全库恢复实操
mysql·msyql
遇见火星3 小时前
为MySQL配置SSL加密访问
mysql·adb·ssl