news 2026/9/9 15:16:08

SQL IN 用法完全指南:从基础语法到性能优化与 NULL 陷阱

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SQL IN 用法完全指南:从基础语法到性能优化与 NULL 陷阱

如果你写 SQL 已经有段时间,一定遇到过这种场景:想查某个城市的所有用户,条件里要匹配“北京、上海、广州、深圳”四个值。新手第一反应是写四个OR,老手会顺手写一个IN。但IN真的只是“多个 OR 的简写”吗?

如果你这么想,那今天这篇文章值得看完。因为IN看起来是 SQL 里最简单的语法之一,实际使用中却藏着不少坑:NULL值导致的诡异结果、NOT IN返回空表的经典陷阱、子查询中数据量过大引发的性能问题、以及IN列表拼接带来的 SQL 注入风险。

这篇文章会从IN的核心语义讲起,结合完整示例、性能对比、常见误区和排查思路,帮你把这个“简单语法”真正用明白。无论你是刚学 SQL 的初学者,还是在生产环境写复杂查询的开发者,都能从中得到一些有用的判断依据。

1. 为什么需要 IN:从一段真实的查询需求说起

先看一个最常见的开发场景。假设你维护一张用户表users,需要统计华东区域核心城市的注册用户数。城市条件是“上海、杭州、南京、苏州、合肥”。

没有IN的时候,你只能这样写:

SELECT COUNT(*) FROM users WHERE city = '上海' OR city = '杭州' OR city = '南京' OR city = '苏州' OR city = '合肥';

这段 SQL 能跑,但问题很明显:

  • 条件一多,SQL 冗长得像裹脚布。
  • OR的优先级容易和AND混淆。比如你再加一个status = 1的条件,不加括号就可能出错。
  • 将来要调整城市列表,你得改 SQL 里的每一行条件,维护成本高。

IN改写之后,同样逻辑变成:

SELECT COUNT(*) FROM users WHERE city IN ('上海', '杭州', '南京', '苏州', '合肥');

一句话总结:IN解决的是“某个字段的值是否匹配集合中任意一个成员”的判断需求。它让 SQL 的表达式更接近自然语言,可读性更高,维护成本更低。

但这里必须给一个明确判断:IN不是简单的语法糖,它的执行语义、边界行为、性能特征都和OR有区别。如果只把它当成“多个 OR 的缩写”,遇到NULL、子查询、大数据量场景时就会踩坑。后面的章节会逐个拆解。

2. IN 的核心概念与基础语法

2.1 什么是 IN

IN是 SQL 中的条件运算符,用于判断一个表达式的值是否包含在指定的值列表或子查询结果集中。它的结果是一个布尔值:匹配成功返回TRUE,匹配失败返回FALSE,如果涉及NULL则可能返回UNKNOWN(这部分是重点,后面专门讲)。

基本语法有两种形式:

-- 形式一:值列表 expression IN (value1, value2, value3, ...) -- 形式二:子查询 expression IN (SELECT column FROM table WHERE condition)

2.2 IN 的一个完整示例

为了后续讲解方便,先建一张简单的订单表,模拟电商场景:

-- 文件路径:demo.sql CREATE TABLE orders ( id INT PRIMARY KEY, customer_name VARCHAR(50), city VARCHAR(50), amount DECIMAL(10, 2), status VARCHAR(20) ); INSERT INTO orders (id, customer_name, city, amount, status) VALUES (1, '张三', '上海', 100.00, '已完成'), (2, '李四', '杭州', 250.00, '待付款'), (3, '王五', '南京', 80.50, '已完成'), (4, '赵六', '北京', 320.00, '已取消'), (5, '孙七', '广州', 150.00, '已完成'), (6, '周八', '上海', 600.00, '待发货'), (7, '吴九', '杭州', NULL, '已完成'), (8, '郑十', '成都', 90.00, '已完成');

查询华东城市(上海、杭州、南京)的订单:

SELECT id, customer_name, city, amount FROM orders WHERE city IN ('上海', '杭州', '南京');

运行结果:

idcustomer_namecityamount
1张三上海100.00
2李四杭州250.00
3王五南京80.50
6周八上海600.00
7吴九杭州NULL

注意第 7 行,amountNULL,但city匹配IN条件,所以记录正常返回。这说明IN只判断你写在条件里的列,其他列的NULL值不影响行是否被选中。

2.3 IN 的类型匹配规则

IN后面的值列表必须与左侧表达式的数据类型兼容。比如city是字符串,列表里就不能写数字。某些数据库(如 MySQL)在非严格模式下会做隐式转换,但强烈建议不要依赖这种行为。

-- 不推荐:依赖隐式类型转换 SELECT * FROM orders WHERE city IN (1, 2, 3); -- 推荐:类型保持一致 SELECT * FROM orders WHERE city IN ('上海', '杭州');

2.4 IN 对 NULL 的处理原则

这是IN最容易踩坑的地方,先记住两条结论:

  1. IN列表中包含NULL,不会匹配到任何“值为 NULL”的行。
  2. NOT IN的子查询或列表中存在NULL,整个查询可能返回空结果。

具体原因在 6.1 节详细展开。这里先看一个直观例子:

-- 查询城市为 NULL 的订单 SELECT * FROM orders WHERE city IN (NULL);

这条 SQL 不会返回第 7 行那样的“城市为 NULL”的记录。因为在 SQL 的三值逻辑中,NULL IN (NULL)的结果是UNKNOWN,而WHERE子句只接受TRUE,不接受TRUE以外的值。

3. IN 与 OR、= ANY 的对比

很多人想知道INOR到底有什么区别,性能是否一样。答案取决于数据库实现和具体场景。

3.1 IN 与 OR 的等价性

如果列表中没有NULL,且表达式不涉及子查询,INOR在逻辑上是等价的:

WHERE city IN ('上海', '杭州'); -- 等价于 WHERE city = '上海' OR city = '杭州';

从语义上,这两条 SQL 返回相同的行。但从工程角度看,IN的可读性和可维护性明显更好。

3.2 与 AND 混用时的优先级坑

OR的优先级低于AND,多个条件混用时容易出错。比如要查“上海或杭州的已完成订单”:

-- 错误写法:AND 优先级更高,实际语义变成了“上海的全部订单 + 杭州的已完成订单” SELECT * FROM orders WHERE city = '上海' OR city = '杭州' AND status = '已完成'; -- 正确写法:加括号 SELECT * FROM orders WHERE (city = '上海' OR city = '杭州') AND status = '已完成'; -- 更推荐写法:用 IN 避免优先级问题 SELECT * FROM orders WHERE city IN ('上海', '杭州') AND status = '已完成';

这个例子很好地说明:IN不只是简化书写,还能减少因为运算符优先级导致的逻辑错误。

3.3 = ANY 与 IN 的关系

在 PostgreSQL 中,IN实际上可以写成= ANY

SELECT * FROM orders WHERE city = ANY (ARRAY['上海', '杭州']);

两者的执行计划通常是等价的。= ANY的写法更灵活,因为数组可以动态传入;IN的写法更直观。其他数据库也有各自的等价形式,比如 MySQL 中可以用JSON_CONTAINSFIND_IN_SET做类似事情,但都不如IN直接。

3.4 选择建议

场景推荐写法原因
固定值列表,2~100 个值IN可读性好,维护方便
条件中包含AND且列表较长IN避免OR优先级问题
值列表需要动态传入IN配合参数化查询或= ANY防止 SQL 注入
子查询返回结果集INEXISTS(视场景)大数据量时需结合执行计划判断

4. IN 在子查询中的使用与注意事项

IN最常见的进阶用法是搭配子查询。比如查“下过单的用户”:

SELECT id, customer_name FROM users WHERE id IN (SELECT user_id FROM orders);

这种写法逻辑清晰,但有几个实际问题需要注意。

4.1 子查询结果集大小

如果子查询返回的数据量很大,IN的性能可能成为问题。不同数据库的处理方式不同:

  • MySQL 对IN子查询做了优化,但列表长度极大时仍可能产生临时表和额外开销。
  • Oracle 对超过 1000 项的IN列表会直接报错ORA-01795
  • SQL Server 对IN列表长度也有限制,虽然现代版本放宽了不少,但超长列表依然是性能隐患。

实际开发中,如果子查询可能返回上万条数据,通常建议改用EXISTSJOIN。一个经验法则是:外层表小、子查询大时用IN;外层表大、子查询小时用EXISTSJOIN但这只是经验,最终要看执行计划。

4.2 IN 子查询的 NULL 陷阱

NOT IN遇到子查询中包含NULL,结果可能出现空表。这是数据库面试和实际开发中非常经典的一个坑。

先看一个场景。orders表中的user_id存在NULL,表示匿名订单:

-- 建表模拟 CREATE TABLE users (id INT PRIMARY KEY, customer_name VARCHAR(50)); INSERT INTO users VALUES (1, '张三'), (2, '李四'), (3, '王五'); -- 查询下过单的用户(注意:orders 中可能包含 NULL 的 user_id) SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM orders);

如果orders.user_id中有一个NULL,这条查询不会返回任何行。原因在于 SQL 的三值逻辑:

  • id NOT IN (1, 2, NULL)等价于id <> 1 AND id <> 2 AND id <> NULL
  • id <> NULL的结果是UNKNOWN
  • 整个AND链中有一个UNKNOWN,最终结果就是UNKNOWN
  • WHERE只接受TRUE,所以所有行都被过滤掉。

解决办法有三种:

-- 方案一:子查询中过滤 NULL(推荐) SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM orders WHERE user_id IS NOT NULL); -- 方案二:使用 NOT EXISTS SELECT * FROM users u WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id); -- 方案三:使用 LEFT JOIN + IS NULL SELECT u.* FROM users u LEFT JOIN orders o ON o.user_id = u.id WHERE o.id IS NULL;

三种方案中,NOT EXISTS通常最稳妥,既能避免NULL问题,又更容易利用索引。但这不意味着NOT IN一无是处,只要你能保证子查询结果不含NULLNOT IN依然可读且高效。

4.3 IN 子查询与 EXISTS 的选择

INEXISTS在语义上很接近,但它们的工作方式不同:

  • IN会先执行子查询,生成结果集,再对外层表的每一行做匹配。
  • EXISTS对外层表的每一行,去子查询中检查是否存在满足条件的行,找到即停止。

两种写法在数据量分布不同时性能差异很大。更关键的是,EXISTSNULL的容忍度更高,因为它在“行存在性”层面判断,不受NULL三值逻辑影响。

一般建议:

  • 子查询结果集较小、外层表较大:IN通常不差。
  • 子查询结果集很大、外层表较小:优先考虑EXISTS
  • NULL敏感或无法确定子查询是否含NULL:优先EXISTS
  • 需要返回子查询中的多个列做进一步判断:考虑JOIN

5. IN 与 JOIN 的取舍:什么时候别用 IN

如果说INOR的对比是语法层面,那INJOIN的对比就是执行计划层面。很多开发者习惯性用IN做关联查询,但有些场景改成JOIN更合适。

5.1 数据量不同时的表现差异

一个典型例子:查“订单金额超过 100 的用户信息”。

-- 用 IN(思路:先圈出符合条件的 user_id,再匹配用户) SELECT * FROM users WHERE id IN ( SELECT user_id FROM orders WHERE amount > 100 ); -- 用 JOIN(思路:直接把用户和订单关联,再过滤) SELECT DISTINCT u.* FROM users u JOIN orders o ON o.user_id = u.id WHERE o.amount > 100;

两个写法的语义相近,但执行方式不同。如果orders表巨大,而amount > 100的订单也很多,IN生成的中间结果集可能很大;JOIN则可以通过驱动表的选择和索引优化,得到更好的执行计划。

DISTINCT也是成本,如果用户和订单是一对多关系,需要去重。所以不能说JOIN永远更好,要结合索引、数据分布和执行计划判断。

5.2 什么时候必须用 JOIN

如果你需要从子查询中获取多个字段,IN就无能为力了。比如要查“最近下单超过 3 次的用户,并且显示其最近一次下单时间”:

-- 错误:IN 无法取出子查询中的其他字段 SELECT u.*, o.last_order_time FROM users u WHERE u.id IN (SELECT user_id, MAX(created_at) FROM orders ...); -- 语法错误 -- 正确:用 JOIN 子查询 SELECT u.*, t.last_order_time FROM users u JOIN ( SELECT user_id, MAX(created_at) AS last_order_time FROM orders GROUP BY user_id HAVING COUNT(*) > 3 ) t ON t.user_id = u.id;

5.3 选择框架

需求场景推荐工具说明
判断某个值是否在集合中IN最直接
判断存在性且外层表大EXISTS更容易走索引
需要关联多表并返回多列JOIN表达能力更强
排除某个集合先过滤NULLNOT IN/NOT EXISTS注意NULL陷阱

6. IN 与 NULL 的深入剖析

NULL是 SQL 中最容易让人困惑的概念,而INNULL的交互更是坑中坑。这一节从三值逻辑讲清楚。

6.1 SQL 的三值逻辑

SQL 中的逻辑判断不只是TRUEFALSE,还有第三个值UNKNOWNNULL参与比较、算术、逻辑运算时,结果往往是UNKNOWN

先看IN的基本判断规则:

表达式结果
'上海' IN ('上海', '杭州')TRUE
'北京' IN ('上海', '杭州')FALSE
NULL IN ('上海', '杭州')UNKNOWN
NULL IN (NULL)UNKNOWN
'上海' IN ('上海', NULL)TRUE
'北京' IN ('上海', NULL)UNKNOWN

注意看最后一行:这里没有返回FALSE,而是UNKNOWN。原因是'北京' = NULL的结果是UNKNOWN,而UNKNOWN OR FALSE的结果依然是UNKNOWN

6.2 WHERE 子句只接受 TRUE

WHERE子句只保留结果为TRUE的行,FALSEUNKNOWN都会被过滤。所以:

SELECT * FROM orders WHERE city IN (NULL);

返回空结果。这符合 SQL 标准,但不符合很多人的直觉。

6.3 NOT IN 的 NULL 陷阱

再看NOT IN

SELECT * FROM users WHERE id NOT IN (1, 2, NULL);

等价于:

SELECT * FROM users WHERE id <> 1 AND id <> 2 AND id <> NULL;

id <> NULL的结果是UNKNOWN。SQL 中TRUE AND UNKNOWN的结果是UNKNOWN,所以整行都不会被返回。

这就是为什么NOT IN遇到NULL会返回空表的根本原因。

6.4 如何安全地使用 NOT IN

最佳实践是:使用NOT IN之前,必须确保子查询或列表中没有NULL

-- 安全写法一:在子查询里过滤掉 NULL SELECT * FROM users WHERE id NOT IN ( SELECT user_id FROM orders WHERE user_id IS NOT NULL ); -- 安全写法二:干脆不用 NOT IN SELECT * FROM users u WHERE NOT EXISTS ( SELECT 1 FROM orders o WHERE o.user_id = u.id );

如果代码是给别人维护的,或者你无法保证数据质量,优先选NOT EXISTS

7. IN 的常见误用与安全问题

7.1 列表过长导致数据库报错或性能下降

不同数据库对IN列表长度有不同限制。Oracle 经典的限制是 1000 项,超过会报ORA-01795: maximum number of expressions in a list is 1000。MySQL 虽然没这么严格的硬限制,但几百上千项的IN列表会让优化器很吃力。

解决方案:

  • 把大列表拆分成多个小列表,用OR拼接,但注意长度和可读性。
  • 数据量大时改用临时表或JOIN
  • 利用应用层分批查询。

7.2 动态拼接 IN 列表的 SQL 注入风险

这是安全领域的高危问题。很多开发者在业务代码里这样写:

// 危险示例:千万不要这么写 String ids = request.getParameter("ids"); // 例如 "1,2,3; DROP TABLE users" String sql = "SELECT * FROM users WHERE id IN (" + ids + ")";

如果ids来自用户输入,攻击者可以构造恶意内容,导致 SQL 注入。正确的做法是使用参数化查询:

// 以 JDBC 为例:使用占位符 String sql = "SELECT * FROM users WHERE id IN (?, ?, ?)"; PreparedStatement ps = connection.prepareStatement(sql); ps.setInt(1, 1); ps.setInt(2, 2); ps.setInt(3, 3);

对于动态长度的列表,可以使用ANY数组语法(PostgreSQL)或动态拼接占位符:

// 动态生成占位符,但值通过参数绑定 List<Integer> idList = Arrays.asList(1, 2, 3); String placeholders = idList.stream().map(id -> "?").collect(Collectors.joining(", ")); String sql = "SELECT * FROM users WHERE id IN (" + placeholders + ")"; PreparedStatement ps = connection.prepareStatement(sql); for (int i = 0; i < idList.size(); i++) { ps.setInt(i + 1, idList.get(i)); }

核心原则:无论列表内容如何变化,用户输入永远只能作为参数值进入 SQL,不能作为 SQL 片段拼接。

7.3 IN 列表中的隐式类型转换

字符串列和整数列表混用,可能导致索引失效或全表扫描:

-- 如果 id 是 VARCHAR 类型,下面的写法可能让索引失效 SELECT * FROM users WHERE id IN (1, 2, 3);

建议保持类型一致:要么id是整数型,要么列表里写字符串:

SELECT * FROM users WHERE id IN ('1', '2', '3');

7.4 值列表中有重复项

IN值列表中有重复项不会影响查询结果,因为集合语义天然去重。但这是一个代码气味,说明生成列表的逻辑可能有问题,建议在应用层去重。

8. IN 的性能分析与优化建议

8.1 不要凭感觉判断性能

很多人一听到“用 JOIN 替代 IN”,就觉得性能一定会提升。实际上,IN在合理的数据量和索引条件下,性能完全可以接受。问题的关键是:你有没有看执行计划。

以 MySQL 为例:

EXPLAIN SELECT * FROM orders WHERE city IN ('上海', '杭州', '南京');

观察type列:如果出现ALL(全表扫描),就需要考虑加索引;如果出现rangeref,说明索引有效。

8.2 IN 列表大小的影响

IN列表比较小时,优化器通常能转化为多个等值比较,利用索引;列表变大后,可能退化为全表扫描或产生临时表。MySQL 中IN列表元素过多时,优化器会将其与range条件等同,最终表现取决于实际成本和统计信息。

8.3 IN 子查询的优化方向

  • 子查询返回的结果集最好是有索引的唯一值或主键。
  • 避免在子查询中做不必要的全表扫描。
  • 必要时将子查询改为JOIN,让优化器有更多选择。

8.4 一条实用的判断路径

遇到IN相关查询变慢,按下面顺序排查:

  1. EXPLAIN查看执行计划,是否走索引。
  2. IN列表长度和数据量,是否超出合理范围。
  3. 检查子查询是否包含NULL、重复值或复杂计算。
  4. 对比EXISTSJOIN的执行计划,选最优。
  5. 如果列表是动态传入的,确认没有造成隐式类型转换。

9. 常见问题与排查方法

下面用表格整理一下最常见的IN使用问题:

问题现象可能原因排查方式解决方案
NOT IN查询返回空结果子查询或列表中存在NULL单独执行子查询,检查是否有NULL子查询加IS NOT NULL,或改用NOT EXISTS
Oracle 报 ORA-01795IN列表超过 1000 项统计列表长度拆分列表,或用临时表JOIN
查询走了全表扫描,速度慢条件列没有索引,或类型不匹配EXPLAIN查看执行计划加索引,统一类型
IN列表拼接导致 SQL 注入使用了字符串拼接 SQL代码审查,检查持久层写法改用PreparedStatement参数化查询
INAND混用时结果不对OR优先级高于预期检查 SQL 逻辑使用IN或加括号明确优先级
大数据量IN子查询响应慢子查询结果集过大,临时表开销高查看慢查询日志和执行计划EXISTSJOIN,子查询内先过滤
IN列表包含重复项应用层列表生成逻辑问题检查传入参数应用层去重

10. 最佳实践:生产环境中如何用好 IN

10.1 优先使用参数化查询

无论IN列表是固定值还是动态传入,都推荐使用参数绑定,避免 SQL 注入风险。这也符合数据库安全规范中的最小权限和输入校验原则。

10.2 警惕 NOT IN 的 NULL 问题

NOT IN时,默认假设数据里可能有NULL。建议在代码规范层面规定:除非显式过滤了NULL,否则不用NOT IN,改用NOT EXISTSLEFT JOIN

10.3 列表长度控制

  • 固定业务枚举:通常没问题,但保持列表可读性。
  • 动态数据集合:建议控制在几十到几百个值以内,超长时使用临时表或JOIN
  • 子查询返回结果集:用执行计划验证,不要让子查询返回超大结果集。

10.4 索引设计配合

如果IN经常出现在查询条件中,要为对应列设计索引。例如:

CREATE INDEX idx_orders_city ON orders(city);

复合索引需要根据实际查询模式决定。比如经常用city + status查询,可以考虑:

CREATE INDEX idx_orders_city_status ON orders(city, status);

10.5 日志与监控

线上系统建议开启慢查询日志。凡是超过阈值(比如 1 秒)的 SQL,无论是否包含IN,都要记录下来并定期分析。很多IN问题不是一开始就存在,而是随着数据量增长逐渐显现。

10.6 测试环境先行

修改涉及IN的查询逻辑前,一定要在测试环境执行计划,确认索引和行数符合预期。生产环境数据库的变更,必须经过备份、灰度、回滚方案评估。

11. 总结与后续学习方向

IN是 SQL 中最常用、也最容易被低估的语法之一。它的核心价值在于用一种声明式的方式表达“值属于某个集合”的判断,让查询更简洁、更接近业务语言。但同时,INNULL、子查询、大数据量的交互非常微妙,使用不当会产生“返回空结果”“全表扫描”“SQL 注入”等严重问题。

读完这篇文章,你应该能回答下面几个问题:

  • INOR有什么区别?为什么推荐用IN
  • NOT IN返回空结果的根源是什么?
  • IN子查询和EXISTSJOIN如何选择?
  • 动态拼接IN列表如何避免 SQL 注入?
  • 生产环境中IN查询变慢,排查路径是什么?

下一步,建议你打开本地数据库,把文中的示例跑一遍,尤其是NULL相关的场景。亲手看一眼“NOT IN遇到NULL返回空表”的现象,比背十遍理论都有用。然后找一条业务里真实存在的慢 SQL,用EXPLAIN分析它是否适合继续用IN

SQL 的学习很像搭积木:单个语法用熟了,后面学窗口函数、CTE、执行计划优化都会更顺。如果你对EXISTSJOININ的性能对比感兴趣,可以继续深入了解数据库优化器的实现原理,这是从“会写 SQL”走向“会调 SQL”的必经之路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 15:14:59

Design Compiler:解组(Ungroup)

相关阅读 Design Compilerhttps://blog.csdn.net/weixin_45791458/category_12738116.html?spm1001.2014.3001.5482 解组(Ungroup)指的是指将某一层级中的子设计&#xff08;或者说模块&#xff09;合并到其父设计中&#xff0c;通常在当前设计的层次划分不合理导致优化受限时…

作者头像 李华
网站建设 2026/9/9 15:13:31

栈溢出入门实战:从jarvisoj_level2学会ret2text与返回地址覆盖

得&#xff0c;看到这个标题&#xff0c;老pwn手应该都懂——又一道经典的栈溢出入门题。BUUCTF上的jarvisoj_level2&#xff0c;属于那种你刷完一遍之后&#xff0c;会把整个ret2text和ret2shellcode的底层逻辑都彻底理顺的题目。如果你刚开始接触PWN&#xff0c;第一次打开这…

作者头像 李华
网站建设 2026/9/9 15:13:14

连续-离散耦合模拟SHPB岩石动态破裂:FLAC-PFC建模与参数标定全流程

做岩石动力学试验的人应该都有这种感觉&#xff1a;真实试件里的裂纹是怎么起裂、怎么扩展、又是在哪个应力水平下突然碎成几块的&#xff0c;光靠实验曲线只能猜个大概。尤其SHPB这类冲击试验&#xff0c;加载时间只有几十到几百微秒&#xff0c;高速相机能拍到宏观破碎过程&a…

作者头像 李华
网站建设 2026/9/9 15:13:03

SQL Server附加失败?MDF还在数据就还在:三大修复路线实战

接手任何一个“数据库迁移后附加失败”的问题&#xff0c;很多人的第一反应都是先慌一会儿&#xff1a;服务器换了、报错看不懂、数据库挂在“正在恢复”或者直接变成 SUSPECT&#xff0c;现场客户盯着你&#xff0c;网上的教程搜来搜去还都是“删掉 LDF 重建日志”这种一句话带…

作者头像 李华
网站建设 2026/9/9 15:12:29

手写线程安全消息队列:条件变量与生产者消费者模型实战

1. 从"裸奔"的共享变量到消息队列&#xff1a;先看一个真实案例我接手过一个采集程序&#xff0c;最初版本用的是最直白的做法&#xff1a;一个生产者线程不断往全局链表里塞数据&#xff0c;消费者线程定时醒来遍历链表&#xff0c;为了防冲突给链表挂了一把大锁。前…

作者头像 李华