MySQL性能调优实战:从慢查询诊断到索引优化的全链路配置方案

MySQL性能问题的排查入口

数据库慢不是模糊的感觉,是有数据可查的客观指标。性能调优的第一步是开启慢查询日志,找出真正的瓶颈语句,而不是凭猜测优化。

这篇文章从慢查询定位、执行计划分析、索引优化和参数调优四个层面,给出MySQL 8.0生产环境的性能调优方案。

慢查询日志配置与分析

开启慢查询日志:

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;       -- 超过1秒记录
SET GLOBAL log_queries_not_using_indexes = 'ON';  -- 未用索引也记录
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';

生产环境long_query_time建议先设2秒,收集一周数据后逐步下调到0.5秒。直接设0.1秒会产生大量日志影响IO。

用mysqldumpslow分析慢日志Top N:

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

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

更强大的工具是pt-query-digest,输出详细的查询分析报告:

pt-query-digest /var/log/mysql/slow.log > slow_report.txt

报告中关注三个指标:Query_time的95百分位、Rows_examined(扫描行数)、Rows_sent(返回行数)。Rows_examined/Rows_sent比值超过100的查询基本都有优化空间。

EXPLAIN执行计划逐字段解读

拿到慢SQL后,用EXPLAIN分析执行计划:

EXPLAIN SELECT o.order_id, o.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.amount DESC
LIMIT 20;

关键字段解读:

type:访问类型,从好到差:system > const > eq_ref > ref > range > index > ALL。出现ALL说明走了全表扫描,必须加索引
key:实际使用的索引,NULL表示没走索引
rows:预估扫描行数,越小越好
Extra:额外信息。出现Using filesort(额外排序)或Using temporary(临时表)需要优化

常见问题与对策:

1. type=ALL + rows很大 → 加WHERE条件对应的索引
2. Extra=Using filesort → ORDER BY字段需要索引覆盖
3. key=NULL但possible_keys有值 → 索引选择错误,用FORCE INDEX或优化索引
4. rows远大于实际返回行数 → 索引区分度不够,需要优化索引列顺序

索引优化:从创建到维护的完整方案

索引创建原则:

1. WHERE条件列建索引,高选择性列在前
2. ORDER BY和GROUP BY列建索引,避免filesort
3. 多列查询建联合索引,遵循最左前缀原则
4. 覆盖索引(Covering Index)减少回表

-- 联合索引:status选择性低放后面,created_at选择性高放前面
ALTER TABLE orders ADD INDEX idx_status_created (status, created_at);

-- 覆盖索引:查询只需要order_id和amount,索引直接返回不走回表
ALTER TABLE orders ADD INDEX idx_cover_paid (status, created_at, order_id, amount);

索引失效的五种常见场景:

-- 1. 对索引列使用函数
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';  -- 索引有效

-- 2. 隐式类型转换
SELECT * FROM orders WHERE order_no = 12345;  -- order_no是varchar,索引失效
SELECT * FROM orders WHERE order_no = '12345';  -- 索引有效

-- 3. LIKE以通配符开头
SELECT * FROM products WHERE name LIKE '%手机';  -- 索引失效
SELECT * FROM products WHERE name LIKE '华为%';  -- 索引有效

-- 4. OR条件中有无索引列混合
SELECT * FROM orders WHERE status = 'PAID' OR remark = '急单';  -- 索引失效
-- 改写为UNION
SELECT * FROM orders WHERE status = 'PAID'
UNION
SELECT * FROM orders WHERE remark = '急单';

-- 5. 联合索引跳过前缀列
-- INDEX(a, b, c)
SELECT * FROM t WHERE b = 1 AND c = 2;  -- 索引失效,跳过了a
SELECT * FROM t WHERE a = 1 AND c = 2;  -- 只走a的索引,c无法利用

索引维护:

索引不是越多越好。每个索引增加写操作的开销(INSERT/UPDATE/DELETE都需要更新索引)。生产环境建议:

– 单表索引数量控制在5-8个
– 定期检查冗余索引和未使用索引

-- 查找未使用的索引(MySQL 8.0 sys库)
SELECT * FROM sys.schema_unused_indexes
WHERE object_schema = 'your_db';

-- 查找冗余索引
SELECT * FROM sys.schema_redundant_indexes
WHERE table_schema = 'your_db';

MySQL参数调优

参数调优基于硬件配置和业务负载。以下是一个64GB内存、SSD盘服务器的推荐配置:

[mysqld]
# InnoDB缓冲池,设为物理内存的60%-75%
innodb_buffer_pool_size = 48G
# 缓冲池实例数,每个实例不低于1GB
innodb_buffer_pool_instances = 8

# 日志配置
innodb_log_file_size = 2G
innodb_log_buffer_size = 64M
innodb_flush_log_at_trx_commit = 1  # 生产必须为1,保证事务安全

# 并发配置
innodb_thread_concurrency = 0  # 0表示不限制,由InnoDB自行管理
innodb_read_io_threads = 8
innodb_write_io_threads = 8

# 连接配置
max_connections = 500
wait_timeout = 600
interactive_timeout = 600

# 查询缓存(MySQL 8.0已移除,不配置)

# 临时表配置
tmp_table_size = 256M
max_heap_table_size = 256M

# 排序缓冲
sort_buffer_size = 4M
join_buffer_size = 4M

innodb_flush_log_at_trx_commit参数说明:

– 设为1:每次事务提交都刷盘,最安全但最慢
– 设为2:每次提交写入OS缓存,每秒刷盘,性能提升明显
– 主库必须设1,从库可设2

大表优化:分区与归档策略

单表数据超过5000万行后,查询性能开始明显下降。分区和归档是两种处理思路:

按时间范围分区:

ALTER TABLE orders PARTITION BY RANGE (YEAR(created_at) * 100 + MONTH(created_at)) (
    PARTITION p202601 VALUES LESS THAN (202602),
    PARTITION p202602 VALUES LESS THAN (202603),
    PARTITION p202603 VALUES LESS THAN (202604),
    PARTITION p202604 VALUES LESS THAN (202605),
    PARTITION p202605 VALUES LESS THAN (202606),
    PARTITION p202606 VALUES LESS THAN (202607),
    PARTITION p202607 VALUES LESS THAN (202608),
    PARTITION p_future VALUES LESS THAN MAXVALUE
);

冷数据归档:

超过6个月的订单数据迁移到归档库,主库只保留近6个月热数据。用pt-archiver工具在线迁移:

pt-archiver   --source h=main-db,D=production,t=orders   --dest h=archive-db,D=archive,t=orders   --where "created_at < DATE_SUB(NOW(), INTERVAL 6 MONTH)"   --limit 1000   --commit-each   --progress 1000

MySQL性能调优不是一次性的工作,而是一个持续监测-分析-优化的循环。慢查询日志是起点,EXPLAIN是分析工具,索引和参数配置是手段。每次优化后用基准测试验证效果,避免改了反而更慢。

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

(0)
小编小编
上一篇 10小时前
下一篇 10小时前

相关推荐

MySQL性能调优实战:从慢查询诊断到索引优化的全链路方案

MySQL性能问题不是靠猜的

数据库性能调优的第一原则:先量化再优化。很多DBA看到CPU飙高就加索引、看到慢查询就改SQL,结果往往是优化了不关键的路径,真正的问题依然存在。本文从慢查询诊断出发,系统化拆解MySQL性能调优的完整链路。

慢查询诊断:从日志到EXPLAIN

开启慢查询日志

-- my.cnf配置
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 0.5    -- 超过500ms记录
min_examined_row_limit = 100  -- 扫描行少于100的不记录

-- 动态修改(不重启)
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 0.5;

pt-query-digest分析Top SQL

# 安装Percona Toolkit
apt install percona-toolkit

# 分析慢查询日志
pt-query-digest /var/log/mysql/slow.log > slow_report.txt

# 输出按执行时间排序的Top 10 SQL
# 关注三个指标:
# 1. Query_time: 累计执行时间
# 2. Rows_examined: 扫描行数(与Rows_sent的比值越大,效率越低)
# 3. Lock_time: 锁等待时间

EXPLAIN执行计划深度解读

EXPLAIN SELECT o.id, o.order_no, u.name
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = 'PAID' AND o.created_at > '2026-07-01'
ORDER BY o.created_at DESC
LIMIT 20;

EXPLAIN关键字段解读:

| 字段 | 关注点 | 危险信号 |
|——|——–|———|
| type | 访问类型 | ALL(全表扫描)、index(全索引扫描) |
| key | 实际使用的索引 | NULL(没用索引) |
| rows | 预估扫描行数 | 远大于返回行数 |
| Extra | 额外信息 | Using filesort、Using temporary |
| filtered | 过滤比例 | 低于10%说明索引选择度差 |

type从好到差:system > const > eq_ref > ref > range > index > ALL。生产环境至少要达到ref级别。

索引优化:建立高选择度的索引策略

复合索引的最左前缀原则

-- 常见错误:为每个WHERE条件建单列索引
CREATE INDEX idx_status ON orders(status);
CREATE INDEX idx_created ON orders(created_at);
-- 这两个单列索引MySQL只会选一个用

-- 正确做法:根据查询模式建复合索引
CREATE INDEX idx_status_created ON orders(status, created_at);

-- 最左前缀原则下的索引生效场景
-- OK WHERE status = 'PAID' AND created_at > '2026-07-01'  全部命中
-- OK WHERE status = 'PAID'                                  命中status列
-- FAIL WHERE created_at > '2026-07-01'                     不命中(跳过最左列)

索引选择度评估

-- 计算列的选择度(越接近1越好)
SELECT
    COUNT(DISTINCT status) / COUNT(*) AS status_selectivity,
    COUNT(DISTINCT user_id) / COUNT(*) AS user_selectivity,
    COUNT(DISTINCT created_at) / COUNT(*) AS created_selectivity
FROM orders;

-- 选择度高的列放前面
-- status选择度通常很低(2-5个值),应该放后面
-- 正确顺序:user_id(高) + created_at(中) + status(低)
CREATE INDEX idx_user_created_status ON orders(user_id, created_at, status);

覆盖索引:避免回表

-- 回表查询:先查索引再查聚簇索引
SELECT id, order_no, amount FROM orders WHERE user_id = 100;

-- 覆盖索引:索引包含了所有查询字段
CREATE INDEX idx_user_order ON orders(user_id, order_no, amount);

-- EXPLAIN中 Extra = Using index 表示覆盖索引生效

SQL改写:常见性能陷阱与优化

陷阱1:隐式类型转换导致索引失效

-- 错误:user_id是VARCHAR类型,传入整数导致隐式转换
SELECT * FROM users WHERE user_id = 123;

-- 正确:保持类型一致
SELECT * FROM users WHERE user_id = '123';

陷阱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:OR条件优化

-- 错误:OR导致无法走单一索引
SELECT * FROM orders WHERE status = 'PAID' OR user_id = 100;

-- 正确方案1:UNION ALL拆分
SELECT * FROM orders WHERE status = 'PAID'
UNION ALL
SELECT * FROM orders WHERE user_id = 100 AND status != 'PAID';

-- 正确方案2:使用IN替代
SELECT * FROM orders WHERE user_id IN (
    SELECT id FROM users WHERE vip_level >= 3
);

陷阱4:LIMIT深分页

-- 错误:OFFSET 1000000需要扫描前100万行再丢弃
SELECT * FROM orders ORDER BY id LIMIT 1000000, 20;

-- 正确方案1:游标分页(推荐)
SELECT * FROM orders WHERE id > 1000020 ORDER BY id LIMIT 20;

-- 正确方案2:延迟关联
SELECT o.* FROM orders o
JOIN (SELECT id FROM orders ORDER BY id LIMIT 1000000, 20) t
ON o.id = t.id;

InnoDB缓冲池调优

-- my.cnf关键参数
[mysqld]
innodb_buffer_pool_size = 16G         # 物理内存的70%-80%
innodb_buffer_pool_instances = 8      # 多实例减少锁争用
innodb_log_file_size = 2G            # Redo log大小
innodb_flush_method = O_DIRECT        # 绕过OS缓存
innodb_io_capacity = 2000            # SSD建议2000-5000
innodb_io_capacity_max = 4000

-- 在线调整缓冲池
SET GLOBAL innodb_buffer_pool_size = 17179869184;

-- 查看缓冲池命中率(应 > 99%)
SHOW STATUS LIKE 'Innodb_buffer_pool_read%';
-- 命中率 = 1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests)

缓冲池预热:重启后快速恢复性能

# MySQL 8.0+支持缓冲池dump/load
-- 关闭前保存
SET GLOBAL innodb_buffer_pool_dump_at_shutdown = 1;

-- 启动时加载
SET GLOBAL innodb_buffer_pool_load_at_startup = 1;

-- 手动触发
SET GLOBAL innodb_buffer_pool_dump_now = ON;
SET GLOBAL innodb_buffer_pool_load_now = ON;

-- 查看加载进度
SHOW STATUS LIKE 'Innodb_buffer_pool_load_status';

监控体系:Performance Schema关键指标

-- 查看当前执行的SQL及其耗时
SELECT * FROM sys.session
WHERE command = 'Query' AND time > 1
ORDER BY time DESC;

-- 查看等待事件Top 10
SELECT event_name, count_star, sum_timer_wait/1000000000 AS total_sec
FROM performance_schema.events_waits_summary_global_by_event_name
WHERE count_star > 0
ORDER BY sum_timer_wait DESC LIMIT 10;

-- 查看索引使用统计
SELECT object_schema, object_name, index_name, count_read, count_fetch
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE count_read > 0
ORDER BY count_read DESC LIMIT 20;

-- 找出从未使用过的索引(删除它们)
SELECT s.table_schema, s.table_name, s.index_name, s.non_unique
FROM statistics s
LEFT JOIN performance_schema.table_io_waits_summary_by_index_usage p
ON s.index_name = p.index_name AND s.table_name = p.object_name
WHERE p.count_read IS NULL AND s.index_name != 'PRIMARY'
AND s.table_schema NOT IN ('mysql', 'information_schema');

MySQL性能调优是持续工程,不是一次性工作。上线后的慢查询监控、定期索引审计、参数微调需要形成常态化流程,才能保证数据库在数据量持续增长下保持稳定性能。

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

(0)
小编小编
上一篇 12小时前
下一篇 12小时前

相关推荐

MySQL性能调优实战:从慢查询诊断到索引优化与分库分表方案的完整路径

MySQL性能调优的诊断起点:慢查询日志分析

数据库运维中,性能问题的排查起点永远是慢查询日志。MySQL的慢查询日志记录执行时间超过阈值的SQL语句,是定位瓶颈最直接的数据来源。

慢查询日志的配置与启用:

-- my.cnf 关键配置
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 0.5    -- 超过0.5秒的查询记入日志
log_queries_not_using_indexes = 1  -- 未走索引的查询也记录
min_examined_row_limit = 100  -- 扫描行数低于100的不记录

生产环境建议long_query_time设为0.5秒甚至0.2秒,而不是默认的10秒。10秒的阈值只能捕获极端慢查询,大量的性能损耗发生在0.5-5秒区间的查询中。

慢查询日志的分析工具pt-query-digest是Percona Toolkit的核心组件:

pt-query-digest /var/log/mysql/slow.log --since="24h ago" \
  --limit 20 --order-by Query_time:sum

输出按总执行时间排序的TOP20慢查询,关注三个指标:

  • Query_time sum:该查询模板累计消耗的总时间
  • Rows_examined:扫描行数,与实际返回行数的比值反映索引效率
  • Lock_time:锁等待时间,高Lock_time说明有锁竞争

EXPLAIN执行计划深度解读

拿到慢查询SQL后,用EXPLAIN分析执行计划是SQL查询优化的标准流程。但只看type列的ALLindexrange远远不够。

EXPLAIN FORMAT=JSON SELECT o.order_id, o.amount, c.name
FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE o.status = 'PAID' AND o.created_at > '2026-07-01'
ORDER BY o.amount DESC LIMIT 50;

JSON格式的EXPLAIN提供更多细节。关键关注点:

  • attached_condition:哪些条件在存储引擎层过滤,哪些在Server层过滤
  • used_columns:查询用到的列,判断是否可以通过覆盖索引消除回表
  • filtered:存储引擎返回的行在Server层过滤后剩余的百分比,低于10%说明索引选择度很差

索引优化:从单列到联合索引的设计决策

索引设计是MySQL性能调优的核心环节。常见误区:给每个WHERE条件列都建单列索引,以为MySQL会自动选择最优索引组合。事实上,MySQL一次查询通常只用一个索引。

联合索引的设计原则——最左前缀匹配:

-- 查询模式:按状态+创建时间范围查询
SELECT * FROM orders 
WHERE status = 'PAID' AND created_at BETWEEN '2026-07-01' AND '2026-07-31'
ORDER BY created_at DESC LIMIT 100;

-- 错误索引:两个单列索引
CREATE INDEX idx_status ON orders(status);
CREATE INDEX idx_created ON orders(created_at);
-- MySQL只会选其中一个,另一个条件做回表过滤

-- 正确索引:联合索引,等值条件在前,范围条件在后
CREATE INDEX idx_status_created ON orders(status, created_at);
-- MySQL可以同时利用status的等值过滤和created_at的范围扫描

索引优化中的统计信息维护:

-- 查看索引统计信息
SHOW INDEX FROM orders;

-- 手动更新统计信息(大量数据变更后)
ANALYZE TABLE orders;

-- 查看索引选择度
SELECT 
  INDEX_NAME,
  CARDINALITY,
  TABLE_ROWS,
  ROUND(CARDINALITY / TABLE_ROWS, 4) AS selectivity
FROM information_schema.STATISTICS s
JOIN information_schema.TABLES t USING (TABLE_SCHEMA, TABLE_NAME)
WHERE s.TABLE_SCHEMA = 'production' AND s.TABLE_NAME = 'orders';

数据库高可用架构:MySQL Group Replication配置实战

单机MySQL的可用性无法满足生产要求。MySQL Group Replication(MGR)是官方提供的高可用方案,基于Paxos协议实现多主复制和自动故障检测。

-- my.cnf MGR配置
[mysqld]
server_id = 1
gtid_mode = ON
enforce_gtid_consistency = ON
binlog_format = ROW
log_slave_updates = ON

plugin_load = 'group_replication.so'
group_replication_group_name = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
group_replication_start_on_boot = OFF
group_replication_local_address = "node1:33061"
group_replication_group_seeds = "node1:33061,node2:33061,node3:33061"
group_replication_bootstrap_group = OFF

group_replication_single_primary_mode = ON
group_replication_enforce_update_everywhere_checks = OFF

group_replication_member_weight = 50
group_replication_auto_rejoin_attempts = 3
group_replication_exit_state_action = READ_ONLY

MGR部署时常见的问题是网络分区导致的脑裂。三节点集群中,如果两个节点同时失联,剩余一个节点无法获得多数票,集群停止写入。解决方案是在各节点部署MySQL Router做流量路由:

# MySQL Router配置
[DEFAULT]
logging_folder = /var/log/mysql-router
runtime_folder = /var/run/mysql-router

[routing:primary]
bind_address = 0.0.0.0
bind_port = 6446
destinations = metadata-cache://default/?role=PRIMARY
routing_strategy = first-available
protocol = classic

[routing:secondary]
bind_address = 0.0.0.0
bind_port = 6447
destinations = metadata-cache://default/?role=SECONDARY
routing_strategy = round-robin
protocol = classic

分库分表方案:从单表瓶颈到水平拆分的工程路径

当单表数据量超过5000万行,即便索引优化到位,写入性能和DDL操作的耗时也会显著上升。分库分表是最后的手段。

分片策略的选择直接决定后续运维复杂度:

范围分片(Range Sharding):按时间或ID范围划分。优点是范围查询友好,缺点是热点集中在最新分片。

哈希分片(Hash Sharding):按分片键取模分配。优点是数据均匀分布,缺点是范围查询需要扫全部分片。

# ShardingSphere配置示例
dataSources:
  ds_0:
    url: jdbc:mysql://node1:3306/db_0
    username: root
    password: pass
  ds_1:
    url: jdbc:mysql://node2:3306/db_1
    username: root
    password: pass

shardingRule:
  tables:
    orders:
      actualDataNodes: ds_${0..1}.orders_${0..3}
      databaseStrategy:
        standard:
          shardingColumn: customer_id
          shardingAlgorithmName: orders_db_mod
      tableStrategy:
        standard:
          shardingColumn: order_id
          shardingAlgorithmName: orders_tbl_mod
  
  shardingAlgorithms:
    orders_db_mod:
      type: MOD
      props:
        sharding-count: 2
    orders_tbl_mod:
      type: MOD
      props:
        sharding-count: 4

数据备份恢复:分片环境下的一致性备份策略

分库分表后,备份的挑战是如何保证跨分片的数据一致性。单库单表的备份时间点不同,恢复后可能产生逻辑不一致。

一致性备份方案:使用全局标记点(Global Mark Point),在所有分片上同时设置只读标记,记录各分片的GTID位点,然后解除只读:

#!/bin/bash
# 全局一致性备份脚本
SHARDS=("node1:3306" "node2:3306" "node3:3306")
BACKUP_DIR="/data/backup/$(date +%Y%m%d_%H%M)"
mkdir -p $BACKUP_DIR

# 1. 所有分片设置只读
for shard in "${SHARDS[@]}"; do
  mysql -h ${shard%:*} -P ${shard#*:} \
    -e "FLUSH TABLES WITH READ LOCK; SET GLOBAL read_only=ON;"
done

# 2. 记录GTID位点
for shard in "${SHARDS[@]}"; do
  GTID=$(mysql -h ${shard%:*} -P ${shard#*:} \
    -e "SHOW MASTER STATUS\G" | grep Executed_Gtid_Set)
  echo "$shard $GTID" >> $BACKUP_DIR/gtid_checkpoint.txt
done

# 3. 解除只读,开始并行备份
for shard in "${SHARDS[@]}"; do
  mysql -h ${shard%:*} -P ${shard#*:} \
    -e "SET GLOBAL read_only=OFF; UNLOCK TABLES;"
done

# 4. 并行执行物理备份
for shard in "${SHARDS[@]}"; do
  HOST=${shard%:*}
  PORT=${shard#*:}
  mysqldump -h $HOST -P $PORT --all-databases --single-transaction \
    --gtid --set-gtid-purged=ON > "$BACKUP_DIR/shard_${HOST}_${PORT}.sql" &
done
wait
echo "备份完成: $BACKUP_DIR"

数据库运维中性能调优和架构优化是持续过程。慢查询日志定位瓶颈,EXPLAIN验证索引设计,MGR保障可用性,分库分表解决容量瓶颈,一致性备份守住数据安全底线。每个环节都需要配套的监控和自动化,纯手工操作在规模化环境下不可持续。

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

(0)
小编小编
上一篇 13小时前
下一篇 13小时前

相关推荐