首页/新闻资讯/正文详情

UPDATE语句从入门到避坑:事务、锁、索引与批量更新全解析

发布时间:2026/9/24 19:43:04 来源:云帆数科 栏目:资讯中心
UPDATE语句从入门到避坑:事务、锁、索引与批量更新全解析
最近整理SQL学习笔记时很多同学问过我同一个问题明明就是一条UPDATE语句为什么值得连续用两篇笔记来记录其实答案是更新数据这个动作看起来简单背后牵扯的东西一点都不少——从UPDATE的语法细节、WHERE条件的影响范围到事务、锁、日志再到类型转换、NULL语义、批量更新的性能问题随便拎一个出来都能写出一篇完整的排错实录。这篇文章就是把第113、114篇里的核心知识点做了一次系统化汇总顺手把两篇里没有展开的细节也补齐了。不管你是刚学SQL的新手还是写了好几年SQL但每次执行UPDATE前心里都会咯噔一下的开发同学这篇内容应该都能让你少踩一些坑。1. 从“改一个字段”到“改整张表”UPDATE语句的三种典型更新场景1.1 整表更新加了WHERE和没加WHERE是两个世界最基础的UPDATE长这样UPDATE user_info SET status 1;这条语句会把整张表的status字段全部置为1。如果这张表有100万行、1000万行它就会把100万行、1000万行全都改掉。很多时候我们会下意识地以为“我就跑一条更新而已”但实际上数据库要做的工作量完全取决于这张表有多大。真正的风险不只是数据被全表覆盖而是这条更新对在线业务的影响。数据库更新数据时会在行上加锁整表更新就意味着锁的范围会随着扫描进度不断扩张。中途如果有别的会话想更新这张表里任意一行都会被长时间阻塞。更麻烦的是如果更新到一半你发现条件写错了想停下来回滚也需要很长的时间——回滚要逆向重放日志处理的行越多回滚越慢有时候甚至比正常执行还要慢得多。我见过一个真实的例子有人对一张千万级的订单表执行了类似这样的语句UPDATE order_info SET order_status 0 WHERE order_no 202501010001;单看SQL没毛病问题出在order_no列没有索引。为了找这一行数据库只能全表扫描扫描过程中所有被扫到的行都要加锁结果整个订单表被锁住了所有写请求全部卡死。这个过程在监控面板上看就是一条慢SQL挂了几分钟实际上是整个表的更新入口被堵死了。所以不要把“整表更新”只理解为不加WHERE的裸UPDATE。只要WHERE条件上的列没有索引或者选择性很差同样会演变成实质上的全表扫描更新。稳妥的习惯是执行UPDATE之前先把这个WHERE条件复制到SELECT里看一下会命中多少行心里有数再去执行。1.2 条件更新WHERE条件是UPDATE的生命线绝大多数业务场景下的UPDATE都是带WHERE条件的有条件更新UPDATE student SET status 1 WHERE class_id 10;这种写法本身没有歧义但WHERE条件里的坑比大多数人想象的多。最典型的有几类。第一类是NULL判断。很多人会写成UPDATE student SET remark 已联系 WHERE remark NULL;这句SQL永远不会更新任何一行因为SQL标准里NULL不等于任何值包括NULL本身。正确写法是UPDATE student SET remark 已联系 WHERE remark IS NULL;第二类是隐式类型转换这一块我后面会专门展开这里先记住一个原则WHERE条件的值和目标列的类型保持完全一致不要依赖数据库去自动转换。第三类是字符串末尾空格。在SQL Server的VARCHAR类型中abc和abc 做等值比较时通常被视为相等条件能命中但如果你更新的时候往字段里写了带空格的值存储时空格会被保留下来。也就是说比较的时候数据库可能“忽略”空格写进去的时候空格却“留在”那里这会给后续的数据处理造成很多莫名其妙的麻烦。真正有经验的做法是每次执行条件更新之前先跑一句确认查询SELECT COUNT(*) FROM student WHERE class_id 10;确认这个行数符合预期再换成UPDATE执行。这个过程听起来多此一举但可以在大事故之前拦住99%的低级失误。1.3 关联更新从另一张表取值更新当前表的三种写法业务里经常遇到这种需求需要根据另一张表的数据来更新当前表。比如临时表里算好了每个学生的成绩现在要回写到学生主表。SQL Server里最常见的写法是利用UPDATE...FROMUPDATE s SET s.score t.score, s.updated_at GETDATE() FROM student s INNER JOIN temp_score t ON s.student_id t.student_id WHERE t.score IS NOT NULL;MySQL不认这种语法不过它有自己的JOIN式UPDATE写法UPDATE student s INNER JOIN temp_score t ON s.student_id t.student_id SET s.score t.score WHERE t.score IS NOT NULL;如果不想用JOIN也可以用子查询UPDATE student SET score ( SELECT TOP 1 t.score FROM temp_score t WHERE t.student_id student.student_id ORDER BY t.version DESC ) WHERE EXISTS ( SELECT 1 FROM temp_score t WHERE t.student_id student.student_id );关联更新最容易踩的坑是关联字段有重复值。比如temp_score里一个学生有多条成绩记录JOIN式更新会用哪一条去覆盖不同数据库的行为并不一致有的可能取物理存储碰到的第一条有的可能把重复匹配多次执行最终结果很难预判。所以做关联更新之前必须确认关联字段在右表里是唯一的。如果存在重复要先做去重或者用子查询TOP 1/ROW_NUMBER()的方式显式指定取哪一条。另外还要注意关联更新的匹配条件在两张表都不会产生NULL。如果右表的关联字段是NULLJOIN根本匹配不上那种情况下可以先用临时表把数据清洗干净再更新避免在一条UPDATE里同时做清洗和回写两件事逻辑会清晰很多。2. 事务、日志与锁执行UPDATE前必须先想清楚的三件事2.1 为什么UPDATE必须放进事务里默认情况下大多数数据库每执行一条UPDATE都会自动提交。也就是说你的SQL一旦跑出去影响就生效了没有反悔的机会。有些业务场景允许这样但很多关键业务不行。典型场景是转账扣款。假设需要把账户A的余额减100账户B的余额加100写成两条UPDATE。如果第一条成功、第二条失败系统里就会莫名少掉100块钱。这种场景的正确做法是把两条更新放进同一个事务里BEGIN TRANSACTION; UPDATE account SET balance balance - 100 WHERE user_id 1 AND balance 100; IF ROWCOUNT 0 BEGIN ROLLBACK TRANSACTION; RETURN; END; UPDATE account SET balance balance 100 WHERE user_id 2; IF ROWCOUNT 0 BEGIN ROLLBACK TRANSACTION; RETURN; END; COMMIT TRANSACTION;这里有一个很多人会忽略的关键点为什么每次UPDATE之后都要检查ROWCOUNT因为如果user_id1的账户余额不足UPDATE ... WHERE user_id 1 AND balance 100这条语句执行成功但影响行数为0如果不检查就直接执行后面的加款那么“扣款没成功加款却成功了”数据照样是错的。所以把UPDATE放进事务的目的不只是保证“一起成功或一起失败”更重要的是给你一个机会在提交之前对每条更新的结果做校验。校验不通过就回滚校验通过才提交。实践中我还建议在这个事务里加一个自定义的标记比如更新后再查一次账户余额确认没有负数再做COMMIT。这种“更新后校验”的习惯能兜住很多逻辑漏洞。提示在SQL Server中RETURN会直接跳出批处理。如果是在存储过程中使用要确保流程确实会结束不要跳到后面的业务逻辑继续执行。也可以用IF...ELSE把回滚分支包起来避免提前RETURN导致后面的代码不可达。2.2 UPDATE的锁行锁还是表锁影响的是整个系统的并发一条UPDATE执行时数据库默认会给涉及的数据行加排他锁防止其他事务同时修改同一行。这本来是数据库并发的正常机制但锁的范围一旦扩大系统就会变慢。什么情况下锁会从行锁升级成页锁甚至表锁最常见的有三种更新的行数非常多超过数据库的行锁升级阈值。WHERE条件无法走索引执行计划选择了全表扫描扫描过程中的每一行都可能被锁。事务长时间不提交导致锁被长时间持有其他会话全部排队等待。锁的问题是所有数据库都会遇到的区别只是排查手段不同。最典型的现场是会话A执行了一条UPDATE但一直不COMMIT会话B更新同一行时就一直卡住直到超时或A提交。此时数据库看起来“没坏”但业务已经瘫痪了。我自己的习惯是生产环境的UPDATE如果可能影响到多行会先评估会不会长时间持锁。如果行数规模大就分段提交减少单次持锁时间。后面讲批量更新时具体说。另外可以在连接层面设置合理的锁等待时间。SQL Server可以设置SET LOCK_TIMEOUT 2000;这表示如果等待锁超过2秒就直接报错而不是无限期等下去。无限期等锁是很多后端接口超时的直接原因。2.3 日志与恢复模式一条UPDATE在背后做了多少事很多初学者不知道UPDATE在数据库内部不是简单地把磁盘上的值改掉。为了能在崩溃后恢复数据库会把更新前后的关键信息写入事务日志。简单来说日志里要记录“哪些数据被改了、旧值是什么、新值是什么”再次执行时也要重放这些日志。这就引出了三个实践问题。第一日志文件会增长。一次大UPDATE会生成大量日志。如果数据库是完整恢复模式SQL Server术语日志不会自动收缩必须靠定期备份日志来截断。如果日志一直不备份它会持续膨胀直到磁盘写满整个数据库变成只读状态业务全部停摆。第二UPDATE的日志量跟影响的行数正相关。更新1000行的日志量和更新100万行的日志量不是一个量级。很多慢更新和磁盘卡顿其实不是SELECT扫描慢而是日志写入IO跟不上了。第三要注意区分简单恢复模式和完整恢复模式。简单恢复模式下日志空间可以循环利用但遇到故障时只能恢复到最近一次完整备份完整恢复模式能支持精确到某个时间点的恢复但代价是日志文件管理成本更高。如果业务要求不能丢数据就得承受完整恢复模式带来的日志膨胀问题。建议是批量UPDATE之前先看一眼当前数据库的恢复模式和磁盘剩余空间。如果预估会产生大量日志提前规划日志备份或分批更新。别等到磁盘满的那天再查日志那时候已经卡到连查都难了。3. 更新数据时的隐形杀手类型转换、NULL语义与字符集陷阱3.1 隐式类型转换SQL写对了性能却崩了先看一条“看似正常”的更新UPDATE order_info SET order_status 2 WHERE order_no 202501010001;如果order_no列是VARCHAR类型而条件里的202501010001是数字数据库会怎么处理不同的数据库行为不完全一样但比较常见的是把列值隐式转换成数字再比较。一旦列上发生了类型转换这列上的索引就基本失效了因为索引本身就是按原类型排序的改成数字之后没法直接走索引只能全表扫描。更隐蔽的是这种问题不会报错。数据量小的时候你根本察觉不到数据量一大同样的SQL从毫秒级变成秒级然后再变成分钟级。解决方式很简单让条件的类型和目标列完全一致。UPDATE order_info SET order_status 2 WHERE order_no 202501010001;加个引号索引就能用上更新效率天差地别。SQL Server里还有一个本地隐藏陷阱列是NVARCHAR条件里传的是VARCHAR两者之间也会发生隐式转换而且可能导致索引失效。解决思路是一样的应用层在传参时保持参数类型与数据库列类型一致。3.2 NULL语义更新时最容易踩的坑NULL在SQL里不是“空字符串”也不是0它代表的是“未知”。这个哲学层面的差异在日常UPDATE中影响很大。第一把字段更新为NULL和更新为空字符串是两回事。SET remark NULL表示“没有任何值”SET remark 表示“有一个空文本”。如果你的应用前端把空字符串和NULL混用查询统计时就会出现很多诡异的数据差异。比如WHERE remark IS NULL查不到空字符串WHERE remark 查不到NULL。第二UPDATE的NULL传播问题。假设有一张表有旧值列old_value和新值列new_value你想把new_value更新成old_valueUPDATE my_table SET new_value old_value;如果某一行old_value是NULL那么new_value也会变成NULL这可能是你不希望发生的。解决办法是先判断UPDATE my_table SET new_value COALESCE(old_value, new_value);第三CASE WHEN表达式里NULL会走ELSE分支。比如UPDATE student SET grade CASE WHEN score 90 THEN A WHEN score 75 THEN B ELSE C END;当score是NULL时不会命中任何一个WHEN而是直接落到ELSEgrade会被置为C。如果业务上不希望在分数缺失时给C就得在CASE里显式处理NULLSET grade CASE WHEN score IS NULL THEN NULL WHEN score 90 THEN A WHEN score 75 THEN B ELSE C END;很多“线上成绩莫名其妙变成C”的故障根因就是这样一条看似合理的UPDATE。3.3 字符串空格与字符集问题字符串字段在更新时的行为比你想的更微妙。VARCHAR类型比较时数据库通常忽略末尾空格。也就是说WHERE user_name tom能匹配到存储值为tom 的行。但如果你往字符串中间或末尾写入空格存储时空格会被保留。这带来的问题是查询时感觉“相等”存储结果却不一样后续做字符串拼接、去重、分组统计时全都会出现不一致。字符集的问题更隐蔽。SQL Server里VARCHAR和NVARCHAR混用MySQL里utf8mb4和utf8混用都可能导致隐式转换。这种转换不仅影响索引使用还可能引起数据长度变化。一个典型的例子是某个字符串字段原来存的是单字节字符换了字符集之后每个字符占用的字节数变了字段长度不够就可能会被截断更新时数据就悄悄丢了一截。遇到这种情况建议先做数据体检确认目标列和自己传入的数据在字符集、字节长度上一致再执行更新。尤其在多语言环境下不要想当然地认为“字符串就是字符串”。4. 慢UPDATE的排查链路与批量更新性能优化4.1 影响UPDATE性能的因素拆解一条UPDATE为什么慢我习惯从四个环节去拆。扫描成本找到需要更新的行消耗了多少IO。索引缺失、隐式类型转换、条件选择性差都会放大这部分成本。修改成本更新数据本身、更新二级索引的成本。一张表上的索引越多更新时要同步修改的索引也越多。日志成本UPDATE生成的日志量。日志写入IO很容易成为瓶颈。锁成本锁等待时间。等其他事务提交的时间也算在UPDATE耗时里。排查时建议先看执行计划确认扫描方式到底是什么。如果发现Update语句中的查询部分在走全表扫描优先补索引或优化WHERE条件如果扫描方式没问题但还是很慢再看日志和锁。4.2 分批更新把大UPDATE拆成小事务面对几百万行的批量更新最有效的优化手段之一就是分批更新。比如要把全表status从0改成1如果直接执行一条UPDATE会长时间持有大量锁日志也会急剧膨胀。换成批量方式DECLARE batch_size INT 1000; DECLARE rows INT 1; WHILE rows 0 BEGIN UPDATE TOP (batch_size) student SET status 1 WHERE status 0; SET rows ROWCOUNT; WAITFOR DELAY 00:00:01; END;每批只更新1000行每批之间停顿1秒这样做的效果是锁的持有时间很短其他业务请求在间隙里还能正常执行日志增长可控即使出问题也容易恢复。分批大小不是越大越好。太大锁和日志的压力依然存在太小循环次数多整体效率变低。我一般从500到2000之间起步再根据线上监控调整。你可以在测试环境用不同批次大小跑一遍观察单批次耗时和锁阻塞情况再来决定。另一个容易忽略的问题是“断点续传”。日常更新的WHERE条件要能定位到剩余未更新的数据。上面的写法用WHERE status 0天然支持但如果你的更新条件没有这种状态标志就要考虑引入一个标记字段或者用主键范围分段避免重复扫描已更新的数据。4.3 执行计划与阻塞现场如果更新已经卡住了第一步是查它到底在等什么。SQL Server里可以用这个查询看当前阻塞链SELECT blocking.session_id AS 阻塞会话ID, blocked.session_id AS 被阻塞会话ID, blocked.wait_type, blocked.wait_time, blk_text.text AS 阻塞SQL, bnd_text.text AS 被阻塞SQL FROM sys.dm_exec_requests blocked JOIN sys.dm_exec_requests blocking ON blocked.blocking_session_id blocking.session_id CROSS APPLY sys.dm_exec_sql_text(blocking.sql_handle) blk_text CROSS APPLY sys.dm_exec_sql_text(blocked.sql_handle) bnd_text WHERE blocked.blocking_session_id 0;如果查到阻塞源是某个会话执行了UPDATE却没提交那问题就清晰了让对应应用的负责人确认事务是否正常提交或者直接终止那个阻塞会话。死锁比阻塞更麻烦。两个会话各自持有一把锁又同时等待对方释放锁数据库会自动回滚其中一个事务来解除死锁。排查死锁需要开启死锁追踪把死锁图捞出来仔细看资源申请顺序。预防死锁的通用思路是所有事务按相同的顺序访问表或索引尽量缩短事务长度。提示真正的大更新强烈建议放在业务低峰期执行。无论优化做得再精细一次大规模UPDATE对在线系统的影响都是客观存在的。低峰期执行能让你在出问题时有充足的时间去处理而不是在用户投诉的轰炸中艰难排查。5. 113114两篇笔记之外的几条实战经验5.1 更新前先做两件小事第一件小事备份受影响数据。这不是让你备份整个数据库而是把本次更新会命中的那部分数据导出来留一条后路SELECT * INTO student_backup_20250101 FROM student WHERE class_id 10;如果更新后发现业务数据被改坏了可以直接从这张备份表恢复。备份表的名字里带上日期时间久了也知道是哪次操作留下的。第二件小事先用SELECT验证影响范围SELECT COUNT(*) FROM student WHERE class_id 10;对比一下这个数字和业务预期的量级是否一致。如果预期的100行结果查出10万行那肯定哪里不对劲停下来查清楚再说。5.2 用OUTPUT子句或影响行数记录更新明细标题里提到的“更新记录”在数据层面也有一层含义记录每一次更新前后的值。SQL Server的OUTPUT子句可以直接把这次UPDATE影响到的数据输出配合临时表或真实审计表使用UPDATE student SET status 1 OUTPUT inserted.student_id, deleted.status AS old_status, inserted.status AS new_status, GETDATE() AS updated_at WHERE class_id 10;执行完之后可以通过程序把OUTPUT返回的结果集保存下来也可以把它插入到一张更新日志表里UPDATE student SET status 1 OUTPUT inserted.student_id, deleted.status AS old_status, inserted.status AS new_status, GETDATE() AS updated_at INTO student_update_log(student_id, old_status, new_status, updated_at) WHERE class_id 10;deleted代表更新前的旧行inserted代表更新后的新行这样就能完整保留每一次更新的旧值和新值。对于有审计需求的业务这个功能非常实用不用额外写触发器还能保证数据一致性。MySQL里没有OUTPUT子句但你可以先查出一批要更新的主键更新后再根据主键抓取新值去写审计表。虽然多几步但效果类似。5.3 串联两篇更新笔记的核心思路第113篇主要讲单表更新的基础语法和WHERE条件第114篇重点讲关联更新和条件组合更新。把这两篇串起来之后我自己的体会是一次靠谱的UPDATE操作永远遵循三步走——先确认影响哪些数据再确认更新成什么值最后确认做完怎么验证。影响哪些数据靠的是SELECT预检和WHERE条件的精确控制更新成什么值靠的是对类型、NULL、字符集这些细节的把握做完怎么验证靠的是事务里的ROWCOUNT检查、OUTPUT审计还有更新后的业务数据抽查。这三步缺一不可。很多人写UPDATE只关注SQL语法本身觉得不报错就万事大吉但真正在线上环境里不报错和数据正确完全是两码事。至少在我自己的开发环境里UPDATE一直是我最谨慎的一条语句。整理完113和114这两篇之后我又把笔记里的示例在几种数据库上重新跑了一遍印象最深的是同一个更新需求在SQL Server和MySQL里的关联写法差异。如果你也想做类似的验证建议先找一台测试机把文中常用写法都跑一遍留着这些实测过程当笔记比背语法说明书有用得多。

相关推荐

审批状态错乱之谜:并发覆盖下的数据库事务、MVCC与缓存一致性实战
审批状态错乱之谜:并发覆盖下的数据库事务、MVCC与缓存一致性实战

1. 审批状态“倒带”了:现象、复现和第一反应1.1 诡异状态:同意之后又变回审批中在生产环境干过审批流的老哥应该都遇到过这种诡异场景:明明数据库里的审批单已经走完,业务方却拿着截图说状态是“审批中”,再次点同意又… · 2026/9/24 19:43:04

网吧盈利现状与未来潜力深度全景解读(超万字攻略)
网吧盈利现状与未来潜力深度全景解读(超万字攻略)

一、前言:网吧,从辉煌到转型的世纪旅程 曾几何时,网吧是无数80后、90后青春的记忆——“上网两小时,快乐一整天”,在那个互联网刚起步的年代,网吧是信息的窗口,是游戏的乐园,也是城市… · 2026/9/24 19:42:58

PyTorch3D 点云光栅化完全指南:rasterize_points 参数、原理与实战
PyTorch3D 点云光栅化完全指南:rasterize_points 参数、原理与实战

PyTorch3D 点云光栅化完全指南:rasterize_points 参数、原理与实战 【免费下载链接】pytorch3d PyTorch3D is FAIRs library of reusable components for deep learning with 3D data 项目地址: https://gitcode.com/gh_mirrors/py/pytorch3d 导读 本文围绕… · 2026/9/24 19:42:51

2026年组件安全扫描选型指南:商业、开源与信创方案对比
2026年组件安全扫描选型指南:商业、开源与信创方案对比

1. 组件安全扫描到底在扫什么,为什么2026年突然成了刚需组件安全扫描,圈子里更习惯叫SCA(Software Composition Analysis),说白了就是把你项目里用到的所有第三方依赖——不管是Maven拉下来的jar包、npm装的node_modul… · 2026/9/24 20:25:57

拯救者玩游戏花屏闪退,不一定是显卡驱动问题
拯救者玩游戏花屏闪退,不一定是显卡驱动问题

不少拯救者游戏本用户碰到这样的故障:桌面浏览网页、看视频一切正常,只要打开大型游戏,画面就出现色块、条纹、马赛克花屏,紧接着游戏闪退,严重时直接蓝屏。很多人第一反应就是显卡驱动出问题,反复卸载、重… · 2026/9/24 20:25:51

求职焦虑自救指南:用能力定位和项目思维破局就业困境
求职焦虑自救指南:用能力定位和项目思维破局就业困境

1. 焦虑人人都有,但别被"数字"牵着走我最近后台收到不少年轻朋友的留言,都在问同一个问题:大环境不好,是不是毕业就等于失业?是不是再怎么努力也没用?说实话,只要打开社交平台&#x… · 2026/9/24 20:25:51

全开源超级签名系统部署指南:iOS内部分发与UDID签名原理详解
全开源超级签名系统部署指南:iOS内部分发与UDID签名原理详解

简介:面向需要搭建iOS应用分发与签名服务的开发者和企业,这是一套全开源的APP分发系统及超级签名系统源码,基于PHP开发,具备后台管理功能,并附详细部署文档。系统方案涵盖后台账号配置、阿里云OSS存储、七牛云下载包托… · 2026/9/24 20:25:51

Edge无法发送验证码?揭秘浏览器UA检测与兼容性问题
Edge无法发送验证码?揭秘浏览器UA检测与兼容性问题

“全国新书目-书籍-教材查询-最全面-用chrome 浏览器才能发送验证码——用edge浏览器登入提示无法发送验证码,为何?”这个标题里的问题,我太熟了。遇到这个问题的绝对不止你一个人,它背后牵扯出的其实是很多老网站做浏览器适配时留… · 2026/9/24 20:25:39

订单多了,利润却薄了?模具注塑厂的效率困局
订单多了,利润却薄了?模具注塑厂的效率困局

订单量上涨,账上利润却没同步变厚,这是当下不少模具注塑厂的真实体感。旺季产线排满,淡季又空转,摊薄下来单件成本反而走高。问题往往不在订单本身,而在从开模到量产之间的衔接损耗。有行业统计显示,制造环… · 2026/9/24 20:25:39

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13

1D-CNN时间序列建模实战:从Conv1d原理到工业落地
1D-CNN时间序列建模实战:从Conv1d原理到工业落地

简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26

柔软的L:汉语语流中被忽视的舌肌张力控制
柔软的L:汉语语流中被忽视的舌肌张力控制

1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码