程序员必备的十大技能(进阶版)之高性能数据库实战(五)

简介: 教程来源 http://qcycj.cn/ 本节涵盖数据库监控诊断(慢日志配置、实时SQL与表状态分析)、NoSQL/NewSQL选型策略(缓存穿透/击穿/雪崩应对、分库分表与TiDB对比),以及千万级订单系统的实战优化路径,强调“硬件→架构→业务”逐层治理理念。

七、数据库监控与诊断

7.1 慢查询日志配置

-- 开启慢查询日志
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;  -- 超过1秒记录
SET GLOBAL log_queries_not_using_indexes = ON;  -- 记录未使用索引的查询

-- 查看慢查询日志位置
SHOW VARIABLES LIKE 'slow_query_log_file';

-- 使用pt-query-digest分析慢查询
-- pt-query-digest /var/log/mysql/slow.log > slow_report.txt

7.2 实时监控SQL

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

-- 查看事务运行情况
SELECT 
    trx_id,
    trx_state,
    trx_started,
    trx_mysql_thread_id,
    TIMESTAMPDIFF(SECOND, trx_started, NOW()) as duration_sec,
    trx_query
FROM information_schema.innodb_trx
ORDER BY duration_sec DESC;

-- 查看锁等待
SELECT 
    r.trx_id waiting_trx_id,
    r.trx_mysql_thread_id waiting_thread,
    b.trx_id blocking_trx_id,
    b.trx_mysql_thread_id blocking_thread,
    TIMESTAMPDIFF(SECOND, r.trx_started, NOW()) wait_sec
FROM information_schema.innodb_lock_waits w
INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id
INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id;

7.3 表状态分析

-- 查看表大小
SELECT 
    table_schema,
    table_name,
    ROUND((data_length + index_length) / 1024 / 1024, 2) AS size_mb,
    table_rows,
    ROUND(data_length / table_rows, 2) AS avg_row_size
FROM information_schema.tables
WHERE table_schema NOT IN ('information_schema', 'mysql', 'performance_schema')
ORDER BY size_mb DESC
LIMIT 20;

-- 查看索引使用统计
SELECT 
    index_name,
    cardinality,
    (cardinality / table_rows) * 100 AS selectivity
FROM information_schema.statistics
WHERE table_name = 'orders'
ORDER BY selectivity DESC;

八、NoSQL与NewSQL选型

8.1 何时使用NoSQL
image.png
8.2 缓存策略实战

// 缓存穿透、击穿、雪崩处理

@Component
public class CacheService {

    // 1. 缓存穿透(查询不存在的数据):布隆过滤器 + 缓存空值
    @Autowired
    private BloomFilter bloomFilter;

    public Order getOrder(Long id) {
        // 布隆过滤器过滤不存在ID
        if (!bloomFilter.mightContain(id)) {
            return null;
        }

        String cacheKey = "order:" + id;
        Order order = redisTemplate.opsForValue().get(cacheKey);
        if (order != null) {
            // 空值标记
            if (order.getId() == -1L) {
                return null;
            }
            return order;
        }

        order = orderMapper.selectById(id);
        if (order == null) {
            // 缓存空值,过期时间短(5分钟)
            redisTemplate.opsForValue().set(cacheKey, new Order(-1L), 300, TimeUnit.SECONDS);
            return null;
        }

        redisTemplate.opsForValue().set(cacheKey, order, 3600, TimeUnit.SECONDS);
        return order;
    }

    // 2. 缓存击穿(热点key过期):互斥锁
    public Order getOrderWithMutex(Long id) {
        String cacheKey = "order:" + id;
        Order order = redisTemplate.opsForValue().get(cacheKey);
        if (order != null) {
            return order;
        }

        // 尝试获取分布式锁
        String lockKey = "lock:order:" + id;
        Boolean locked = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);

        if (Boolean.TRUE.equals(locked)) {
            try {
                // 双重检查
                order = redisTemplate.opsForValue().get(cacheKey);
                if (order != null) {
                    return order;
                }
                order = orderMapper.selectById(id);
                redisTemplate.opsForValue().set(cacheKey, order, 3600, TimeUnit.SECONDS);
                return order;
            } finally {
                redisTemplate.delete(lockKey);
            }
        } else {
            // 等待其他线程加载
            Thread.sleep(50);
            return getOrderWithMutex(id);
        }
    }

    // 3. 缓存雪崩:过期时间加随机值
    public void cacheOrder(Order order) {
        String cacheKey = "order:" + order.getId();
        int baseTtl = 3600;
        int randomTtl = ThreadLocalRandom.current().nextInt(300, 600);
        redisTemplate.opsForValue().set(cacheKey, order, baseTtl + randomTtl, TimeUnit.SECONDS);
    }
}

8.3 分库分表 vs TiDB(NewSQL)
image.png

九、实战案例:千万级订单系统优化

9.1 优化前状态
数据量:订单表5000万行

QPS:峰值3000

平均响应:800ms

慢查询:每天2000+条

9.2 优化步骤

-- Step 1: 索引优化(去除冗余,添加必要)
-- 删除未使用的索引
SELECT * FROM sys.schema_unused_indexes WHERE table_name = 'orders';
DROP INDEX idx_unused ON orders;

-- 添加复合索引覆盖80%查询
CREATE INDEX idx_user_time_status ON orders(user_id, create_time, status);
CREATE INDEX idx_shop_time ON orders(shop_id, create_time);

-- Step 2: 冷热数据分离
-- 热数据(最近3个月)保留在主表
-- 冷数据(>3个月)迁移到history表
INSERT INTO orders_history SELECT * FROM orders WHERE create_time < DATE_SUB(NOW(), INTERVAL 3 MONTH);
DELETE FROM orders WHERE create_time < DATE_SUB(NOW(), INTERVAL 3 MONTH);
-- 使用分区表简化
ALTER TABLE orders PARTITION BY RANGE (TO_DAYS(create_time)) (
    PARTITION p202401 VALUES LESS THAN (TO_DAYS('2024-02-01')),
    PARTITION p202402 VALUES LESS THAN (TO_DAYS('2024-03-01')),
    ...
);

-- Step 3: 读写分离
-- 写操作主库,读操作从库
@RoutingWith(DataSourceType.SLAVE)
public List<Order> getOrdersByUser(Long userId) {
    return orderMapper.selectByUserId(userId);
}

-- Step 4: 应用层缓存
@Cacheable(value = "order", key = "#id", unless = "#result == null")
public Order getOrder(Long id) {
    return orderMapper.selectById(id);
}

-- Step 5: SQL改写
-- 原SQL(使用子查询,性能差)
SELECT * FROM orders 
WHERE user_id IN (SELECT user_id FROM users WHERE vip_level = 5)
ORDER BY create_time DESC
LIMIT 100;

-- 改写为JOIN
SELECT o.* FROM orders o
INNER JOIN users u ON o.user_id = u.id
WHERE u.vip_level = 5
ORDER BY o.create_time DESC
LIMIT 100;

记住数据库优化的黄金法则:硬件解决不了的问题,架构可以;架构解决不了的问题,业务可以。 始终保持对数据的敬畏之心,每一次变更都要有回滚预案。
来源:
http://fndvx.cn/

相关文章
|
8天前
|
Shell API 开发工具
Claude Code 快速上手指南(新手友好版)
AI编程工具卷疯啦!Claude Code凭借任务驱动+终端原生的特性,成了开发者的效率搭子。本文从安装、登录、切换国产模型到常用命令,手把手带新手快速上手,全程避坑,30分钟独立用起来。
2763 15
|
6天前
|
人工智能 开发工具 iOS开发
Claude Code 新手完全上手指南:安装、国产模型配置与常用命令全解
Claude Code 是一款运行在终端环境中的 AI 编程助手,能够直接在命令行中完成代码生成、项目分析、文件修改、命令执行、Git 管理等开发全流程工作。它最大的特点是**任务驱动、终端原生、轻量高效、多模型兼容**,无需图形界面、不依赖 IDE 插件,能够深度融入开发者日常工作流。
2304 4
|
21天前
|
人工智能 JSON 供应链
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
LucianaiB分享零成本畅用JVS Claw教程(学生认证享7个月使用权),并开源GeoMind项目——将JVS改造为科研与产业地理情报可视化AI助手,支持飞书文档解析、地理编码与腾讯地图可视化,助力产业关系图谱构建。
23554 13
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
|
8天前
|
人工智能 JSON BI
DeepSeek V4-Pro 接入 Claude Code 完全实战:体验、测试与关键避坑指南
Claude Code 作为当前主流的 AI 编程辅助工具,凭借强大的代码理解、工程执行与自动化能力深受开发者喜爱,但原生模型的使用成本相对较高。为了在保持能力的同时进一步降低开销,不少开发者开始寻找兼容度高、价格更友好的替代模型。DeepSeek V4 系列的发布带来了新的选择,该系列包含 V4-Pro 与 V4-Flash 两款模型,并提供了与 Anthropic 完全兼容的 API 接口,理论上只需简单修改配置,即可让 Claude Code 无缝切换为 DeepSeek 引擎。
2055 1
|
2天前
|
人工智能 Linux BI
国内用 Claude Code 终于不用翻墙了:一行命令搞定,自动接 DeepSeek
JeecgBoot AI专题研究 一键脚本:Claude Code + JeecgBoot Skills + DeepSeek 全平台接入 一行命令装好 Claude Code + JeecgBoot Skills + DeepSeek 接入,无需翻墙使用 Claude Code,支持 Wind
1306 1
国内用 Claude Code 终于不用翻墙了:一行命令搞定,自动接 DeepSeek
|
14天前
|
人工智能 缓存 Shell
Claude Code 全攻略:命令大全 + 实战工作流(完整版)
Claude Code 是一款运行在终端环境下的 AI 编码助手,能够直接在项目目录中理解代码结构、编辑文件、执行命令、执行开发计划,并支持持久化记忆、上下文压缩、后台任务、多模型切换等专业能力。对于日常开发、项目维护、快速重构、代码审查等场景,它可以大幅减少手动操作、提升编码效率。本文从常用命令、界面模式、核心指令、记忆机制、图片处理、进阶工作流等维度完整说明,帮助开发者快速上手并稳定使用。
3457 5
|
7天前
|
人工智能 安全 开发工具
Claude Code 官方工作原理与使用指南
Claude Code 不是传统代码补全工具,而是 Anthropic 推出的终端 AI 代理,具备代理循环、双驱动架构(模型+工具)、全局项目感知、6 种权限模式等核心能力,本文基于官方文档系统解析其工作原理与高效使用技巧。
1095 0