MySQL性能调优实战:慢查询诊断、索引设计与分库分表全流程

数据库运维的核心诉求是稳定和性能。MySQL作为使用最广泛的关系型数据库,其性能瓶颈往往不在硬件而在配置和SQL。本文从SQL查询优化、索引设计、高可用架构到分库分表方案,提供可执行的调优指南。

一、SQL查询优化:执行计划分析

SQL查询优化始于EXPLAIN执行计划分析。EXPLAIN输出的type、key、rows、Extra四个字段是判断SQL健康度的核心指标。

-- 1. 查看执行计划
EXPLAIN SELECT o.id, o.total_amount, u.username
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = 'PAID' AND o.created_at >= '2026-01-01'
ORDER BY o.created_at DESC
LIMIT 20;

-- 执行计划关键字段解读:
-- type: 访问类型,从好到差:system > const > eq_ref > ref > range > index > ALL
-- key: 实际使用的索引
-- rows: 预估扫描行数
-- Extra: Using index(覆盖索引)、Using filesort(文件排序)、Using temporary(临时表)

-- 2. 开启慢查询日志定位问题SQL
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;     -- 超过1秒记录
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';

-- 3. 查看当前正在执行的SQL
SELECT id, user, host, db, command, time, state, info
FROM information_schema.processlist
WHERE command != 'Sleep' AND time > 5
ORDER BY time DESC;

常见SQL调优场景与方案:

-- 场景1: 深度分页优化
-- 问题:LIMIT 1000000, 20 需要扫描100万行再丢弃
-- 优化:基于游标的延迟关联
SELECT o.* FROM orders o
INNER JOIN (
    SELECT id FROM orders
    WHERE status = 'PAID'
    ORDER BY created_at DESC
    LIMIT 1000000, 20
) t ON o.id = t.id;

-- 更优方案:基于主键游标
SELECT * FROM orders
WHERE id > #{last_id} AND status = 'PAID'
ORDER BY id ASC
LIMIT 20;

-- 场景2: 避免隐式类型转换导致索引失效
-- 问题:phone字段是varchar但查询传了整数
SELECT * FROM users WHERE phone = 13800138000;        -- 索引失效
SELECT * FROM users WHERE phone = '13800138000';      -- 索引生效

-- 场景3: OR条件优化为UNION ALL
-- 问题:OR可能导致无法使用索引
SELECT * FROM orders WHERE user_id = 100 OR status = 'PAID';
-- 优化:拆分为UNION ALL
SELECT * FROM orders WHERE user_id = 100
UNION ALL
SELECT * FROM orders WHERE status = 'PAID' AND user_id != 100;

-- 场景4: 聚合查询优化 - 预计算
-- 问题:实时COUNT在大表上性能差
SELECT status, COUNT(*) FROM orders GROUP BY status;
-- 优化:维护汇总表
SELECT status, cnt FROM order_status_summary
WHERE stat_date = CURDATE();

二、MySQL索引设计与优化

索引是SQL查询优化的物理基础。索引设计有三个原则:最左前缀匹配、覆盖索引优先、避免冗余索引。联合索引的列顺序必须与查询条件匹配。

-- 联合索引设计示例
-- 业务查询模式:
-- 1. WHERE user_id = ? AND status = ? ORDER BY created_at
-- 2. WHERE user_id = ? AND created_at >= ?
-- 3. WHERE status = ? AND created_at >= ?

-- 最优联合索引:user_id, status, created_at
-- 覆盖查询1: 完全匹配最左前缀
-- 覆盖查询2: 匹配user_id前缀 + created_at范围扫描
-- 查询3无法使用该索引(缺少user_id)

-- 创建索引
ALTER TABLE orders ADD INDEX idx_user_status_created
    (user_id, status, created_at);

-- 覆盖索引:查询字段都在索引中,避免回表
SELECT user_id, status, created_at FROM orders
WHERE user_id = 100 AND status = 'PAID';

-- 查看索引使用情况
SELECT
    object_schema AS db,
    object_name AS table_name,
    index_name,
    count_read AS read_count,
    count_fetch AS rows_fetched
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE index_name IS NOT NULL
ORDER BY count_read DESC;

MySQL性能调优还需要调整Buffer Pool。InnoDB Buffer Pool是影响性能最关键的参数,建议设置为物理内存的60-75%:

# /etc/my.cnf - InnoDB核心参数调优
[mysqld]
# Buffer Pool
innodb_buffer_pool_size = 32G         # 服务器64G内存分配50%
innodb_buffer_pool_instances = 8      # 多实例减少锁竞争
innodb_buffer_pool_load_at_startup = ON  # 重启后预加载热数据

# 日志与刷盘
innodb_log_file_size = 2G             # Redo Log大小
innodb_log_buffer_size = 64M
innodb_flush_log_at_trx_commit = 1    # 每次事务提交刷盘(最安全)
innodb_flush_method = O_DIRECT        # 跳过OS缓存直接写磁盘

# 并发控制
innodb_read_io_threads = 8
innodb_write_io_threads = 8
innodb_io_capacity = 2000             # SSD建议2000以上
innodb_io_capacity_max = 4000

# 连接管理
max_connections = 500
thread_cache_size = 64
table_open_cache = 4096
open_files_limit = 65535

# SQL模式
sql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
character_set_server = utf8mb4
collation_server = utf8mb4_unicode_ci

三、数据库高可用架构与数据备份恢复

数据库高可用架构的核心是主从复制 + 自动故障切换。MHA(Master High Availability)和Orchestrator是常用方案,但更现代的选择是MySQL InnoDB Cluster based on Group Replication。

-- 主从复制配置

-- Master节点 my.cnf配置
[mysqld]
server_id = 1
log_bin = mysql-bin
binlog_format = ROW
binlog_row_image = MINIMAL
gtid_mode = ON
enforce_gtid_consistency = ON

-- 创建复制用户
CREATE USER 'repl'@'%' IDENTIFIED BY 'StrongReplPass!23';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';

-- Slave节点 my.cnf配置
[mysqld]
server_id = 2
log_bin = mysql-bin
relay_log = relay-bin
read_only = ON
gtid_mode = ON
enforce_gtid_consistency = ON

-- 配置复制源
CHANGE REPLICATION SOURCE TO
    SOURCE_HOST='10.0.1.10',
    SOURCE_PORT=3306,
    SOURCE_USER='repl',
    SOURCE_PASSWORD='StrongReplPass!23',
    SOURCE_AUTO_POSITION=1;  -- GTID自动定位

START REPLICA;

-- 验证复制状态
SHOW REPLICA STATUS\G
-- 关键指标:
-- Replica_IO_Running: Yes
-- Replica_SQL_Running: Yes
-- Seconds_Behind_Source: 0

数据备份恢复策略需要满足RPO(数据恢复点目标)和RTO(恢复时间目标):

#!/bin/bash
# 全量物理备份脚本 (xtrabackup)
BACKUP_DIR="/data/backup/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
FULL_DIR="${BACKUP_DIR}/full_${DATE}"

# 全量备份
xtrabackup --backup \
    --target-dir=${FULL_DIR} \
    --user=backup --password=BackupPass!23 \
    --compress --compress-threads=4

# 增量备份(基于全量)
xtrabackup --backup \
    --target-dir=${BACKUP_DIR}/incr_${DATE} \
    --incremental-basedir=${FULL_DIR} \
    --user=backup --password=BackupPass!23

# 恢复流程
# 1. 解压
xbstream -x -C /var/lib/mysql_restore/ < full_backup.xb
# 2. 准备
xtrabackup --prepare --target-dir=/var/lib/mysql_restore/
# 3. 恢复
xtrabackup --copy-back --target-dir=/var/lib/mysql_restore/
# 4. 修改权限并启动
chown -R mysql:mysql /var/lib/mysql
systemctl start mysqld

四、分库分表方案:ShardingSphere实战

单表数据量超过千万行后查询性能开始退化,分库分表是必然选择。ShardingSphere-JDBC是Java生态最成熟的分库分表中间件,以无侵入方式实现数据分片。

# application.yml - ShardingSphere分库分表配置
spring:
  shardingsphere:
    mode:
      type: Cluster                # 集群模式,配置存于ZooKeeper
      repository:
        type: ZooKeeper
        props:
          namespace: governance
          server-lists: zk1:2181,zk2:2181,zk3:2181
    datasource:
      names: ds0,ds1,ds2,ds3
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://10.0.1.20:3306/order_db_0
        username: app
        password: AppPass!23
      # ds1, ds2, ds3配置类似...

    rules:
      sharding:
        tables:
          orders:
            actualDataNodes: ds${0..3}.orders_${0..15}
            databaseStrategy:
              standard:
                shardingColumn: user_id
                shardingAlgorithmName: db_mod
            tableStrategy:
              standard:
                shardingColumn: order_id
                shardingAlgorithmName: table_mod
            keyGenerateStrategy:
              column: order_id
              keyGeneratorName: snowflake

        shardingAlgorithms:
          db_mod:
            type: MOD
            props:
              sharding-count: 4
          table_mod:
            type: MOD
            props:
              sharding-count: 16

        keyGenerators:
          snowflake:
            type: SNOWFLAKE
            props:
              worker-id: 1

    props:
      sql-show: true               # 打印实际执行SQL(调试用)

分库分表后跨库查询和分布式事务是新挑战。ShardingSphere支持基于XA和BASE的分布式事务:

// Java代码 - 分布式事务示例
@Service
@RequiredArgsConstructor
public class OrderTransactionService {

    private final OrderMapper orderMapper;
    private final InventoryMapper inventoryMapper;

    @ShardingSphereTransactionType(TransactionType.XA)  // XA事务
    @Transactional
    public void createOrderWithDeduct(Order order, Long productId, int qty) {
        orderMapper.insert(order);                 // 可能落到ds0
        inventoryMapper.deduct(productId, qty);     // 可能落到ds2
        // ShardingSphere自动协调XA事务,保证原子性
    }
}

数据迁移实战中,双写方案最为稳妥。新分片库与旧单库并行写入,通过数据同步工具(如Canal监听binlog)保证一致,灰度切读,最终下线旧库。NoSQL选型应用方面,Redis处理热点缓存和分布式锁,MongoDB适合文档型不固定结构数据,Elasticsearch专攻全文检索。国产数据库如OceanBase、TiDB在兼容MySQL协议的同时提供分布式能力,是应对超大规模数据场景的有效方案。数据库运维没有银弹,MySQL性能调优需要结合具体业务场景和监控数据持续迭代。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/mysql-xing-neng-diao-you-shi-zhan-man-cha-xun-zhen-duan-suo/

(0)
小编小编
上一篇 2026年8月1日
下一篇 2026年8月1日

相关推荐

MySQL性能调优实战:慢查询诊断、索引设计与分库分表全流程

MySQL性能调优是数据库运维中最高频的工作内容。面对慢查询、高CPU、锁等待等问题,经验丰富的DBA有一套系统化的诊断流程。本文以一个真实的慢查询优化案例为主线,演示从问题发现到根因定位到最终优化的完整过程,涵盖SQL查询优化、索引设计、分库分表等核心技能。

慢查询发现与初步定位

数据库运维的第一步是建立慢查询采集机制。MySQL的slow_query_log是发现问题的入口:

-- 开启慢查询日志
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;  -- 超过1秒的查询记录
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';

-- 查看慢查询数量
SHOW GLOBAL STATUS LIKE 'Slow_queries';

-- 使用mysqldumpslow分析慢日志
-- 按查询时间排序,取前10条
-- mysqldumpslow -s t -t 10 /var/log/mysql/slow.log

实际案例:某电商系统商品列表页加载缓慢,慢查询日志中出现一条平均执行时间3.2秒的查询:

SELECT p.id, p.name, p.price, p.stock, c.name AS category_name, b.name AS brand_name
FROM products p
LEFT JOIN categories c ON p.category_id = c.id
LEFT JOIN brands b ON p.brand_id = b.id
WHERE p.status = 1
  AND p.category_id IN (1, 2, 3, 5, 8, 13, 21)
  AND p.price BETWEEN 100 AND 5000
ORDER BY p.create_time DESC
LIMIT 20;

EXPLAIN执行计划分析

定位到慢查询后,用EXPLAIN分析执行计划。SQL查询优化的核心技能是读懂执行计划:

EXPLAIN SELECT p.id, p.name, p.price, p.stock, c.name, b.name
FROM products p
LEFT JOIN categories c ON p.category_id = c.id
LEFT JOIN brands b ON p.brand_id = b.id
WHERE p.status = 1
  AND p.category_id IN (1, 2, 3, 5, 8, 13, 21)
  AND p.price BETWEEN 100 AND 5000
ORDER BY p.create_time DESC
LIMIT 20;

执行计划输出:

+----+--------+----------+------+---------+------+---------+----------------+
| id | select | table    | type | key     | rows | filtered| Extra          |
+----+--------+----------+------+---------+------+---------+----------------+
| 1  | SIMPLE | p        | ALL  | NULL    | 380K | 1.11    | Using filesort |
| 1  | SIMPLE | c        | eq_ref| PRIMARY | 1   | 100.00  |                |
| 1  | SIMPLE | b        | eq_ref| PRIMARY | 1   | 100.00  |                |
+----+--------+----------+------+---------+------+---------+----------------+

问题一目了然:

  • products表 type=ALL,全表扫描,扫描了38万行
  • key=NULL,没有使用任何索引
  • Using filesort,ORDER BY触发了文件排序
  • filtered=1.11%,过滤条件过滤掉了99%的数据,但扫描了全部行

索引设计与优化方案

MySQL性能调优中,索引设计是最有效的优化手段。上述查询有三个WHERE条件和一个ORDER BY,需要设计联合索引。

联合索引设计原则:等值条件放前面,范围条件放后面,排序字段紧跟其后。

-- 错误的索引设计:把范围条件放在前面
-- CREATE INDEX idx_wrong ON products(price, status, category_id, create_time);
-- price是范围查询,后面的列无法走索引

-- 正确的索引设计
CREATE INDEX idx_status_cat_price_time
ON products(status, category_id, price, create_time);

-- 索引列顺序解释:
-- status: 等值查询,放第一位
-- category_id: IN查询(可走索引),放第二位
-- price: 范围查询,放第三位
-- create_time: ORDER BY字段,放最后利用索引天然有序

添加索引后再次EXPLAIN:

+----+--------+----------+-------+-------------------------+------+---------+
| id | select | table    | type  | key                     | rows | filtered|
+----+--------+----------+-------+-------------------------+------+---------+
| 1  | SIMPLE | p        | range | idx_status_cat_price_time| 8200 | 100.00  |
| 1  | SIMPLE | c        | eq_ref| PRIMARY                 | 1    | 100.00  |
| 1  | SIMPLE | b        | eq_ref| PRIMARY                 | 1    | 100.00  |
+----+--------+----------+-------+-------------------------+------+---------+
-- Extra: Using index condition(不再有filesort)

优化效果:扫描行数从38万降到8200,Using filesort消失。查询时间从3.2秒降到0.08秒。

索引失效场景排查

数据库高可用架构中,即使设计了索引,也可能因为SQL写法导致索引失效。以下是常见场景:

-- 1. 函数操作导致索引失效
-- 错误(索引失效)
SELECT * FROM products WHERE DATE(create_time) = '2026-08-01';
-- 正确(走索引)
SELECT * FROM products WHERE create_time >= '2026-08-01'
  AND create_time < '2026-08-02';

-- 2. 隐式类型转换
-- 错误(category_id是int,传字符串导致隐式转换,索引失效)
SELECT * FROM products WHERE category_id = '1';
-- 正确
SELECT * FROM products WHERE category_id = 1;

-- 3. LIKE前缀通配符
-- 错误(索引失效)
SELECT * FROM products WHERE name LIKE '%手机%';
-- 可走索引(前缀匹配)
SELECT * FROM products WHERE name LIKE '手机%';

-- 4. OR连接非索引列
-- 错误(status有索引,description没有,整体走全表)
SELECT * FROM products WHERE status = 1 OR description LIKE '%手机%';
-- 正确(用UNION ALL拆分)
SELECT * FROM products WHERE status = 1
UNION ALL
SELECT * FROM products WHERE description LIKE '%手机%' AND status != 1;

Redis缓存策略:减轻数据库压力

MySQL性能调优不能只靠数据库本身,Redis缓存策略是降低数据库负载的关键手段。商品列表这类读多写少的数据,适合用缓存:

// 缓存策略:Cache-Aside Pattern
// 1. 先查缓存
// 2. 缓存未命中查数据库
// 3. 写入缓存(带过期时间)

public List<Product> getProductList(Long categoryId, int page, int pageSize) {
    String cacheKey = String.format("products:cat:%d:page:%d:size:%d",
        categoryId, page, pageSize);

    // 先查Redis
    String cached = redisTemplate.opsForValue().get(cacheKey);
    if (cached != null) {
        return parseJson(cached, ProductList.class);
    }

    // 缓存未命中,查数据库
    List<Product> products = productMapper.findByCategory(
        categoryId, page, pageSize);

    // 写入缓存,TTL 5分钟
    redisTemplate.opsForValue().set(
        cacheKey, toJson(products), 5, TimeUnit.MINUTES);

    return products;
}

// 缓存更新策略:数据变更时删除缓存(而非更新缓存)
public void updateProduct(Product product) {
    productMapper.update(product);
    // 删除相关缓存
    redisTemplate.delete(String.format("products:cat:%d:*", product.getCategoryId()));
    redisTemplate.delete("products:detail:" + product.getId());
}
# Redis防穿透:布隆过滤器
# 空结果也缓存,但设置短TTL(30秒)
SET products:cat:999:page:1:size:20 "NULL" EX 30

# Redis防雪崩:TTL加随机偏移
TTL = base_ttl + random(0, 60s)
# 例如基础TTL 300秒,加上0到60秒随机值,避免大量key同时过期

分库分表方案:数据量超限时的水平扩展

当单表数据量超过1000万行,B+树层级增加,查询性能下降明显。分库分表方案是数据库运维的进阶技能。以ShardingSphere为例:

# ShardingSphere分表配置(YAML)
spring:
  shardingsphere:
    datasource:
      names: ds0,ds1
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://192.168.1.101:3306/shop_db0
        username: root
        password: xxx
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://192.168.1.102:3306/shop_db1
        username: root
        password: xxx
    rules:
      sharding:
        tables:
          products:
            actual-data-nodes: ds${0..1}.products_${0..3}
            database-strategy:
              standard:
                sharding-column: merchant_id
                sharding-algorithm-name: db-mod
            table-strategy:
              standard:
                sharding-column: product_id
                sharding-algorithm-name: table-mod
        sharding-algorithms:
          db-mod:
            type: MOD
            props:
              sharding-count: 2
          table-mod:
            type: MOD
            props:
              sharding-count: 4
-- 分表后查询:ShardingSphere自动路由
-- 应用层无感知,SQL照常写
SELECT * FROM products
WHERE merchant_id = 1001
  AND product_id = 50001;
-- ShardingSphere自动路由到 ds0.products_1

-- 跨分片聚合查询(走所有分片合并结果)
SELECT category_id, COUNT(*) FROM products
GROUP BY category_id;
-- 需要查询所有分片并在内存中合并

数据备份恢复:最后的防线

无论数据库性能怎么优化,数据备份恢复都是不可省略的运维环节。推荐的备份策略:

# 物理备份(xtrabackup,适合大数据量)
# 全量备份(每天凌晨2点)
xtrabackup --backup --target-dir=/backup/full \
  --user=root --password=xxx

# 增量备份(每6小时)
xtrabackup --backup --target-dir=/backup/inc1 \
  --incremental-basedir=/backup/full \
  --user=root --password=xxx

# 恢复流程
# 1. 准备全量备份
xtrabackup --prepare --target-dir=/backup/full
# 2. 合并增量备份
xtrabackup --prepare --target-dir=/backup/full \
  --incremental-dir=/backup/inc1
# 3. 拷贝数据文件
xtrabackup --copy-back --target-dir=/backup/full

# 逻辑备份(mysqldump,适合小数据量或特定表)
mysqldump --single-transaction --routines --triggers \
  --set-gtid-purged=OFF shop_db products > products_backup.sql

MySQL性能调优是一个系统工程:慢查询发现靠监控,根因定位靠EXPLAIN,优化手段靠索引设计,压力缓解靠Redis缓存,扩展能力靠分库分表,安全底线靠数据备份恢复。每个环节都需要实战积累,没有捷径。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/mysql-xing-neng-diao-you-shi-zhan-man-cha-xun-zhen-duan-suo/

(0)
小编小编
上一篇 2026年8月1日
下一篇 2026年8月1日

相关推荐

MySQL性能调优实战:慢查询诊断、索引优化与分库分表方案全解

MySQL性能问题的定位方法论

数据库运维中,性能问题80%由慢查询导致,20%由配置和硬件瓶颈导致。MySQL性能调优必须从慢查询诊断入手,而非盲目调整参数。一条查询从客户端发起到返回结果,经历连接器、解析器、优化器、执行器四个阶段,性能瓶颈可能出现在任何一个环节。以下从诊断、优化、扩展三个层面展开。

慢查询诊断与EXPLAIN深度分析

开启慢查询日志是第一步:

# my.cnf慢查询配置
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 0.5      # 超过500ms记录
log_queries_not_using_indexes = 1
min_examined_row_limit = 100  # 扫描行数低于100不记录

# 运行时动态调整
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.5;

使用mysqldumpslow分析慢查询Top N:

# 按查询时间排序取Top 10
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log

# 按锁定时间排序
mysqldumpslow -s l -t 10 /var/log/mysql/slow.log

EXPLAIN输出中各列的含义决定了优化方向:

-- 查看执行计划
EXPLAIN ANALYZE
SELECT o.order_id, o.amount, u.username
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.created_at >= '2026-07-01'
  AND o.status = 'PAID'
ORDER BY o.amount DESC
LIMIT 20;

关键字段解读:

  • type:访问类型,从优到差:system > const > eq_ref > ref > range > index > ALL
  • key:实际使用的索引,NULL表示未命中索引
  • rows:预估扫描行数,偏差大说明统计信息过期
  • Extra:Using filesort(额外排序)和Using temporary(临时表)是性能杀手

索引优化策略

SQL查询优化中索引设计是核心。复合索引需遵循最左前缀原则:

-- 订单表常见查询场景
-- 场景1: 按用户+状态查询
SELECT * FROM orders WHERE user_id = 1001 AND status = 'PAID';

-- 场景2: 按用户+创建时间范围查询
SELECT * FROM orders WHERE user_id = 1001
  AND created_at >= '2026-07-01';

-- 场景3: 按用户+状态+时间排序
SELECT * FROM orders WHERE user_id = 1001 AND status = 'PAID'
ORDER BY created_at DESC LIMIT 20;

-- 最优复合索引设计(覆盖上述三个场景)
ALTER TABLE orders ADD INDEX idx_user_status_created (user_id, status, created_at);

索引选择性和基数决定了索引效率:

-- 查看索引基数(Cardinality越高越好)
SHOW INDEX FROM orders;

-- 计算选择性
SELECT
    COUNT(DISTINCT user_id) / COUNT(*) AS user_id_selectivity,
    COUNT(DISTINCT status) / COUNT(*) AS status_selectivity
FROM orders;

-- 低选择性字段不适合建索引(如status只有5个值)
-- 但在复合索引中作为第二列是合理的

索引失效的常见陷阱:

-- 1. 隐式类型转换(user_id是varchar,传入整数)
SELECT * FROM orders WHERE user_id = 1001;    -- 索引失效
SELECT * FROM orders WHERE user_id = '1001';  -- 索引命中

-- 2. 函数运算导致索引失效
SELECT * FROM orders WHERE DATE(created_at) = '2026-07-30';  -- 索引失效
SELECT * FROM orders WHERE created_at >= '2026-07-30'
  AND created_at < '2026-07-31';                                -- 索引命中

-- 3. LIKE前缀通配符
SELECT * FROM products WHERE name LIKE '%手机';   -- 索引失效
SELECT * FROM products WHERE name LIKE '华为%';   -- 索引命中

-- 4. OR条件中有一列无索引
SELECT * FROM orders WHERE user_id = 1001
  OR amount > 10000;  -- 全表扫描
-- 优化:UNION ALL替代
SELECT * FROM orders WHERE user_id = 1001
UNION ALL
SELECT * FROM orders WHERE amount > 10000 AND user_id != 1001;

分库分表方案实战

单表数据量超过2000万行后,B+Tree索引层级增加导致查询性能下降。分库分表方案的选型取决于数据增长速度和查询模式:

# ShardingSphere-JDBC配置分片规则
spring:
  shardingsphere:
    datasource:
      names: ds0,ds1
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://10.0.0.1:3306/order_db_0
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://10.0.0.2:3306/order_db_1
    rules:
      sharding:
        tables:
          orders:
            actual-data-nodes: ds${0..1}.orders_${0..15}
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: orders-db-mod
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: orders-tbl-mod
        sharding-algorithms:
          orders-db-mod:
            type: MOD
            props:
              sharding-count: 2
          orders-tbl-mod:
            type: MOD
            props:
              sharding-count: 16

数据库高可用架构:MySQL MGR部署

MySQL Group Replication提供多主写入和自动故障转移,是数据备份恢复之外的高可用方案:

# my.cnf MGR配置
[mysqld]
plugin_load = 'group_replication.so'
group_replication_group_name = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
group_replication_start_on_boot = OFF
group_replication_local_address = "10.0.0.1:33061"
group_replication_group_seeds = "10.0.0.1:33061,10.0.0.2:33061,10.0.0.3:33061"
group_replication_bootstrap_group = OFF
group_replication_single_primary_mode = ON
group_replication_enforce_update_everywhere_checks = OFF

# 启动MGR
CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='password'
  FOR CHANNEL 'group_replication_recovery';
START GROUP_REPLICATION;

# 检查成员状态
SELECT * FROM performance_schema.replication_group_members;

Redis缓存策略与数据一致性

NoSQL选型应用中,Redis作为MySQL前置缓存是标准架构。缓存更新的核心矛盾是性能与一致性:

# Cache-Aside模式实现(Python伪代码)
def get_order(order_id):
    # 1. 查缓存
    cache_key = f"order:{order_id}"
    data = redis.get(cache_key)
    if data:
        return json.loads(data)

    # 2. 查数据库
    data = mysql.query("SELECT * FROM orders WHERE id = ?", order_id)
    if data:
        # 3. 写缓存,设置合理过期时间防雪崩
        redis.setex(cache_key, 300 + random.randint(0, 60), json.dumps(data))
    return data

def update_order(order_id, updates):
    # 1. 先更新数据库
    mysql.update("UPDATE orders SET ... WHERE id = ?", updates, order_id)
    # 2. 再删除缓存(而非更新缓存)
    redis.delete(f"order:{order_id}")

MySQL性能调优是数据库运维的核心技能。从慢查询日志定位问题,到索引优化消除全表扫描,再到分库分表突破单库上限,每个阶段都有明确的技术手段和验证方法。索引设计遵循最左前缀和高选择性优先原则,分片策略按业务查询模式选择分片键,缓存方案在一致性和性能间取权衡——掌握这些,就能应对绝大多数数据库性能问题。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/mysql-xing-neng-diao-you-shi-zhan-man-cha-xun-zhen-duan-suo/

(0)
小编小编
上一篇 2026年7月31日
下一篇 2026年7月31日

相关推荐