MySQL锁机制与死锁排查实战:从行锁、间隙锁到索引优化

这篇文章的选题我很早就想写了。起因是前阵子帮一个社区的朋友排查线上死锁,他跑了一个定时任务,每天凌晨批量更新订单状态,结果第二天一上班就收到一堆锁等待超时的告警。日志一拉,两条 UPDATE 语句堵在同一条索引记录上,偏偏两边还都加了别的事务,谁也等不到谁。我翻了翻他当时的建表语句和事务代码,发现问题根本不在于 SQL 写得不好,而是在于他完全没有意识到 MySQL 的锁机制在背后是怎么运作的。

其实“锁”这个词在 MySQL 里听着抽象,但它几乎决定了你每一个并发事务的成败。很多人知道有“行锁”“表锁”这些名词,可真到了排查死锁的时候,却连SHOW ENGINE INNODB STATUS里的信息都看不懂。这篇就当是给所有被锁困扰过的开发者、运维和 DBA 一份从全局到行级、从原理到实战的完整梳理。你会看到:全局锁到底锁住了什么,表锁和 MDL 锁如何悄悄卡住你的查询,InnoDB 的行锁又是怎样在索引记录上跳舞,以及遇到死锁时,最快能落地的排查和改写手段。

1. 从一条慢 SQL 引出的锁问题

1.1 一次线上事故的复盘

想理解锁机制,最好先回到事故现场。上面提到的那个定时任务,伪代码如下:

BEGIN; UPDATE order_info SET status = 'PAID' WHERE order_no = 'A10001'; UPDATE order_info SET status = 'REFUNDING' WHERE order_no = 'B10002'; COMMIT;

另一个事务则倒过来操作:

BEGIN; UPDATE order_info SET status = 'REFUNDING' WHERE order_no = 'B10002'; UPDATE order_info SET status = 'PAID' WHERE order_no = 'A10001'; COMMIT;

两个事务分别持有对方下一步要更新的行锁,于是互相等待,直到innodb_lock_wait_timeout默认的 50 秒耗尽。MySQL 只能把其中一个事务回滚掉,应用侧就会看到“Lock wait timeout exceeded; try restarting transaction”的报错。

问题看起来是程序写得太“对称”,但我当时让他先跑了一遍SHOW ENGINE INNODB STATUS,发现一个更隐蔽的细节:虽然两条记录在逻辑上不同,但在 B+ 树里它们落在了同一个索引页的相邻位置。事务 A 第一条 UPDATE 除了锁住order_no = 'A10001'这条记录,还在记录之前和之后加上了间隙锁,这就直接把事务 B 要更新的B10002给堵在了更小范围的锁等待里。等你真正弄清楚这个因果链,就会明白:死锁不是运气差,而是加锁顺序、索引选择、隔离级别三者共同作用的结果。

1.2 为什么每个 MySQL DBA 都要懂锁

很多开发者觉得锁只是数据库内部的事,跟写业务没关系。但只要你写过一条带WHERE条件的 UPDATE 或 DELETE,你就已经在和锁打交道了。比如:

DELETE FROM user_login_log WHERE create_time < '2025-01-01';

这条 SQL 看起来只是想清理半年以上的日志,可如果没有合适的索引,MySQL 会扫描主键做全表判断,每扫到一行就加一次行锁。扫描过程中,别的会话想插入新的登录日志,如果被间隙锁挡住,就会出现“明明没碰同一条记录,却互相卡住”的诡异现象。

理解锁机制的收益有三个层面:一是能快速读懂performance_schema.data_lock_waits这类视图,定位到底谁在等谁;二是能在设计表结构和 SQL 的时候主动避开全表更新、大范围扫描这些容易放大的锁范围;三是遇到死锁时,不靠重启数据库、不靠瞎改超时参数来碰运气,而是直接分析加锁顺序,用最小的代码改动解决问题。

2. 锁的类型全景图:全局锁、表锁、行锁各自管什么

2.1 全局锁:Flush tables with read lock 到底锁了什么

全局锁是整个实例维度的锁,执行:

FLUSH TABLES WITH READ LOCK;

之后,整个实例进入只读状态。所有数据表的 DML、DDL 都会被阻塞,直到执行UNLOCK TABLES释放。这个操作最经典的用途是作为全库逻辑备份的锚点。比如用mysqldump做一致性快照时,需要保证备份期间没有任何写入,否则备份出来的数据可能是“半个事务”的状态,恢复回去一定会出问题。

但全局锁的代价太粗暴了。线上业务一执行,等于所有写入全部停摆。于是 InnoDB 提供了更优雅的替代方案:利用 MVCC(多版本并发控制),在备份时开启一个可重复读的隔离级别事务,mysqldump --single-transaction就是靠这个原理,在备份期间不阻塞任何业务写入,依旧能看到一致的数据快照。

我在实际运维中只会在两种场景下考虑全局锁:一是主从切换时为了保证主库和备库的数据点位完全一致,短暂加一下;二是某些历史遗留的表还是 MyISAM 引擎,--single-transaction对它无效,只能靠 FTWRL 保证一致性。如果你遇到的是 InnoDB 表,能用--single-transaction就别碰全局锁。

2.2 表级锁:表锁、MDL 锁、意向锁的协同

表锁是最容易被忽略的“隐藏杀手”。显式的表锁语句是:

LOCK TABLES order_info WRITE;

这个锁会把整张表锁住,其他会话不可以读也不可以写(WRITE 锁下,读也会被阻塞)。虽然 InnoDB 支持行锁,但它同样提供表级锁,只是默认不主动使用。真正的坑其实在 MDL 锁(元数据锁)。

MDL 锁不需要你手动加,它是 MySQL 在 5.5 版本引入的,目的是保护表结构定义。当你执行一条 SELECT 查询时,会先拿一个 MDL 读锁;执行 ALTER TABLE 时,则要拿 MDL 写锁。MDL 读锁之间不互斥,写锁和读锁互斥。这就引出一个非常常见的线上事故:一个慢查询长期占着 MDL 读锁,后面一个 DDL 在排队等写锁,而 DDL 后面所有的查询、更新全部排队等待。最终表现为“数据库莫名不可用”,SHOW PROCESSLIST里能看到大量 “Waiting for table metadata lock” 的会话。

意向锁则是 InnoDB 为了让表锁和行锁能共存而设计的“信号牌”。你执行SELECT ... FOR UPDATE加行级排他锁时,InnoDB 会先在表上加一个“意向排他锁”。另一个会话想对全表加表锁时,一看存在意向排他锁,就知道“有行数据被锁住了”,立即返回冲突。这样查表时不需要逐个扫描行记录,效率高很多。

2.3 行锁:Record Lock、Gap Lock、Next-Key Lock 的三角关系

行锁才是 InnoDB 的看家本领。按锁定的范围,它可以细分成三种:

  • Record Lock(记录锁):只锁索引树上的某一条记录。比如主键id = 10,加锁后其他事务不能修改或删除这一行。
  • Gap Lock(间隙锁):锁住记录之间的“空隙”。比如索引里有 10 和 20 两条记录,间隙锁会把(10, 20)这个区间锁住,作用是防止其他事务在这个区间插入新记录。
  • Next-Key Lock(临键锁):记录锁 + 间隙锁的组合,锁住的范围是(10, 20],即左开右闭区间。它既保护了记录本身,又阻止了在这个区间插入新数据。

为什么要分这么多类型?因为默认的隔离级别是REPEATABLE READ(可重复读),MySQL 必须防止“幻读”——也就是同一事务里,两次相同的查询返回了不同的行集合。如果不锁间隙,事务 A 第一次查id > 10没有记录,事务 B 插入一条id = 15并提交,事务 A 再查一次,就会多出一行,这就是幻读。Next-Key Lock 就是 InnoDB 在 RR 隔离级别下用来狙击幻读的武器。

理解了这三种锁,你在看SHOW ENGINE INNODB STATUS的死锁信息时就能脑补出一幅 B+ 树索引页的图:哪个事务持有什么锁,等待的又是哪个锁,一目了然。这也是我把行锁放到一个单独章节慢慢讲的原因。

3. 行级锁的底层实现与隔离级别

3.1 InnoDB 为什么只认行锁

MySQL 里有很多存储引擎,MyISAM 就只有表锁,InnoDB 才有行锁。要理解行锁的价值,得回到索引结构本身。

InnoDB 的主索引是聚簇索引,数据行就存在 B+ 树的叶子节点上,每条记录都有一个主键值作为唯一标识。行锁本质上是“锁定索引记录”,而不是直接锁磁盘上的数据行。这个说法很关键,因为这意味着:如果你更新条件里没有索引,或没走索引,InnoDB 找不到确定的索引记录,就只能扫描聚簇索引,等于把全表所有索引记录都锁一遍。

行锁的好处是并发度高:不同事务可以同时修改不同行,只要锁定的索引记录不重叠。坏处是拿锁的成本更高,因为要精确定位到叶子节点、检查锁对象是否冲突。这也是为什么 InnoDB 需要一套高效的锁系统来管理锁对象,而不是简单粗暴地在表头挂一把锁。

3.2 RR 隔离级别下的 Gap 锁是怎么产生的

Gap 锁的产生和事务隔离级别直接相关。在 READ COMMITTED 级别,InnoDB 只使用 Record Lock,不会加 Gap Lock,所以不会出现“两个事务之间因为区间插入互相阻塞”的情况,但它会有幻读问题。对于很多业务来说,RC 级别已经够用,而且并发能力更强,所以有不少团队会把隔离级别调成 RC。

但默认的 RR 级别就不同了。我们看一个典型的场景:

-- 事务 A BEGIN; SELECT * FROM orders WHERE amount > 100 FOR UPDATE;

假设orders表里 amount 字段有二级索引,索引中已有 80、120、150 几条记录。事务 A 这条 SQL 不仅会把 120、150 两条记录加上 Record Lock,还会在 80 和 120 之间、120 和 150 之间、以及 150 之后到无穷大的区间加上 Gap Lock。目的就是保证:事务 A 再次执行同样的查询时,不会出现新的满足条件的行。但这也意味着,事务 B 如果尝试插入一条 amount = 130 的新订单,会被 Gap Lock 卡住。

很多人在这个点上踩坑:他们以为只锁住了查出来的两行,结果另一个会话插一条与查询条件相关的新记录也被阻塞。不是 MySQL 锁多了,而是你选的隔离级别决定了它必须帮你防幻读。如果你业务上能接受提交后再读看到最新数据,把隔离级别调成 RC 会显著降低 Gap Lock 出现的概率。

3.3 加锁语句与索引的三次握手

我们可以把一条 UPDATE 的加锁过程简化三步:

  1. 优化器选择索引:根据 WHERE 条件,选择成本最低的执行计划。如果选主键索引,锁直接落在主键行;如果选二级索引,InnoDB 会先在二级索引上加锁,再回表到聚簇索引,把主键对应的记录也锁上。
  2. 遍历索引记录并加锁:每访问到一条索引记录,就按当前隔离级别生成 Record Lock 或 Next-Key Lock。注意,这里的“访问到”指的是扫描过程中碰到的每条记录,不是最终结果集里的每条记录。
  3. 提交或回滚时释放:锁并不是立刻释放的,普通读(一致性读)走 MVCC 不加锁,但 UPDATE、DELETE、SELECT FOR UPDATE 这些加锁读都会持有锁直到事务结束。

拿一个细节举例:假设执行:

UPDATE users SET nickname = 'new_name' WHERE mobile = '13800000000';

如果mobile建了唯一索引,InnoDB 只需要在唯一索引上找到对应记录加锁,再回表锁主键。整个过程锁范围极小。而如果mobile没有索引,优化器只能走主键索引的全表扫描,每扫一条主键记录都会加锁,效果等同于锁全表。这也是我下一章要强调的重点——行锁的高并发优势是建立在索引正确的前提之上的。

4. 死锁排查与优化实战:从 show engine innodb status 到锁等待日志

4.1 两个事务相互等待的完整现场

当我们遇到死锁报错,第一反应应该是拿到死锁现场。MySQL 会把最近一次死锁的信息记录在SHOW ENGINE INNODB STATUS的输出里,重点看LATEST DETECTED DEADLOCK部分。下面是我实际分析过的一个简化例子:

-- 事务 A UPDATE account SET balance = balance - 100 WHERE id = 1; UPDATE account SET balance = balance + 100 WHERE id = 2; -- 事务 B UPDATE account SET balance = balance - 100 WHERE id = 2; UPDATE account SET balance = balance + 100 WHERE id = 1;

两个事务都是先更新 id=1 再更新 id=2,只不过 A 的顺序是 1→2,B 是 2→1。假设 A 先持有 id=1 的锁,B 先持有 id=2 的锁,然后 A 去申请 id=2 的锁时发现被 B 持有,B 去申请 id=1 的锁时发现被 A 持有,死锁形成。InnoDB 检测到死锁后,会选择一个事务回滚,一般是回滚代价较小的那个(按 UNDO 数量估算)。应用层收到的报错信息类似:

Deadlock found when trying to get lock; try restarting transaction

这在日志里不算罕见,正确做法是捕获这个错误码,然后重试整个事务。

4.2 如何快速定位锁等待和源头事务

死锁是“互相等”,而锁等待是“单方向等”。排查锁等待时,我建议按下面三步走:

  1. 查当前锁等待关系:

    SELECT * FROM performance_schema.data_lock_waits;

    这个视图能看到哪个事务(REQUESTING_THREAD_ID)在等哪个事务(BLOCKING_THREAD_ID)持有的哪个锁。比早期的INNODB_LOCKS表信息更完整。

  2. 查正在运行的事务和 SQL:

    SELECT trx_id, trx_state, trx_started, trx_query FROM information_schema.innodb_trx ORDER BY trx_started;

    从这里可以找到长时间未提交的事务。记住,一个事务哪怕是只执行了一条 SELECT,也会开启事务并持有快照,后续对同表执行 UPDATE,就可能和别的正在写入的事务冲突。

  3. 查系统进程:

    SHOW PROCESSLIST;

    找到被卡住会话的状态,如果看到Waiting for lock或Lock wait timeout exceeded,基本可以断定是锁等待。

我在定位时还有一个值得养成的习惯:给事务里的 SQL 打上注释标记,比如/* batchJob:123 */ UPDATE ...,这样看trx_query时能立刻知道是哪个业务模块发起的事务。这个习惯在大规模集群里帮助极大,不然你只能看到一堆抽象 SQL。

4.3 常见死锁场景与 SQL 改写思路

我把这几年遇到的高频死锁场景整理成了三类,每一类都有对应的改写思路。

  • 交叉更新(上面说的 A 和 B 反向更新):核心解法是固定加锁顺序。比如业务上约定所有涉及账户余额的更新,都从小到大按账户 id 排序后再执行。项目里可以在 DAO 层统一封装一个批量更新方法,把入参按 id 排序。

  • 先查后改导致间隙锁叠加:

    BEGIN; SELECT COUNT(*) FROM orders WHERE status = 0 FOR UPDATE; -- 业务逻辑... UPDATE orders SET status = 1 WHERE order_id = 123; COMMIT;

    这种写法的问题在于SELECT FOR UPDATE的 WHERE 条件如果走了二级索引,都可能产生大量 Next-Key Lock。建议先确认能否避免FOR UPDATE,改用普通读 + 乐观锁版本号的方式,或者把查询范围缩小到具体的业务批次。

  • 批量更新范围重叠:

    UPDATE orders SET status = 1 WHERE create_time BETWEEN '2025-01-01' AND '2025-01-31';

    两个事务如果都执行这个更新,即使更新的是不同 create_time 的记录,间隙锁也可能互相覆盖。解决办法是按更细的粒度分批,例如每次只处理 100 条主键范围内的记录,并且批与批之间错开主键区间。

改写 SQL 的原则是:尽量让锁的范围等于你真正要修改的数据范围,不要用宽泛条件把索引区间整个锁住。

5. 锁与索引的关系:没有索引行锁会退化为表锁

5.1 为什么无索引更新会锁全表

这是一个很经典的问题:InnoDB 行锁是靠索引记录实现的,那 WHERE 条件不带索引会怎样?答案是,扫描了多少条索引记录,就会锁多少条。全表扫描等于锁全表,而且因为扫描过程中会持续上锁,其他事务的插入也被间隙锁大面积覆盖。

举个实际数字:一张user_login_log表有 200 万行,某天运维同事执行:

DELETE FROM user_login_log WHERE create_time < '2024-06-01';

create_time没有索引,这条 SQL 会全表扫描,等于把所有记录的锁都过一遍。期间任何对这张表的 INSERT / UPDATE / DELETE 都会被阻塞,而且每条 DML 都要等前面的锁释放,越积越多,最后直接把数据库连接池打满。

要避免这种事故,第一是必须给create_time建索引,第二是如果删除量确实很大,用“分段删除”的方式:每次用主键范围取一批 id,比如WHERE id BETWEEN ? AND ?,循环删除,单批事务的锁范围就只限制在这批 id 里。这也是很多 DBA 处理归档数据时的标准操作。

5.2 唯一索引与主键索引的加锁差异

很多人以为 WHERE 条件用了唯一索引,加锁就和主键一样。其实有区别。

如果 WHERE 条件用唯一索引:

UPDATE users SET email = 'new@example.com' WHERE user_id = 100;

只要user_id是唯一索引,InnoDB 会先锁定user_id = 100这个二级索引记录,再去聚簇索引锁定主键id对应记录。两个锁都要持有,直到事务结束。

但如果 WHERE 条件用普通二级索引:

UPDATE users SET status = 1 WHERE age = 25;

age不是唯一索引,那么所有age = 25的二级索引记录都会被加锁,同时回表对应的聚簇索引记录也都加锁。锁的数量等于匹配行数。

这里有个容易被忽略的坑:如果age索引的区分度很低,比如表里一半人都 25 岁,那么锁的记录数量依然很大。所以在设计索引时,不能只看“有没有索引”,还要看索引列的区分度。区分度越低,一次扫描命中的记录越多,锁影响面越大。

5.3 如何利用索引下推和覆盖索引减少锁范围

想减少锁范围,一种很有效的思路是让 UPDATE 只回表修正真正要改的行,而不是让优化器大范围扫描。

举例来说,查询条件里有多个列,可以在联合索引中尽量包含 WHERE 列。假设业务经常按status和create_time更新:

UPDATE task SET exec_result = 'SUCCESS' WHERE status = 'RUNNING' AND create_time < '2025-06-01';

如果只在status上建索引,MySQL 会先找到所有RUNNING的记录,再逐行判断create_time,涉及大量锁。如果在(status, create_time)上建联合索引,优化器可以精确命中那一小段符合条件的索引区间,锁的数量会大幅减少。这就是“索引条件下推”(Index Condition Pushdown)带来的间接好处:让存储引擎在索引层面过滤掉更多行,而不是把每一条都回表加锁。

覆盖索引则更适合 SELECT 加锁场景。比如要锁定一批满足条件的订单号:

SELECT order_id FROM orders WHERE merchant_id = 1000 AND status = 'PAID' FOR UPDATE;

如果表有(merchant_id, status, order_id)联合索引,这个查询完全可以在索引中完成,不需要回表读整行数据。虽然FOR UPDATE还是会锁住命中的二级索引记录和聚簇索引记录,但回表扫描的开销显著降低。锁范围没有变小多少,但加锁的速度更快,事务持有锁的时间更短,从而减少锁冲突概率。

6. 常见问题速查与经验总结

6.1 锁等待超时与死锁的区别

锁等待超时和死锁经常被混为一谈,但它们机制完全不同。

  • 锁等待超时:一个事务在等待另一个事务释放锁,等到了innodb_lock_wait_timeout(默认 50 秒)仍然没等到,就会报错。此时另一个事务还在正常运行,并没有被回滚。
  • 死锁:两个以上事务互相持有对方需要的锁,形成环形等待。InnoDB 的死锁检测线程会主动介入,把其中一个事务回滚掉,让另一个事务继续执行。注意,死锁检测默认是开启的,所以死锁不需要等超时,通常几百毫秒内就会触发。

处理建议:锁等待超时先查data_lock_waits,找到阻塞源头,看看那个事务为什么长时间不提交。死锁处理则要抓住两点:一是应用层捕获ER_LOCK_DEADLOCK(错误码 1213),自动重启事务;二是从代码层面消除固定的加锁顺序冲突。

6.2 实际生产环境中的锁监控命令清单

给你一份我压箱底的排查命令组合,我每次遇到锁问题都会按顺序执行:

  1. 看整体事务情况:

    SELECT trx_id, trx_state, trx_started, trx_query, trx_rows_locked FROM information_schema.innodb_trx;
  2. 看锁等待明细:

    SELECT * FROM performance_schema.data_lock_waits;
  3. 看当前持有的锁对象:

    SELECT * FROM performance_schema.data_locks;
  4. 看最近死锁信息:

    SHOW ENGINE INNODB STATUS;
  5. 如果发现某个事务长期不提交,查看它的连接信息:

    SELECT id, user, host, db, command, time, state, info FROM information_schema.processlist WHERE id = 具体连接ID;

这套组合覆盖了从“谁在等谁”到“等了多久”到“锁住了哪些对象”的完整链路。注意版本问题:MySQL 5.7 之后推荐用performance_schema.data_locks,老的information_schema.innodb_locks在新版本里已经废弃。如果你还在用 5.6,那就只能靠SHOW ENGINE INNODB STATUS手工解析了。

6.3 几条独家避坑建议

最后分享几条我在各种环境里踩过坑后沉淀下来的经验。

第一条,别把隔离级别当摆设。如果业务数据对幻读不敏感,比如只有 insert 和 delete、很少做范围查询后再插入的场景,直接把隔离级别设置成READ COMMITTED,能减少大量间隙锁带来的并发阻塞。我见过某个订单系统只是改一个transaction_isolation参数,高峰期锁等待次数直接降了 70%。

第二条,大事务是锁问题的万恶之源。一个事务里如果塞了太多查询、远程调用、甚至耗时算法,都会让锁的持有时间无限拉长。锁并不可怕,可怕的是锁被长时间握住不放。所以写事务代码时要遵守“事务内不做无关操作”的原则,把批量更新拆分到独立小事务里执行。

第三条,监控要前置。不要等到死锁报错才去查。在数据库慢查询监控里,专门把 “Lock wait timeout” 和 “Deadlock found” 这两个错误码拉出来做告警,同时定期采集innodb_row_lock_current_waits和innodb_row_lock_time_avg这两个状态值。一旦平均值异常上升,即使没有报错,也能提前发现锁冲突恶化的趋势。

第四条,也是我最想强调的一点——别动不动就清缓存、杀连接、重启 MySQL。锁问题是事务逻辑和索引设计的问题,不是数据库“坏了”。重启只会让正在运行的事务全部回滚,业务侧可能会补偿数据,反而扩大混乱。正确姿势是冷静下来,用上面的命令把锁等待链查清楚,从源头解决。

说起来,我个人的习惯是在每个项目的数据库设计评审阶段,就把“这条业务 SQL 会加多少锁、加多久、会不会影响其他会话”当成必答题。一旦养成这种思维,很多并发问题在设计阶段就被掐灭了,根本轮不到上线后去死锁排查。这也是我和这篇文章最想传达给你的东西:锁机制并不神秘,它只是 InnoDB 为了保证数据一致性和并发效率,在索引之上画下的一条条边界。你理解了边界在哪里、为什么会存在,自然就知道怎么写 SQL 才能不越界、不踩坑。

相关新闻

SQL注入攻防实战:从攻击链拆解到防御体系搭建

SQL注入攻防实战:从攻击链拆解到防御体系搭建

我做过不少安全相关的项目&#xff0c;也带过团队里刚入门的安全新人&#xff0c;基本上每次聊到Web安全&#xff0c;第一个绕不开的话题就是SQL注入。这玩意诞生了二十多年&#xff0c;至今依然活跃在各种漏洞报告里&#xff0c;甚至很多所谓的新漏洞&#xff0c;追根溯源还是…

2026/10/20 5:55:20 阅读更多 →
LLM+Multi-Agents下的数据治理:元数据、质量、血缘与权限实战

LLM+Multi-Agents下的数据治理:元数据、质量、血缘与权限实战

1. 为什么数据分析平台里的“数据治理”会卡住整条链路1.1 从“有数可用”到“数据可信”的真实距离今年我一直在折腾一个内部的数据分析平台&#xff0c;框架上用了 LLM Data Multi-Agents&#xff0c;前端接 BI 看板&#xff0c;后端接数仓。刚开始跑演示时&#xff0c;所有…

2026/10/20 5:55:20 阅读更多 →
计算机体系结构习题答案的深度解析与工程化验证

计算机体系结构习题答案的深度解析与工程化验证

简介&#xff1a;本资源是《计算机体系结构》课程配套的课后习题部分参考答案&#xff0c;面向高校计算机专业本科生及考研复习者&#xff0c;聚焦系统结构、组成与实现三大核心层次的理解与解题训练。文档覆盖层次结构、虚拟机、翻译与解释、Amdahl定律、局部性原理、CPI计算、…

2026/10/20 5:55:20 阅读更多 →

最新新闻

SRC信息收集进阶:从被动收集到情报网络的实战方法论

SRC信息收集进阶:从被动收集到情报网络的实战方法论

我一直觉得&#xff0c;SRC 信息收集做到后面&#xff0c;拼的早就不是工具数量&#xff0c;而是你对“资产”和“情报”这两个词的理解深度。第一集我们聊了从零开始怎么把域名、IP、子域名这些基础资产捞出来&#xff0c;很多人照着做完一轮&#xff0c;发现东西是收集了一堆…

2026/10/20 6:29:34 阅读更多 →
用代码生成PPT:内容与样式分离的自动化实践

用代码生成PPT:内容与样式分离的自动化实践

1. 为什么我要自己动手做一个 PPT 生成工具做技术分享、写项目复盘、给团队做内部培训&#xff0c;绕不开的一件事就是做 PPT。我粗略算过&#xff0c;过去一年我至少做了四十多份演示文稿&#xff0c;平均每份按六到八小时算&#xff0c;光花在排版、对齐、调字体、找配图上的…

2026/10/20 6:29:34 阅读更多 →
基于python-pptx的PPT自动化生成方案:模板引擎与数据驱动实践

基于python-pptx的PPT自动化生成方案:模板引擎与数据驱动实践

1. 项目缘起与核心定位第一次看到“ppt-master”这个命名&#xff0c;我的直觉是&#xff1a;这大概率是一个围绕演示文稿自动化生成或批量处理的工具型项目。在办公自动化领域摸爬滚打这些年&#xff0c;我见过太多团队被PPT折磨得死去活来——每周例会要出汇报材料、每月要交…

2026/10/20 6:29:34 阅读更多 →
科学家会被Agent取代吗?技能库让AI做科研从Demo走向日常

科学家会被Agent取代吗?技能库让AI做科研从Demo走向日常

科学家会被Agent取代吗&#xff1f;技能库让AI做科研从Demo走向日常 【免费下载链接】scientific-agent-skills Turn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 250,000 scientists worldwide. 177 ready-to-use validated skills…

2026/10/20 6:29:34 阅读更多 →
STM32F103内部Flash读写:掉电不丢参数的嵌入式存储方案

STM32F103内部Flash读写:掉电不丢参数的嵌入式存储方案

简介&#xff1a;一套基于STM32F103芯片内部Flash读写的完整例程&#xff0c;适合嵌入式入门开发者及需要在项目中实现固件升级、参数存储、日志记录等功能的工程师。例程基于HAL库编写&#xff0c;完整涵盖了Flash解锁、扇区擦除、数据编程、读取校验、再次上锁的标准流程&…

2026/10/20 6:29:34 阅读更多 →
JMeter压测Kafka:手写Java脚本攻克SASL认证难题

JMeter压测Kafka:手写Java脚本攻克SASL认证难题

这周接了一个压测需求&#xff0c;要给某个内部Kafka主题灌一批业务数据&#xff0c;规模大概几十万条&#xff0c;消费端开了SASL认证&#xff0c;而且不是常见的PLAINTEXT全局认证&#xff0c;只是针对特定用户做了SASL_PLAINTEXT。我第一个反应是找现成的JMeter Kafka插件&a…

2026/10/20 6:28:34 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →