MySQL幻读背后的真相:MVCC与Next-Key Lock如何影响并发一致性

1. 一次真实的“诡异”复现:同一事务中两次查询结果不一样

“幻读”这个话题,老实说已经被聊烂了。但凡查过mysql面试题的人,多少都能背出几句“可重复读隔离级别下,InnoDB通过MVCC和间隙锁解决了幻读”。但真正在业务里碰到幻读,并且能一眼认出来的,还真不多。

前段时间一个朋友给我发来一段代码:一个批量统计成绩的存储过程,事务开启后先做了一次COUNT,中间处理了几行数据,再做一次COUNT,两次结果居然不一样。他第一反应是“有人并发改了数据”,排查了半天,发现所有写入操作都有自己的业务状态机,不可能出现这种“凭空多了一条记录”的事情。最后定位到问题根源,就是幻读——而且是被很多人误以为“可重复读级别下不存在”的那种幻读。

所以这篇文章不打算背八股,我把复现过程、底层原理、锁机制、隔离级别关系、还有生产环境里那些容易踩的变体,一条一条拆开讲。

1.1 先搭一个能复现问题的最小环境

为了把问题讲清楚,我建了一张学生课程成绩表。这类表在业务里太常见了,学生、课程、成绩三个要素,拿来做示例再合适不过。

sql复制CREATE TABLE `course_score` (
  `id` int NOT NULL AUTO_INCREMENT,
  `student_no` varchar(32) NOT NULL COMMENT '学号',
  `course_no` varchar(32) NOT NULL COMMENT '课程号',
  `score` int DEFAULT NULL COMMENT '成绩',
  PRIMARY KEY (`id`),
  KEY `idx_student_course` (`student_no`, `course_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生课程成绩表';

插入几条初始数据:

sql复制INSERT INTO course_score (student_no, course_no, score) VALUES
('S001', 'C001', 92),
('S001', 'C002', 85),
('S002', 'C001', 78);

现在模拟两个并发事务。终端A开启事务,查询学号S001的成绩;终端B插入一条S001的新课程成绩并提交;终端A再次查询同一个条件。

终端A:

sql复制SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT * FROM course_score WHERE student_no = 'S001';

此时能看到两行:C001和C002。

终端B:

sql复制START TRANSACTION;
INSERT INTO course_score (student_no, course_no, score) VALUES ('S001', 'C003', 88);
COMMIT;

终端A再次执行相同的查询:

sql复制SELECT * FROM course_score WHERE student_no = 'S001';

结果还是两行。很多人到这里会得出结论:“RR级别下并没有幻读啊,新插入的行根本看不见。”

这个结论只对了一半。

关键在下一条SQL。如果终端A执行的是当前读,比如 SELECT ... FOR UPDATE,结果就不一样了。

1.2 同一个事务,快照读和当前读看到的东西不一样

终端A继续在同一个事务里执行:

sql复制SELECT * FROM course_score WHERE student_no = 'S001' FOR UPDATE;

返回三行。C003这条新插入的记录,此时出现在终端A面前了。

同一事务内,仅仅因为读的方式不同,查询结果就从两行变成了三行。这就是幻读最直观的面孔——你的事务快照里看不到那行,但当前读却能把最新已提交的数据捞出来。

这个现象让很多人在刚接触时非常困惑:MySQL明明说自己支持可重复读,怎么同一个事务里两次查询结果不一致?原因在于“普通快照读”和“当前读”走的是两套完全不同的逻辑,而这个差异恰恰是幻读问题的核心。

我整理了一个对比表,方便你对照看:

操作顺序 终端A的观察结果 背后的机制
第1步:终端A开启事务并快照读 看到2行 生成ReadView,后续快照读复用
第2步:终端B插入C003并提交 终端B正常提交,不受阻塞 终端A没有持有任何锁
第3步:终端A再次快照读 仍然看到2行 快照读复用同一份ReadView
第4步:终端A改用FOR UPDATE当前读 看到3行 当前读取最新已提交版本数据

需要强调的是,如果终端A在第1步就执行 SELECT ... FOR UPDATE,情况完全不同。此时终端A会持有临键锁,终端B的插入会进入锁等待状态,直到终端A提交事务后才会执行。这就是Next-Key Lock“防幻读”的真实工作方式。

但实际开发中,很多人不会一上来就加FOR UPDATE,而是先做普通查询判断,再决定下一步操作,这就为后面那种“先快照读、后当前读”的语义裂缝埋下了伏笔。

1.3 复现过程中的两个细节

我在这里想额外提两个实际操作时容易遇到的问题。

第一,MySQL 8.0默认隔离级别是可重复读,但如果你用的是5.7或更早版本,请先确认一下会话隔离级别,别让配置差异干扰复现结果:

sql复制SELECT @@transaction_isolation;

第二,我上面的示例里给 student_no 建了二级索引 idx_student_course。这个索引不是随便建的。如果这张表完全没有索引,FOR UPDATE 的锁范围会直接退化为全表所有间隙,复现出来的结果会更“暴力”,但同时也更容易掩盖真正的机制。后面第七章我会专门讲这个坑。

需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。

2. 幻读到底是什么:和不可重复读的边界被太多人搞混了

在聊机制之前,先把这个概念彻底掰扯清楚。因为我在排查问题、看别人写的技术总结时,发现有不少人把“不可重复读”和“幻读”混为一谈。面试的时候一紧张,脱口而出“幻读就是两次读不一样”,这句话严格来说不算错,但完全不够精确,也掩盖了幻读真正的危害。

2.1 两个常见错误认识

第一个错误认识:凡是同一事务两次查询结果不一样,都叫幻读。不对。不可重复读针对的是“已存在的行被修改”,比如一行数据从 score=92 变成了 score=95,你两次查询看到同一行的值不一样,这是不可重复读。幻读针对的是“凭空多出原本不存在的行”,重点是INSERT操作带来的新记录。

第二个错误认识:幻读只会在“读已提交”及以下隔离级别出现。MySQL的机制比这个复杂。可重复读级别下,快照读确实不会看到新插入的行,但当前读可以看到。也就是说,幻读的“出现与否”和读的方式强相关,不是单靠一个隔离级别就能一刀切判断的。

2.2 一个生活化的类比:会议室名单

我通常用这个例子解释给团队里的新人。

你在会议室里统计到场人员。第一次数了一圈,5个人。有人中途离开,有人换座位,第二次再数,还是5个人,但其中两张面孔换掉了。这是不可重复读,问题出在“已有的人的状态变了”。

如果会议室本来只有5个人,第二次数的时候却多出第6个人,而且这个人之前完全不在你的名单上,这才是幻读。问题出在“名单以外的实体冒出来了”。

这个类比能直接点出两类问题的本质区别:一个是UPDATE/DELETE对现有记录造成的可见性变化,一个是INSERT对记录集合造成的增量变化。只有INSERT才能制造真正的“幻影”。

2.3 幻读为什么在统计场景里特别危险

幻读影响最大的场景是聚合查询。COUNT、SUM、AVG这类操作,本质是对一个记录集合做整体计算,多一行少一行,结果完全不一样。

典型的业务场景是“先查后写”:比如先查询某个学号是否已经有成绩记录,没有就插入一条新成绩。两个并发事务同时发现“没有记录”,然后各自插入,最终出现两条相同学号+课程号的记录。你需要用唯一索引兜底,或者用分布式锁把判断和插入绑成原子操作。

这里的根子就在幻读:你以为自己面对的集合是固定的,实际上集合边界可以被其他事务插入的新行改变。如果没有锁机制的约束,任何基于“先查询再判断”的并发逻辑都可能有漏洞。

2.4 SQL标准与InnoDB实现之间的差异

还有一个必须澄清的点。SQL标准里对隔离级别的定义是:可重复读级别下不能出现幻读。但InnoDB对这个问题的处理是分了两条路:

  • 普通快照读:通过MVCC和ReadView保证事务内的读一致性;
  • 当前读(FOR UPDATE、UPDATE、DELETE):通过临键锁(Next-Key Lock)阻塞范围内的插入。

这两条路合在一起,才算是InnoDB在可重复读级别下“基本消除幻读”的完整方案。为什么说“基本消除”,因为两条路之间存在一个微妙的时间窗口:如果你先用快照读判断,再在当前读时发现多了一行,那么严格从“同一事务读一致性”的角度看,你已经遭遇了幻读的影响。

这个差异在实际工程里不是概念问题,而是真实的线上事故。所以我在讲幻读时,从来不会简单地说“RR解决了幻读”,我会补上一句:“MVCC解决快照读场景,临键锁解决当前读场景,两套机制配合,但存在边界。”

3. 为什么会出现幻读:快照读与当前读的底层较量

要真正搞懂幻读,绕不开MVCC和ReadView。很多人知道MVCC,但只知其名不知其运行逻辑,导致遇到问题时无法把现象和机制对上号。这一节我把底层拆开讲。

3.1 ReadView是如何生成一张“时间快照”的

MVCC是“多版本并发控制”的缩写。在InnoDB里,一行记录可以同时存在多个历史版本,每个版本都记录了自己对应的事务ID。当一行数据被修改时,旧版本不会立即删除,而是留在undo log里,供有需要的读事务访问。

ReadView是某个事务发起快照读时生成的一份“视图”,它记录了发起时刻数据库的活跃事务状态。在可重复读隔离级别下,ReadView只生成一次,之后整个事务内的普通读都复用这同一份视图。

ReadView里有几个核心字段:

  • trx_ids:生成视图时处于活跃状态且未提交的事务ID列表;
  • low_limit_id:当前系统中已经分配过的事务ID上限;
  • up_limit_id:活跃事务列表中最小的ID;
  • creator_trx_id:创建这个ReadView的事务自己的ID。

判断一条记录版本是否可见是有一套规则的,简化后大致如下:

  • 如果版本的事务ID等于当前事务ID,说明是自己改的,可见;
  • 如果版本的事务ID小于活跃事务列表中的最小ID,说明该版本在ReadView生成时已经提交,可见;
  • 如果版本的事务ID大于等于事务ID上限,说明该版本在ReadView生成时还未开始,不可见;
  • 如果版本的事务ID落在活跃列表区间内,需要判断是否仍在未提交群体里,是则不可见,否则可见。

正是因为普通SELECT在可重复读级别下复用了第一份ReadView,所以终端B提交的C003记录,对终端A的快照读来说永远是“未来版本”,始终不可见。这是快照读不产生幻读的根本机制。

3.2 FOR UPDATE和UPDATE为什么叫“当前读”

“当前读”这个叫法很形象:读取记录的最新版本,并且对读取的记录加锁。SELECT ... FOR UPDATE、SELECT ... LOCK IN SHARE MODE、UPDATE、DELETE,都属于当前读。

当前读不借助ReadView判断可见性,它直接读取数据库里最新已提交的数据。所以它能看到终端B新插入并提交的那一行。这就是我在第一章里,同一个事务内FOR UPDATE返回三行而普通SELECT返回两行的原因。

有人可能会问:那如果事务A用FOR UPDATE先锁住了范围,事务B还能插入吗?不能。因为事务A的临键锁会把该范围内的所有间隙锁住,事务B的INSERT必须获取插入意向锁才能执行,插入意向锁和间隙锁互斥,于是事务B阻塞,直到事务A提交释放锁。这个机制就是对“当前读场景防幻读”的保障。

3.3 最容易踩的坑:先快照读后UPDATE

现在来复现那个“朋友遇到的诡异场景”。

终端A:

sql复制START TRANSACTION;
SELECT COUNT(*) FROM course_score WHERE student_no = 'S001';
-- 返回 2

终端B:

sql复制INSERT INTO course_score (student_no, course_no, score) VALUES ('S001', 'C003', 88);
COMMIT;

终端A继续:

sql复制UPDATE course_score SET score = score + 1 WHERE student_no = 'S001';
COMMIT;

SELECT COUNT(*) FROM course_score WHERE student_no = 'S001';
-- 返回 3

最终结果:终端A第一次COUNT是2,第二次COUNT是3,而且C003那条记录的score被人为加到了89。注意,这个UPDATE是在终端A事务内部执行的,现实业务里完全可能藏在某个批量处理逻辑中。

为什么会这样?因为UPDATE是当前读,它读取最新已提交版本,所以把C003这条新记录也包含进来了。更新之后,终端A再做普通快照读时——虽然ReadView是复用的,但这条记录在undo log里的旧版本可能已经被清理或者链路变化,读到的结果就和最初的快照不一致了,于是“可重复读”的语义出现了裂缝。

这个场景下的临键锁为什么没拦住?因为终端A一开始只做快照读,并没有持锁;终端B插入并提交后,锁资源已经释放。等终端A再执行UPDATE时,C003已经存在于表里,当前读看到的范围自然包含了它。

这个坑在“先判断后写入”的业务里尤为常见,特别是统计任务、批量清洗、定时补偿这类场景。我的建议是:如果事务内既要读又要写,尽量让所有读都走当前读,或者加锁,保持读方式的一致性,不要混用快照读和当前读。

提示:判断完业务条件后如果打算执行写入,直接使用SELECT ... FOR UPDATE锁定范围内数据,是让“判断+写入”原子化的一种方式,但要注意锁范围和性能。

4. InnoDB用Next-Key Lock交出的答卷,以及它的边界

前面提到锁,这一章就专门把InnoDB的锁体系讲透。很多人觉得Next-Key Lock是个神秘的东西,其实拆开看就是组合拳。

4.1 为什么只有行锁治不了幻读

行锁(Record Lock)只能锁定已经存在的某一行索引记录。它管得住已经在那里的老行,但管不住别人往这个范围内插入新行。你可以理解为:行锁是给已有的椅子上了锁,但挡不住有人再往房间里搬一把新椅子。

要解决幻读,必须把“不存在记录的间隙”也纳入锁定范围,让别的事务无法往这个间隙里插入数据。于是有了间隙锁Gap Lock的用武之地。

4.2 Gap Lock、Next-Key Lock与前开后闭区间

InnoDB的锁有三种主要形态:

  • Record Lock(记录锁):锁定一条索引记录本身;
  • Gap Lock(间隙锁):锁定两条记录之间的空隙,允许已有记录被读,但阻止其他事务在间隙中插入;
  • Next-Key Lock(临键锁):记录锁和间隙锁的组合,锁定一个左开右闭的区间。

以主键id为例,假设表里有id=1、2、3三条记录。临键锁锁定的区间大致是:(-∞,1]、(1,2]、(2,3]、(3,+∞)。注意,Next-Key Lock是“前开后闭”,也就是包含右端点。

如果执行:

sql复制SELECT * FROM course_score WHERE id > 1 FOR UPDATE;

锁定的范围会覆盖(1,2]、(2,3]、(3,+∞)。此时另一个事务尝试插入id=4的行,会被阻塞,因为id=4落在(3,+∞)这个间隙区间里。

如果执行等值查询并且命中唯一索引记录,比如:

sql复制SELECT * FROM course_score WHERE id = 2 FOR UPDATE;

此时只锁定id=2这一条记录,不会对其他间隙加锁。这是因为唯一索引等值查询时,优化器能确定“值如果不存在,才会产生幻读”,既然记录已经存在,就不需要锁间隙。但如果是等值查询未命中,比如 WHERE id = 4,则会锁住3和5(如果下一个值是5)之间不存在的间隙,防止其他事务插入id=4。

非唯一索引的等值查询,即使命中了记录,也会锁范围内的间隙,因为同一个索引值可能对应多条记录,还可能未来插入新的相同键值的记录。这一点在开发里很容易忽略。

4.3 如何亲眼看到这些锁

只讲理论不管用,实际排查问题时你需要在数据库里看到锁。这里分享几个常用命令。

查看当前事务:

sql复制SELECT trx_id, trx_state, trx_started, trx_mysql_thread_id
FROM information_schema.innodb_trx;

查看锁等待明细(MySQL 8.0使用performance_schema):

sql复制SELECT * FROM performance_schema.data_locks\G;

在5.7及更早版本,用的是 information_schema.innodb_lock_waits 和 INNODB_LOCKS,字段名略有不同。

复现锁等待时,事务1执行:

sql复制SELECT * FROM course_score WHERE id > 1 FOR UPDATE;

事务2执行:

sql复制INSERT INTO course_score (student_no, course_no, score) VALUES ('S003', 'C001', 90);

此时事务2会一直卡住,进入锁等待状态。查询 innodb_trx,能看到一个事务状态是RUNNING,另一个是LOCK WAIT。再查 data_locks,能看到事务1持有范围锁,锁类型为X(排他锁),锁模式里包含GAP字样:X,GAP;事务2等待的锁模式是插入意向锁:INSERT_INTENTION。

这种直观的观察方式,能帮你在分析线上死锁或锁等待时迅速定位到是哪条SQL惹的祸。

4.4 RR下用了临键锁,为什么还会有“奇怪”现象

有一个重要事实:临键锁只有在查询条件能走索引时,才会精确锁定对应区间。如果查询条件没有索引,或者优化器选择全表扫描,InnoDB为了在可重复读级别下保证一致性,会给所有扫描过的记录和所有间隙都加上临键锁,实际效果等同于锁表。

所以你会看到一些线上事故:一条 UPDATE 语句因为没走索引,把整张表的所有写入请求全部阻塞。问题不在“间隙锁”,而在“索引设计”。这也是我反复强调UPDATE和DELETE必须使用主键或者索引条件的原因。

另一个现象是“死锁”。两个事务各自持有不同区间的间隙锁,然后因为插入请求互相等待,最终触发InnoDB死锁检测机制,其中一个事务被回滚。下一章我会结合生产场景详细分析。

5. 从隔离级别看幻读:RR为什么能成为MySQL默认选择

把视线拉高一点,从隔离级别演化来看幻读,很多为什么就顺理成章了。

5.1 四档隔离级别逐步消除三类问题

SQL标准定义了四个隔离级别,分别解决脏读、不可重复读、幻读三类问题。用表格总览一下:

隔离级别 脏读 不可重复读 幻读(纯SQL标准角度)
READ UNCOMMITTED 可能 可能 可能
READ COMMITTED 已消除 可能 可能
REPEATABLE READ 已消除 已消除 理论上应消除
SERIALIZABLE 已消除 已消除 已消除

在InnoDB里,读已提交隔离级别下,每次普通SELECT都会生成一个新的ReadView,所以两次读到的数据可能不同,不可重复读发生。可重复读级别下,第一个ReadView被整个事务复用,普通SELECT不会看到其他事务提交的新行,这也是很多人把“可重复读”和“不会幻读”画等号的原因。

SERIALIZABLE级别最彻底,它会把普通SELECT也自动转成加锁读,整个事务基本串行执行,幻读彻底没有,但并发能力也大打折扣。

5.2 为什么MySQL默认RR而不是RC

这是一个面试高频追问,也是理解MySQL设计哲学的好入口。

MySQL在主从复制场景下,如果binlog格式是statement,副本库回放的是逻辑SQL。在“读已提交”级别下,主库上的事务提交顺序可能导致同一批SQL在从库上执行的结果和主库不一致。可重复读级别下,事务在整个执行期间使用一致性的快照,配合事务提交顺序,能最大程度保证基于statement的binlog回放结果与主库一致。

后来有了 binlog_format=ROW,从库回放的是行级别的变更,不再依赖隔离级别来保证一致性。理论上生产环境完全可以把隔离级别降到读已提交来提升并发,很多大厂也是这么做的。但MySQL的默认值至今仍是可重复读,一方面是这个历史惯性,另一方面是可重复读对业务开发者来说,读一致性体验更好,踩坑更少。

5.3 业务真的不能容忍幻读,怎么办

如果业务语义上绝对不能出现幻读,比如账户余额统计、兑换码核销、库存扣减,那就要在架构层面做选择。

常见的方案有这几个:

  1. 将事务隔离级别设为SERIALIZABLE:数据库层面彻底串行化,安全性最高,但并发暴跌,一般不推荐全量使用;
  2. 事务内使用 SELECT ... FOR UPDATE 或 LOCK IN SHARE MODE:把快照读变成当前读,配合临键锁防止范围内插入;
  3. 应用层加分布式锁:以业务主键或范围标识为锁粒度,把“查询+判断+写入”整体锁住;
  4. 数据库唯一索引兜底:即使并发插入,唯一索引会挡住重复数据,让违反约束的事务直接报错。

从成本角度考虑,纯数据库方案最简单,但性能损耗大;应用层锁灵活度高,但需要额外维护中间件。最好是根据业务场景组合使用。我的经验是:能让数据库唯一索引解决的,绝不在应用层写复杂锁逻辑。

6. 面试场上“mysql幻读”怎么答才算加分

说实话,“幻读”在面试里出现的频率非常高,但大多数人的答案都停留在“可重复读解决了幻读”这种一句话水平。面试官追问两句就露馅。这里我给出一套可以实际使用的话术框架。

6.1 一套可以背,但更要能讲清的答题框架

面试官问“聊聊MySQL幻读”,我建议按四层递进回答:

第一层给定义:幻读指同一个事务内执行同一查询,第二次返回的行集中出现了第一次不存在的行,主要是其他事务插入新记录导致的。

第二层讲机制:InnoDB在可重复读级别下,用MVCC的ReadView机制保证普通快照读不会看到其他事务提交的新行;用Next-Key Lock保证当前读的锁范围内,其他事务无法插入新记录。

第三层讲边界:如果事务先执行快照读,再执行UPDATE这类当前读,当前读会看到最新已提交的数据,可能出现“同一个事务里先看到2行,更新后看到3行”的现象。临键锁只在当前读执行期间生效,防不住“先快照读、后当前读”这种组合。

第四层讲工程:完全避免幻读,可以使用SERIALIZABLE、应用层锁、唯一索引兜底。但要注意RR模式下没有索引条件的UPDATE可能锁全表,生产环境要特别谨慎。

这套回答覆盖了概念、原理、边界、工程实践四个维度,既有理论也有实操,面试官基本能判断你是真懂还是背题。

6.2 高频追问:MVCC能替代锁吗

这个追问几乎必出现。答案是:不能完全替代。

MVCC解决的是“读与读之间不互斥”的问题,让快照读无需等待锁就能读到一致性快照;锁解决的是“写与写之间要互斥”的问题,以及“当前读与写之间的一致性”问题。快照读虽然不加锁,但它阻止不了别人写入;反之,当前读必须加锁,才能保证读到的最新数据和之后要做的修改基于同一份数据。

两者是互补关系。把MVCC当成万能药,碰到“先查后写”不加锁,就可能在并发场景下出问题。

6.3 另一个高频追问:RC级别下能避免幻读吗

读已提交级别下,每次普通SELECT都生成新的ReadView,所以其他事务提交的新行,在当前事务的下一次快照读中就可能看到,无法避免幻读。RC下InnoDB只在少数场景保留间隙锁(比如外键检查和重复键检查),因此当前读场景同样无法通过临键锁阻止插入。

如果面试官问到这里,还可以补充:这也是为什么很多团队在改为RC隔离级别时,会同步把binlog格式设为ROW,并且用业务手段保证并发一致性。这句话能体现你对生产环境的理解。

7. 生产环境中的幻读变体:无索引更新、OR条件与锁表

最后一部分讲生产。纸上谈兵的锁机制都很漂亮,但线上真正的坑通常藏在索引失效、条件复杂、长事务这些细节里。

7.1 无索引条件:从精确锁退化为大范围Lock

假设 course_score 表的 student_no 没有索引,执行:

sql复制UPDATE course_score SET score = score + 1 WHERE student_no = 'S001';

由于 student_no 上没有索引,数据库要全表扫描才能定位符合条件的行。为了保证可重复读级别下当前读的一致性,InnoDB会对扫描过程中经过的所有记录和所有间隙加临键锁。结果就是:整个表的写入操作全部被阻塞,包括插入一个完全不同学生的成绩记录。这种问题比显式锁表更隐蔽,因为表没有被LOCK TABLES,但从业务角度看,写入已经完全瘫痪。

排查方法:先看 SHOW PROCESSLIST,确认是否有事务长时间处于等待锁状态;再查 information_schema.innodb_trx,找到 trx_state = 'LOCK WAIT' 的事务;结合 performance_schema.data_locks 定位持有锁的源头SQL。

线上处理时,我一般建议先把肇事SQL找出并终止,再评估是否用主键条件分批更新。

7.2 OR条件走了部分索引,为什么锁范围会爆炸

另一个高频场景是查询条件里出现OR。看看这条SQL:

sql复制UPDATE course_score SET score = score + 1
WHERE student_no = 'S001' OR course_no = 'C002';

即使 student_no 和 course_no 都有单独的索引,OR的存在也可能导致优化器无法同时走两个索引。优化器可能选择其中一个索引回表,再过滤另一个条件;极端情况下干脆全表扫描。无论哪种执行计划,锁的范围都会明显大于实际需要更新的行。

更稳妥的做法是把OR拆开,先定位主键,再按主键更新:

sql复制SELECT id FROM course_score WHERE student_no = 'S001'
UNION ALL
SELECT id FROM course_score WHERE course_no = 'C002';

拿到id集合后,用IN或者临时表去执行UPDATE。这样锁范围可以精确到具体主键,不会有额外的间隙锁膨胀。

7.3 间隙锁加插入意向锁引发死锁:一个实际分析

死锁是当前读场景下最容易碰到的问题之一。经典的死锁场景是这样的:

事务A执行:

sql复制SELECT * FROM course_score WHERE id > 10 FOR UPDATE;

锁住了(10,20]、(20,30]等范围的临键锁。

事务B尝试插入一条id=18的记录:

sql复制INSERT INTO course_score (id, student_no, course_no, score) VALUES (18, 'S010', 'C005', 99);

事务B需要获取id=18所在间隙的插入意向锁,但该间隙已被事务A的间隙锁占据,于是事务B进入锁等待。

此时如果事务A又去更新事务B锁住的某一行(比如事务B在此之前已经更新过id=15的记录并持有行锁),就会形成循环等待,触发InnoDB死锁检测。

SHOW ENGINE INNODB STATUS 的 LATEST DETECTED DEADLOCK 段落会记录这两个事务的SQL、持有的锁和等待的锁,显示核心信息类似于两个事务分别在等对方释放间隙锁和记录锁。遇到死锁时不要慌,看这个段落就够了。

7.4 工程建议

把生产环境的经验沉淀成几条原则:

  • UPDATE和DELETE的WHERE条件必须走索引,最好用主键或者唯一索引;
  • 大范围更新要分批LIMIT处理,降低单个事务的锁持有时间;
  • 长事务要监控,超过阈值自动告警,长事务是锁等待的主要来源;
  • 如果业务对可重复读要求不高,可以考虑降到读已提交,并且开启 binlog_format=ROW,可以减少间隙锁带来的锁冲突;
  • 并发插入的防重,优先用唯一索引兜底,不要在应用层自己写先查后插。

我见过太多线上事故,不是败给高深的分布式理论,而是败在一条没走索引的UPDATE和可重复读默认级别上。幻读带来的问题,本质上都是“读旧版本”和“读新版本”的边界问题。只要记住:先查后写务必加锁,统计结果不要和当前读混着算,大部分幻读坑都能绕开。

这个老话题讲完了,但在实际项目里它永远不嫌旧。希望这篇内容能帮你在面试里讲清楚,更能在生产环境里少踩几个坑。

内容推荐

LeetCode 1022:二叉树二进制路径和的DFS解法
二叉树 · DFS · 位运算
深度优先搜索(DFS)是解决二叉树遍历问题的经典算法,特别适用于需要探索所有可能路径的场景。在二进制数处理中,位运算提供了一种高效的计算方式,通过左移和或运算可以动态构建路径值。这类技术在文件系统权限管理、网络路由算法等实际工程中有广泛应用。以LeetCode 1022为例,题目要求计算从根节点到叶节点的所有二进制路径和,这既考察了数据结构基础,也体现了算法优化思想。递归和迭代两种实现方式各有优势,递归代码简洁但可能有栈溢出风险,迭代解法更稳定但实现稍复杂。理解这类问题的解法有助于提升在技术面试中的表现,也能为处理树形结构数据提供通用思路。
从单体到微服务:架构演进实战与避坑指南
架构演进 · 微服务 · 单体架构
架构演进是系统持续优化的关键过程,其核心在于通过合理的组织方式调整应对业务增长。从技术原理看,架构演进涉及服务拆分、分布式事务处理、性能优化等基础概念,通过垂直拆分解决单体架构瓶颈,引入服务化框架实现模块解耦。在工程实践中,Prometheus监控、Kubernetes容器化等技术工具能有效提升系统可观测性和伸缩性。特别是在电商等高并发场景下,合理的架构演进可以显著提升系统可用性,如某案例通过ES索引优化使查询性能提升3倍。本文基于真实项目经验,详解从单体到云原生的演进路径,分享熔断机制、灰度发布等实战技巧,帮助开发者规避过度设计等常见陷阱。
M142视频点播系统架构设计与性能优化实践
视频点播系统 · 微服务架构 · FFmpeg转码
视频点播系统是现代多媒体应用的核心组件,其技术实现涉及流媒体处理、分布式存储和内容分发网络(CDN)等关键技术。系统通常采用分层架构设计,通过微服务化实现功能解耦,结合关系型数据库和NoSQL的混合存储方案满足不同数据类型的处理需求。在工程实践中,视频转码和自适应码率技术确保不同网络环境下的流畅播放体验,其中FFmpeg作为开源转码工具被广泛应用。性能优化方面,首屏加载时间、CDN缓存策略和并发处理能力是关键指标,合理的监控体系能有效保障系统稳定性。M142系统通过HLS协议和DRM加密实现版权保护,同时采用Prometheus+ELK技术栈构建完整的运维监控方案。
Unity集成讯飞虚拟人SDK开发实战指南
Unity3D · 讯飞虚拟人 · SDK集成
虚拟数字人技术正成为人机交互的重要发展方向,其核心技术包括语音识别、动画驱动和实时渲染。Unity作为领先的实时3D开发平台,结合讯飞先进的语音AI能力,为开发者提供了构建智能虚拟人的完整解决方案。通过SDK集成,开发者可以实现自然的口型同步、表情控制和语音交互,应用于虚拟主播、智能客服等场景。本文详细介绍了从环境配置到核心功能实现的完整流程,包括Android/iOS平台适配、性能优化技巧等实战经验,帮助开发者快速掌握Unity与讯飞虚拟人SDK的深度集成方法。
空间碎片动态聚类算法在航天监测中的应用与优化
空间碎片监测 · 动态聚类算法 · 航天安全
空间碎片监测是航天安全领域的关键技术,通过动态聚类算法实现对轨道物体的高效分类与追踪。该技术基于多源数据融合与轨道动力学原理,结合机器学习方法提升碎片关联准确率。在工程实践中,算法优化涉及实时计算、GPU加速和自适应参数调整,显著提高碰撞预警的时效性和准确性。典型应用场景包括卫星解体事件响应和星座部署监测,其中引入可见性时间窗约束和特征权重动态调整解决了传统方法在高速目标群处理中的局限性。随着近地轨道日益拥挤,这类融合了空间环境感知与智能分析的技术,正成为保障航天器安全运行的重要工具。
TypeScript设计哲学与工程实践:渐进式类型系统解析
TypeScript · 渐进式类型系统 · 前端开发
渐进式类型系统是现代编程语言设计的重要范式,通过在运行时保留动态语言灵活性的同时,编译时引入静态类型检查,显著提升了代码可维护性。TypeScript作为JavaScript的超集,其核心价值在于平衡开发效率与类型安全,通过工具链深度整合实现即时类型反馈。在大型前端项目中,类型即文档的特性可降低60%的API查阅耗时,而条件类型等高级特性支持复杂的类型编程。这些设计使TypeScript成为2023年State of JS调查中最受欢迎的前端技术之一,特别适用于需要长期维护的金融、电商等业务系统。本文通过解析TypeScript之父Anders Hejlsberg的设计理念,揭示如何通过渐进式类型迁移策略解决实际工程问题。
深入理解pytest框架在接口测试中的应用与实践
pytest · 接口测试 · 测试框架
pytest作为Python生态中最流行的测试框架之一,其简洁的语法和强大的扩展性使其成为接口测试的首选工具。测试框架的核心价值在于提高测试代码的可维护性和执行效率,pytest通过fixture机制实现测试环境的准备与清理,配合参数化测试可以轻松覆盖多种测试场景。在工程实践中,pytest的插件系统(pytest-html、pytest-xdist)和标记系统大幅提升了测试报告生成和用例筛选的效率。对于RESTful API测试,pytest能够完美支持从请求发送到响应验证的全流程,结合工厂模式可以高效管理测试数据。这些特性使pytest成为现代持续集成流程中不可或缺的测试解决方案。
射频测试仪器选型指南:功率计、频谱仪与VNA对比
射频测试 · 功率计 · 频谱分析仪
射频测试是无线通信系统开发的关键环节,涉及功率、频谱、网络参数等核心指标的精确测量。功率计通过热电偶或二极管检波原理实现射频能量的精准测量,适用于发射机校准等场景;频谱分析仪基于超外差架构,可详细分析信号的频域特性,是EMI测试和频谱监测的重要工具;矢量网络分析仪(VNA)则通过S参数测量,为天线设计和滤波器调试提供网络特性分析。在5G和毫米波技术快速发展的背景下,合理选择测试仪器对确保测量精度至关重要。本文深入解析功率计、频谱仪和VNA的技术原理、选型参数及典型应用,帮助工程师根据项目需求和预算制定最优测试方案。
智慧法务系统:企业合规管理的数字化转型
智慧法务 · 合规管理 · 法律知识图谱
合规管理是企业运营中的关键环节,尤其在全球化背景下,企业面临日益复杂的法律环境。传统合规模式存在响应滞后、成本高企和协同困难等痛点。智慧法务系统通过法律知识图谱、智能合约审查引擎和实时合规监测等核心技术,将合规管理从事后灭火转变为事前预防。这些技术不仅提升了合规效率,还通过风险量化评分等功能,帮助企业直观评估风险等级。智慧法务系统在跨境业务合规、合同管理和员工行为监控等场景中展现出显著价值,如缩短合同审查周期、降低纠纷率等。实施智慧法务系统需要分阶段部署、强化数据治理和变革管理,避免技术选型陷阱,确保系统与业务流程深度集成。
机器人工程师如何从几何视角掌握线性代数?
线性代数 · 机器人技术 · SLAM
线性代数是描述空间变换的数学语言,其核心概念如向量、矩阵运算与三维空间的几何变换存在深刻对应关系。在机器人技术领域,从SLAM到运动控制都依赖矩阵运算的空间解释能力——旋转矩阵描述刚体姿态,特征向量对应主运动方向,SVD分解解决点云配准问题。理解矩阵乘法与坐标变换的映射关系,能显著提升算法调试效率。本文通过激光雷达里程计、点云配准等典型场景,详解如何建立矩阵运算的几何直觉,并分享避免万向节锁、矩阵维度错误等工程实践技巧。
Linux开发环境构建与核心命令实战指南
Linux开发环境 · Linux命令 · 开发工具链
Linux作为开源操作系统的代表,其核心价值在于提供完整的开发工具链和透明的系统机制。从编译器(gcc)、调试器(gdb)到容器化技术(Docker),Linux环境为开发者提供了原生的技术支持。理解Linux工作原理不仅能提升开发效率,更是掌握服务器运维、嵌入式开发等领域的必备技能。通过配置合理的分区方案、安装开发工具链、优化Shell环境等实践,可以构建高效的Linux开发环境。掌握文件操作、进程管理等核心命令,以及网络调试、性能分析等进阶技巧,能够有效解决实际开发中的各类问题。
论文排版神器Paperxie:告别格式困扰
论文排版 · Paperxie · 高校模板
论文排版是学术写作中不可或缺的环节,涉及字体、行距、页眉页脚等格式规范。传统手动排版效率低下且容易出错,而智能排版工具通过模板库和AI校对技术,能自动适配不同高校的格式要求。Paperxie作为一款专业排版工具,内置4000+高校模板,支持智能格式检查和实时纠错,显著提升排版效率。其应用场景包括课程论文、毕业论文等学术写作,尤其适合需要频繁调整格式的用户。通过动态适应不同章节要求和智能处理跨页表格等高级功能,Paperxie帮助学生和研究者将精力集中在内容创作而非格式调整上。
YOLOv8轻量化改造:GFNet在Neck网络的应用与优化
YOLOv8 · GFNet · 目标检测
目标检测中的特征融合网络(Neck)对模型性能至关重要,传统方法往往面临计算复杂度高与移动端部署难的矛盾。GFNet通过频域变换实现全局信息建模,其O(NlogN)的计算复杂度显著优于自注意力机制,为轻量化设计提供了新思路。结合深度可分离卷积和通道剪枝技术,该方案在YOLOv8的FPN结构中实现了精度与速度的平衡。实验表明,经过ONNX量化与RKNN工具链优化后,模型在RK3588等边缘设备上可实现23ms的实时推理,为移动端目标检测提供了可行的工程实践方案。
2025年漏洞挖掘实战指南:从入门到精通
漏洞挖掘 · OWASP Top 10 · SQL注入
漏洞挖掘作为网络安全领域的核心技术,通过识别系统弱点来提升整体安全性。其原理涉及对输入验证、访问控制等安全机制的突破测试,在渗透测试和红队演练中具有重要价值。随着OWASP Top 10的持续更新和新型物联网设备的普及,漏洞挖掘技术不断演进,出现了SSRF、API安全等新兴攻击面。当前主流技术栈包括SQL注入绕过、WebView漏洞利用等实战技巧,配合Burp Suite、SQLMap等工具链可有效提升挖掘效率。对于初学者,建议从DVWA等本地靶场开始练习,逐步掌握漏洞报告撰写和SRC平台运营策略,同时严格遵守网络安全法律法规。
微信插件开发技术解析:从DLL注入到协议逆向
微信插件 · DLL注入 · API Hook
插件开发是扩展软件功能边界的重要技术手段,其核心原理是通过代码注入或API Hook实现对宿主程序的流程控制。在Windows平台,DLL注入和消息钩子是两种典型实现方式,前者通过CreateRemoteThread将动态库加载到目标进程,后者利用SetWindowsHookEx拦截系统消息。这类技术广泛应用于客户端功能增强领域,如微信生态中的自动化工具开发。以openclaw-weixin为例,这类插件需要结合逆向工程分析微信的私有协议和内存结构,同时应对反调试等防护机制。开发者需掌握PE文件格式、进程内存管理等底层知识,并注意性能优化与法律合规问题。随着WebAssembly等新技术发展,插件架构正在向更安全、跨平台的方向演进。
PPIO与AionUI:多智能体协作的高效解决方案
PPIO · AionUI · 多智能体协作
分布式计算平台与可视化智能体编排工具的结合,为复杂AI工作流提供了高效解决方案。PPIO作为底层算力支持,通过GPU资源池化和网络加速显著提升性能;AionUI则通过DAG调度器和容器化封装实现智能体的动态编排与热插拔。这种分层解耦的设计不仅降低了开发复杂度,还大幅缩短了部署时间。在实际应用中,如智能文档处理和跨模态内容生成场景,该方案展现出卓越的效率提升和资源优化能力,特别适合需要快速迭代和弹性扩展的企业级AI项目。
从需求到部署:完整项目开发框架实践指南
项目开发 · 5W1H分析法 · 技术架构
软件开发项目从需求分析到部署运维需要系统化的方法论支撑。5W1H需求分析法帮助明确用户场景和问题域,而技术架构设计则需要考虑模块化、扩展性和性能指标。现代开发实践中,Docker容器化和Git Flow工作流成为环境配置和版本控制的标准方案,结合TDD和CI/CD实现高效开发。性能优化方面,通过索引优化、缓存引入等手段可显著提升QPS,而完善的监控体系则保障系统稳定性。这些方法论与DevOps理念相结合,构成了完整的项目开发框架,特别适合中大型项目的全生命周期管理。
Agent技术工程化:从理论到实践的挑战与解决方案
Agent技术 · Harness Engineering · LLM
Agent技术作为人工智能领域的重要分支,正在从实验室研究快速转向工程化应用。其核心原理是基于大语言模型(LLM)构建具有自主决策能力的智能体,这种技术通过引入概率性特征和持续学习能力,显著提升了系统的灵活性和适应性。在工程实践中,Agent技术面临不确定性管理、版本控制革新和接口标准化等独特挑战。Harness Engineering框架通过增强可观测性、革新测试范式和调整CI/CD流程等方法应对这些挑战,在客户服务、电商推荐等场景展现出巨大价值。特别是在多Agent系统协作和金融、医疗等敏感领域应用中,结构化通信协议和可解释性保障成为关键技术突破点。随着LangChain等开发框架的成熟,Agent技术正在重塑软件工程实践,为智能化系统开发开辟新路径。
麻雀搜索算法(SSA)的Tent混沌初始化与扰动改进
麻雀搜索算法 · 群体智能优化 · Tent混沌映射
群体智能优化算法通过模拟自然界生物群体行为解决复杂优化问题,其核心在于平衡探索与开发能力。麻雀搜索算法(SSA)作为一种新型群体智能算法,模拟了麻雀觅食行为中的发现者-跟随者机制。针对标准SSA存在的种群多样性不足和易陷入局部最优问题,本文提出基于Tent混沌映射的改进方案。混沌系统具有遍历性和初值敏感性,能有效增强算法全局搜索能力。通过引入改进的Tent混沌初始化和自适应扰动策略,结合高斯扰动增强局部开发,显著提升了算法性能。实验表明,该方法在函数优化和工程应用中都展现出优越性,为智能优化领域提供了新的技术思路。
深度学习入门:MNIST手写识别实战与GPU环境配置
深度学习 · MNIST · GPU加速
深度学习作为机器学习的重要分支,通过神经网络模拟人脑工作机制实现特征自动提取。其核心在于构建模型-数据-训练的闭环系统,其中卷积神经网络(CNN)因其出色的图像处理能力成为计算机视觉标配。以经典的MNIST手写数字识别为例,从环境配置到模型训练全流程涉及GPU加速、数据预处理、网络架构设计等关键技术。实践表明,合理使用CUDA和cuDNN能提升10倍训练速度,而正确的数据归一化方法可避免NaN loss问题。这些技术在工业质检、OCR识别等场景有广泛应用,掌握它们是从理论到实践的关键跨越。
已经到底了哦
精选内容
热门内容
最新内容
Cell bin在工业自动化与物流仓储中的关键应用
Cell bin作为工业自动化与智能物流系统中的核心组件,通过模块化设计实现物料暂存与智能调度。其技术原理基于机械结构稳定性与物联网通信协议,在制造业、物流仓储及实验室自动化等场景发挥关键作用。典型应用包括解决生产节拍差异、实现智能拣货指引、保障样本稳定性等工程实践。随着工业4.0发展,支持PROFINET、EtherCAT等协议的智能cell bin,结合RFID识别与预测性维护算法,正推动产线效率与物流准确率显著提升。本文通过汽车制造、电商仓储等案例,详解cell bin的选型指标与维护技巧。
深度学习优化内燃机燃烧效率的PyTorch实践
深度学习技术正在颠覆传统工程优化方法,特别是在高维度非线性系统建模领域展现出独特优势。以燃烧优化为例,传统CFD仿真面临参数组合爆炸、物理模型失真等挑战,而基于PyTorch构建的3D-CNN和GAN网络能够有效提取燃烧过程的空间时序特征,实现燃烧室设计的智能优化。通过数字孪生技术,神经网络模型可以替代传统经验公式,将预测误差降低75%以上,同时支持实时参数反演和跨工况迁移。这种AI驱动的工程优化方法已在内燃机、燃气轮机等领域取得显著成效,典型应用可实现热效率提升7.8%、排放降低15.6%,为节能减排提供了创新解决方案。
AI代码理解技术:从原理到工程实践
代码理解是软件开发中的基础性挑战,涉及将程序代码转换为可分析的机器可读形式。其核心技术原理包括抽象语法树(AST)解析、控制流图构建等结构化表示方法,结合Transformer和图神经网络等模型架构,实现对代码语义的深度理解。这类技术在工程实践中具有重要价值,能显著提升代码文档生成、自动化审查等场景的效率。特别是随着CodeBERT等预训练模型的出现,开发者现在可以快速构建支持多语言的代码理解系统。当前最典型的应用包括智能错误诊断和遗留系统现代化改造,这些都需要结合AST分析和深度学习技术来实现精准的代码意图识别。
AI驱动的企业网络安全防御体系构建与实践
网络安全防御体系是保护企业数字资产的关键基础设施,其核心原理是通过多维度数据采集、机器学习分析和实时决策构建动态防护机制。随着AI技术在攻击端的广泛应用,传统基于规则的安全防护面临严峻挑战,智能化的防御体系成为行业刚需。在实际工程实践中,结合动态画像系统、多模态检测引擎和攻击链重构技术,能够有效应对自动化攻击的智能规避、持续进化等特征。以某电商平台为例,通过部署LSTM恶意负载检测和增强型WAF规则,攻击识别率提升111%,平均响应时间缩短85%。这类方案特别适用于金融、电商等高频攻击场景,其中异常检测算法和实时对抗引擎的设计是关键突破点。
YashanDB分布式数据库在企业数字化转型中的核心价值与实践
分布式数据库作为现代企业数字化转型的关键基础设施,通过存算分离架构和分布式事务处理机制,有效解决了传统关系型数据库在扩展性和成本效益方面的瓶颈。其核心技术原理包括分布式SQL引擎、多副本一致性协议和智能存储分层,能够同时满足OLTP和OLAP场景需求,在金融、零售等行业实现RTO<30秒的高可用保障。以YashanDB为代表的分布式数据库,通过Shared-Nothing架构支持200+节点的水平扩展,在某大型零售案例中实现查询性能提升63%。针对高并发写入、混合负载等典型场景,合理的部署形态选择和存储引擎调优可带来2.3倍的吞吐提升,这些实践为企业构建弹性可扩展的数字化平台提供了重要参考。
代理SHAP:加速机器学习模型解释的实用方案
机器学习可解释性是模型部署的关键环节,SHAP值作为基于博弈论的解释方法,通过特征边际贡献量化预测结果。传统SHAP计算面临组合爆炸问题,计算复杂度随特征数指数增长。代理SHAP创新性地引入轻量级代理模型(如线性回归或决策树)近似原始模型行为,在保持解释力的同时显著提升计算效率。该技术特别适合实时系统和大规模数据场景,通过特征筛选、采样优化等工程实践,可达成10倍以上的加速比。在金融风控、医疗诊断等需要高频模型解释的领域,代理SHAP有效解决了计算资源与解释时效的平衡问题,是机器学习可解释性工程化的重要突破。
结构光多频外差解相位技术原理与应用
结构光三维测量是一种通过投影特定图案并分析其形变来重建物体三维形状的技术,广泛应用于工业检测、文物数字化和医疗整形等领域。其核心在于相位解算,而多频外差法是目前公认的相位解包裹最优方案之一。该方法通过组合不同频率的正弦光栅图案,利用外差原理生成差频信号,既能保证测量范围又能获得高分辨率。在实际应用中,硬件选型(如DLP4500投影仪和Basler ace工业相机)和软件处理流水线(包括图像预处理、相位计算和外差解包裹)的优化至关重要。特别是在处理高反光表面或复杂形状时,多频外差法展现出显著优势。随着深度学习辅助解包裹等新技术的引入,该方法的精度和效率得到进一步提升。
使用Cloudflare Workers与Resend构建高性能邮件发送服务
Serverless架构正在重塑现代Web开发范式,其中边缘计算技术通过在靠近用户的位置执行代码,显著降低了网络延迟。Cloudflare Workers作为领先的边缘计算平台,结合Resend的高效邮件API,可以构建全球分布式的邮件发送服务。这种技术组合解决了传统SMTP服务器配置复杂、第三方邮件服务API笨重的问题,特别适合需要低延迟、高送达率的应用场景。从技术实现角度看,Cloudflare Workers基于V8隔离环境提供毫秒级冷启动,而Resend则通过简洁的RESTful接口和严格的邮件认证机制(SPF/DKIM/DMARC)确保邮件可靠送达。这种架构已被证明能将邮件发送延迟从300-500ms降至100ms以内,同时通过边缘节点的全球分布保证各地用户的一致体验。
深度学习入门:从MNIST手写数字分类实践神经网络
神经网络作为深度学习的核心架构,通过模拟生物神经元的工作机制实现智能决策。其基础单元遵循y=σ(wx+b)的数学模型,其中激活函数σ(如ReLU)决定信息传递,权重w和偏置b通过训练不断优化。在图像分类等典型应用中,合理的层结构设计(输入层→隐藏层→输出层)和损失函数选择(如交叉熵损失)直接影响模型性能。以MNIST手写数字识别为例,通过Keras框架可以快速实现包含Dropout层和Adam优化器的网络搭建,同时需警惕过拟合和梯度消失等常见问题。掌握这些基础技术后,开发者可进一步探索Grad-CAM可视化等进阶技巧,深入理解模型决策过程。
Windows+WSL2搭建低延迟直播系统全攻略
流媒体技术中的低延迟传输是实时互动场景的核心需求,其原理主要基于协议优化和编码加速。通过RTMP协议配合HTTP-FLV播放方案,结合关键帧间隔控制与分片优化,可实现秒级延迟的直播效果。这种技术在电商直播、在线教育等需要实时交互的场景中具有重要价值。本文演示如何利用WSL2环境配合Nginx-RTMP模块,构建零成本的直播系统解决方案,其中Android推流端采用Larix Broadcaster实现移动端低延迟采集,浏览器播放端通过flv.js实现跨平台兼容。整套方案特别强调对硬件加速编码和网络穿透配置的实践指导。
已经到底了哦