以下对 SQL 优化 涉及的关键场景(含 update 行锁优化)进行极致详细的拆解,从底层原理、执行流程到实战代码、避坑指南全维度覆盖,搭配表格对比让逻辑更清晰:

一、SQL 优化 - COUNT 优化

1. 底层原理:COUNT() 的执行逻辑本质

COUNT() 是 “统计符合条件的非 NULL 行数”,但不同写法会触发数据库不同的执行路径,核心差异在于 “是否利用索引” 和 “如何处理 NULL 值”

2. 细分场景对比(含执行流程、性能、适用场景)
语法写法执行流程拆解(以 InnoDB 为例)性能关键影响点适用场景极端案例对比(1000 万行表)
COUNT(*)1. 选最窄索引(如主键索引、普通索引)
2. 遍历索引树,统计 “非删除标记” 的叶子节点数
3. 无需回表(因索引已记录行数逻辑)
无需判 NULL,依赖优化器选最优索引全表 / 条件总行数统计耗时~100ms(走索引)
COUNT(1)与 COUNT(*) 逻辑几乎等价,数据库将 1 视为 “常量”,同样走索引统计与 COUNT(*) 性能无差异(语法糖)习惯写法,兼容所有场景耗时~100ms(同 COUNT(*)
COUNT(主键)1. 遍历主键索引树(聚簇索引)
2. 统计主键非 NULL 的行数(主键本身非 NULL,所以等价全表行统计)
主键必须存在,否则退化为全表扫描主键明确且需精准统计时耗时~120ms(主键索引稍宽)
COUNT(普通列)1. 遍历普通索引(若列无索引则全表扫描)
2. 逐行判断列值是否为 NULL,非 NULL 才计数
3. 若列有 NULL,需回表确认行状态
需判 NULL + 可能回表,性能极差绝对禁止使用耗时~10s(全表扫 + 判空)
3. 实战优化:从反例到正例
-- 反例 1:用 COUNT(name),name 可能为 NULL,且无索引时全表扫
-- 执行流程:全表扫描每一行 → 判 name 是否为 NULL → 统计非 NULL 值
SELECT COUNT(name) FROM t_user; -- 正例 1:全表行数统计,让优化器自动选最窄索引(如 idx_status)
SELECT COUNT(*) FROM t_user; -- 反例 2:带条件但无索引,触发全表扫
SELECT COUNT(*) FROM t_user WHERE age > 18; -- 正例 2:给 age 加索引,让数据库走索引树统计(无需回表)
CREATE INDEX idx_age ON t_user(age); 
SELECT COUNT(*) FROM t_user WHERE age > 18; -- 进阶优化:高频统计“某状态行数”,用冗余字段/单独表存储
-- 场景:需实时统计 status=1 的行数,直接查冗余字段
ALTER TABLE t_user ADD COLUMN status_count INT DEFAULT 0; 
-- 插入/更新时维护 status_count,查询时直接 SELECT status_count FROM t_user WHERE status=1;

二、SQL 优化 - 插入数据优化

1. 插入性能瓶颈:从磁盘 IO 到索引维护

插入操作的核心消耗是 “写数据页” 和 “维护索引”,具体流程:

  1. 事务日志(Redo Log):插入前先写日志(确保崩溃恢复),磁盘随机 IO 是瓶颈。
  2. 数据页写入:数据写入内存页,若页未满需等待(或触发页分裂)。
  3. 索引维护:每条数据需更新所有索引树(如主键索引、普通索引),索引越多,耗时越久。
2. 细分场景优化(含代码示例、参数调整)
插入场景核心问题优化手段代码示例 / 参数调整性能提升对比(10 万条数据)
单行插入(高频)事务提交次数多,日志刷盘频繁批量插入 + 调整事务提交策略```sql
-- 反例:单行插入(100 次事务)
INSERT INTO t_log (user_id, content) VALUES (1, 'a');
-- 正例:批量插入(1 次事务)
INSERT INTO t_log (user_id, content) VALUES (1, 'a'), (2, 'b'), ..., (1000, 'z');
```
MySQL 调整:SET autocommit = 0;(关闭自动提交)
从 10s → 1s 左右
高并发插入自增主键锁竞争(AUTO_INCREMENT 锁)用分布式 ID 或调整自增锁模式```sql
-- 方案 1:雪花算法生成主键(Java 示例)
Long id = SnowflakeIdGenerator.nextId();
INSERT INTO t_order (id, user_id) VALUES (id, 123);
-- 方案 2:MySQL 调整自增锁模式(适合批量插入)
SET GLOBAL innodb_autoinc_lock_mode = 2; -- 异步分配自增 ID
| 索引过多插入      | 索引维护耗时占比高(如 5 个索引)  | 先删索引,插入后重建              | ```sql
-- 步骤 1:删除索引
DROP INDEX idx_user ON t_order; 
DROP INDEX idx_create_time ON t_order; 
-- 步骤 2:批量插入(无索引维护开销)
INSERT INTO t_order (...) VALUES (...); 
-- 步骤 3:重建索引
CREATE INDEX idx_user ON t_order(user_id); 
CREATE INDEX idx_create_time ON t_order(create_time); 
``` | 插入耗时从 30s → 5s        |  #### 3. 极端场景:冷热数据分离插入  
```sql
-- 问题:历史表(如 3 年前的订单)插入时,因数据页分散,插入慢
-- 优化:分区表+按时间分区,插入时直接定位到“热分区”
ALTER TABLE t_order PARTITION BY RANGE (TO_DAYS(create_time)) (PARTITION p2023 VALUES LESS THAN (TO_DAYS('2024-01-01')),PARTITION p2024 VALUES LESS THAN (TO_DAYS('2025-01-01')),PARTITION p2025 VALUES LESS THAN (MAXVALUE)
); 
-- 插入时自动路由到对应分区,减少数据页碎片影响
INSERT INTO t_order (create_time, ...) VALUES ('2024-06-01', ...); -- 走 p2024 分区

三、SQL 优化 - 主键优化

1. 主键设计的核心矛盾:“唯一性” vs “插入性能” vs “索引紧凑性”

主键是表的 “根索引”(InnoDB 聚簇索引),其设计直接影响 插入顺序性(是否导致页分裂)和 查询效率(索引树高度)。

2. 主键类型对比(含底层存储、优缺点、适用场景)
主键方案底层存储特点(InnoDB)优点缺点适用场景极端案例(10 亿数据)
自增主键(INT)数据页顺序写入,索引树紧凑(类似数组 append)插入性能高,索引树高度低(查询快)高并发下自增锁可能成为瓶颈中小规模业务、读多写少主键占 4B,索引树高度~3(快)
分布式 ID(雪花算法)主键随机不连续,但全局唯一(如 64 位 Long)无锁竞争,支持超高并发插入索引树碎片化(随机写导致页分裂)海量数据、高并发写入场景主键占 8B,索引树高度~4(稍慢)
UUID 主键主键完全随机(128 位字符串)全局唯一,无需依赖数据库索引树碎片化严重(插入性能暴跌)绝对禁止使用主键占 36B,索引树高度~5(极慢)
3. 主键优化实操(解决索引碎片、锁竞争)
-- 问题 1:删除+插入频繁,主键索引碎片化(查询变慢)
-- 步骤 1:查看索引碎片率(MySQL)
SELECT TABLE_NAME, INDEX_NAME, INDEX_TYPE, DATA_FREE -- 碎片大小
FROM INFORMATION_SCHEMA.STATISTICS 
WHERE TABLE_SCHEMA = 'test_db' AND TABLE_NAME = 't_user'; -- 步骤 2:整理碎片(InnoDB 会重建聚簇索引)
OPTIMIZE TABLE t_user; -- 步骤 3:重建后查看碎片(DATA_FREE 大幅减少)
SELECT ...(同上); -- 问题 2:自增主键高并发锁竞争
-- 方案:调整自增锁模式(MySQL 8.0+)
SET GLOBAL innodb_autoinc_lock_mode = 2; -- 异步分配自增 ID,减少锁等待
-- 注意:需确保 binlog 格式为 ROW(避免主从同步问题)

四、SQL 优化 - UPDATE 优化(避免行锁升级为表锁)

1. 锁机制底层逻辑:行锁 → 间隙锁 → 表锁的升级
  • 行锁(Record Lock):仅锁定匹配条件的行,需 WHERE 条件命中唯一索引(如主键、唯一索引)。
  • 间隙锁(Gap Lock):锁定索引区间(防止幻读),若条件用范围查询(如 age > 18)且无唯一索引,会触发间隙锁。
  • 表锁(Table Lock):若条件无索引,数据库会全表扫描 + 锁表,阻塞所有操作。
2. 行锁优化:从反例到正例(含执行计划分析)
-- 反例 1:无索引,触发全表扫+表锁
-- 执行计划:type = ALL(全表扫),rows = 1000000(扫描 100 万行)
UPDATE t_order SET status=1 WHERE create_time < '2023-01-01'; -- 正例 1:给 create_time 加索引,触发行锁(仅锁匹配行)
-- 执行计划:type = range(索引范围扫),rows = 1000(扫描 1000 行)
CREATE INDEX idx_create_time ON t_order(create_time); 
UPDATE t_order SET status=1 WHERE create_time < '2023-01-01'; -- 反例 2:批量更新无 LIMIT,锁太多行导致阻塞
UPDATE t_order SET status=1 WHERE status=0; -- 若 status=0 有 10 万行,锁竞争严重-- 正例 2:拆分批量更新,控制每次锁的行数
-- 每次更新 100 行,循环执行直到完成
WHILE (1=1) DO UPDATE t_order SET status=1 WHERE status=0 LIMIT 100; IF ROW_COUNT() = 0 THEN LEAVE; END IF; -- 无更新时退出
END WHILE; -- 进阶优化:显式缩短事务时间(减少锁持有时间)
BEGIN;
UPDATE t_order SET status=1 WHERE id=123; -- 走主键索引,行锁
COMMIT; -- 立即释放锁,不阻塞其他操作
3. 锁升级监控与排查(MySQL 为例)
-- 查看当前锁等待情况
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; 
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS; -- 定位慢更新 SQL(结合慢查询日志)
-- 慢查询日志配置:
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1 -- 超过 1 秒的 SQL 记录

五、总结:SQL 优化的核心逻辑

所有优化本质围绕 “减少 IO 次数、缩小锁范围、让索引高效命中” 展开,关键是理解数据库执行计划(如 EXPLAIN),识别以下问题:

  1. COUNT:是否触发全表扫、是否判 NULL;
  2. 插入:是否批量提交、是否索引过多;
  3. 主键:是否选对类型、是否有碎片;
  4. UPDATE:是否走索引、是否锁范围过大。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/93797.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/93797.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/93797.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Tomcat 的核心脚本catalina.sh 和 startup.sh的关系

catalina.sh 和 startup.sh 都是 Tomcat 的核心脚本&#xff0c;但它们的角色和使用场景有所不同。以下是它们的主要区别和适用场景&#xff1a;1. 功能区别脚本主要用途底层调用关系startup.sh一个快捷入口脚本&#xff0c;用于快速启动 Tomcat&#xff08;后台模式&#xff0…

飞算JavaAI:简易贪吃蛇小游戏

目录先确定核心功能技术选型核心功能实现过程1. 数据模型设计2. 游戏界面和绘制逻辑3. 游戏主框架和事件处理飞算JavaAI在开发中的应用体验可以进一步优化的地方作为Java课程的小作业&#xff0c;不想做太复杂的管理系统&#xff0c;就选了贪吃蛇这个经典小游戏。全程用Swing做…

如何保障内部网络安全前提下,实现与外部互联网之间的文件传输?

在数字化时代&#xff0c;企业网络环境日益复杂&#xff0c;普遍采用“内外网隔离”的安全架构&#xff1a;内部办公网承载业务系统与数据&#xff0c;外部互联网则用于对外沟通与信息获取。这种隔离有效抵御了外部攻击&#xff0c;但也带来了“信息孤岛”问题——如何在保障内…

计算机视觉 图片处理 在骨架化过程中,每次迭代都会从图像的边缘移除一层像素,直到只剩下单像素宽度的骨架

你说得对&#xff0c;if cv2.countNonZero(binary) 0: break 这个条件确实表示图像中已经没有非零像素&#xff0c;即图像完全变为空白。这并不是骨架化完成的标志&#xff0c;而是表示图像已经被腐蚀到没有任何内容了。 在骨架化过程中&#xff0c;我们需要一个更合适的停止条…

rt-thread audio框架移植stm32 adc+dac,用wavplayer录音和播放

D1 参考 rt-thread官方sdk中&#xff0c;正点原子stm32f429-atk-appollo的board中有audio文件夹&#xff0c;包括了mic/play的程序&#xff0c;wm8978的库文件因为我们基于stm32h750内置adcdac设计&#xff0c;所以不需要wm8978.c/h。只需要移植drv_sound.c和drv_mic.c D2 工程…

AI重塑软件测试:质量保障的下一站

软件开发的世界变化飞快&#xff0c;系统越来越复杂&#xff0c;用户的胃口越来越大&#xff0c;产品上线的压力也越来越大。作为测试工程师&#xff0c;你是不是常常觉得传统测试已经跟不上节奏了&#xff1f;手工测试累死人&#xff0c;自动化脚本维护到崩溃&#xff0c;测试…

【前端基础知识系列六】React 项目基本框架及常见文件夹作用总结(图文版)

在 React 开发中&#xff0c;一个清晰合理的项目结构不仅能提高开发效率&#xff0c;还能让代码更易于维护和扩展。尤其是在团队协作中&#xff0c;统一的项目结构规范至关重要。本文将通过图文结合的方式&#xff0c;详细介绍 React 项目的基本框架以及常见文件夹的定义与作用…

0815 UDP通信协议TCP并发服务器

Part 1.思维导图一.UDP通信协议1.原理服务器端&#xff1a;1.用socket函数创建一个套接字文件2.创建服务器端地址结构体并赋值3.用ford函数将套接字文件与地址结构体绑定4.创建接收客户端地址结构体5.利用sendto和recvfrom函数传输和接收信息客户端&#xff1a;1.用socket函数创…

一个基于纯前端技术实现的五子棋游戏,无需后端服务,直接在浏览器中运行。

一 功能特性1.1 核心游戏功能- **标准五子棋规则**&#xff1a;1515棋盘&#xff0c;黑子(玩家)先手 - **AI对战模式**&#xff1a;白子AI具有中等难度&#xff0c;会进行智能进攻和防守 - **胜负判定**&#xff1a;支持横向、纵向、斜向五子连线获胜 - **平局检测**&#xff1…

HBuilderX升级,Vue2 scss 预编译器默认已由 node-sass 更换为 dart-sass

目录 一、问题描述 二、问题原因 三、问题解析及解决方案 一、问题描述 最近开发新项目&#xff0c;升级了HBuilderX版本到4.75&#xff0c;最近要在之前的项目添加功能的时候发现报错&#xff0c;错误如下&#xff1a;Vue2 scss 预编译器默认已由 node-sass 更换为 dart-sa…

像素风球球大作战 HTML 游戏

像素风球球大作战 HTML 游戏 下面是一个简单的像素风格球球大作战 HTML 游戏代码&#xff1a; <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-widt…

文件导出时无法获取响应头Content-Disposition的文件名

1. 为什么Content-Disposition无法获取&#xff1f; 要拿到 Content-Disposition 里的 filename&#xff0c;可以用正则或者简单的字符串解析。 浏览器默认不让前端访问非标准响应头&#xff0c;Content-Disposition 需要后端显式暴露。 在浏览器开发者工具 → Network → Re…

Leetcode 128. 最长连续序列 哈希

原题链接&#xff1a; Leetcode 128. 最长连续序列 解法1: map&#xff0c;不符合要求 class Solution { public:int longestConsecutive(vector<int>& nums) {if (nums.size()0) return 0;map<int,int> mp;for(auto x: nums){mp[x];}int pre;int l0,r0,res0;…

禾赛激光雷达AT128P/海康相机(2):基于欧几里德聚类的激光雷达障碍物检测

目录 一、参考连接 二、实验效果​编辑 三、安装相应的 ros 依赖包 四、代码驱动 4.1 代码下载 4.2 代码文件放置(请按照这个命名放置代码) 4.3 代码编译 4.4 报错 一、参考连接

Vue Router的常用API有哪些?

文章目录一、路由配置相关二、路由实例方法&#xff08;router 实例&#xff09;三、组件内路由 API&#xff08;useRouter / useRoute&#xff09;四、导航守卫&#xff08;路由拦截&#xff09;五、路由视图与导航组件六、其他常用 API七、history模式和hash模式有什么区别&a…

从现场到云端的“通用语”:Kepware 在工业互联中的角色、使用方法与本土厂商(以胡工科技为例)的差异与优势

从现场到云端的“通用语”&#xff1a;Kepware 在工业互联中的角色、使用方法与本土厂商&#xff08;以胡工科技为例&#xff09;的差异与优势 文章目录从现场到云端的“通用语”&#xff1a;Kepware 在工业互联中的角色、使用方法与本土厂商&#xff08;以胡工科技为例&#x…

深入理解Prompt构建与工程技巧:API高效实践指南

深入理解Prompt构建与工程技巧&#xff1a;API高效实践指南 引言 Prompt&#xff08;提示&#xff09;工程是推动大模型能力极限的关键手段。合理的Prompt不仅能显著提升模型输出的相关性与准确性&#xff0c;在实际落地的API接口开发中同样起到举足轻重的作用。本文将系统介…

C++之多态(从0到1的突破)

世间百态&#xff0c;每个人都扮演着不同的角色&#xff0c;都进行着不同的行为。C更是如此&#xff0c;C中也会出现有着不同行为的多种形态的出现&#xff0c;那就让我们一起进入C的多态世界吧&#xff01;&#xff01;&#xff01; 一. 多态的概念 多态&#xff0c;顾名思义&…

路由器NAT的类型测定

目前所使用的NAT基本都是NAPT&#xff0c;即多端口的NAT技术&#xff0c;因此本文主要是设计了两种测定路由器NAPT类型的实验。 实验环境 设备 主机A&#xff1a;Windows主机B&#xff1a;Windows路由器 软件 ncWiresharkSocketTools 在局域网内部完成所有测试&#xff0c;完全…

ROS 2系统Callback Group概念笔记

核心概念 Callback Group&#xff08;回调组&#xff09;是一个管理一个或多个回调函数执行规则的容器。它决定了这些回调函数是如何被节点&#xff08;Node&#xff09;的 executor 调度的&#xff0c;特别是当多个回调函数同时就绪时&#xff0c;它们之间是并行执行还是必须串…