news 2026/10/4 10:35:10

MySQL CRUD深度解析:事务、锁、索引与误操作恢复实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL CRUD深度解析:事务、锁、索引与误操作恢复实战

1. 从热搜词里看见的真相:简单CRUD背后藏着生产事故

如果你去翻一下最近的MySQL热搜词,会看到一大堆与增删改查毫无直接关系的词条:"mysql update 还原"、"mysql锁的分类"、"mysql事务处理"、"mysql数据库连接池"、"mysql存储过程"。这很有意思——每天有无数的MySQL初学者在学CRUD(Create、Read、Update、Delete 四个数据操作),但真正在线上环境被MySQL"教育"过的人,反而都在检索这些"看起来更高级"的话题。

我自己的体会是:MySQL的CRUD,是所有数据库操作的绝对地基。但恰恰因为它太基础,绝大多数人在学的时候只停留在"能跑通"的层面,从来没有认真思考过一条UPDATE语句在InnoDB引擎里到底做了哪些事、一个不带WHERE的DELETE到底能不能救回来、为什么同样是查一条数据,别人用0.01秒而你用了3秒。

这篇文章就是来补这些课的。面向的读者不是完全没接触过数据库的小白,而是那些已经能用Navicat跑通增删改查、但还没有真正在项目里扛过生产环境、写过复杂报表查询、处理过并发扣减的开发者。我会把CRUD的核心语法模型、事务与锁的关系、误操作恢复手段、索引与连接池的提速方案一条条拆开讲,每一步都可以直接拿到你的项目里去用。

先说一句我踩过十年坑之后得出的结论:会写CRUD的人很多,但能把CRUD写得稳、写得快、写不坏的人不多。这篇文章就是帮你完成从"会写"到"写得稳"这一步跨越。

2. 四条核心语句的完整语法拆解与执行细节

2.1 INSERT:批量插入与单条插入的性能分水岭

INSERT的写法在教科书里通常会列好几种:INSERT INTO 表名 VALUES (...)、INSERT INTO 表名 (字段1, 字段2) VALUES (...)、INSERT INTO 表名 SET 字段1=值1, 字段2=值2。但实际开发里,真正重要的是"批量插入"和"单条插入"的选择。因为每一次INSERT都需要经历网络往返、SQL解析、权限检查、存储引擎写入、日志记录这一整套链路,单条循环插入1万条数据,哪怕是本地连接也要几秒钟。

我做过一次真实测试:在8代i5、机械硬盘的机器上,用单条INSERT循环往一个无索引的测试表里插1万行,耗时接近4秒;改用一条INSERT带上100条VALUES的多值写法,只需要约1.2秒;如果再把100条一组的批量写压到单次连接事务里,总耗时能压进0.8秒左右。差距就是这么大。

但批量插入也有两个隐性坑。第一个是max_allowed_packet参数,默认通常是4MB或64MB,如果一条INSERT的VALUES串得太大直接报错。第二个是单条事务里插的行数太多,会导致InnoDB的undo log膨胀、binlog变大,后期做数据恢复时重放会很慢。我的经验是单批次控制在500到1000行之间比较稳妥,既照顾了性能,又不至于撑爆事务日志。

还有一个容易被忽略的点:INSERT ... ON DUPLICATE KEY UPDATE。这语句解决的是"存在就更新、不存在就插入"的业务场景,比如用户积分流水、设备状态上报。它比"先SELECT判断再决定INSERT还是UPDATE"少了一次网络往返,并且不是多此一举——在高并发下,先查再写的逻辑会被竞态条件打穿,后执行的判断结果会把先执行的覆盖掉。

2.2 SELECT:执行顺序比你以为的更重要

SELECT是CRUD里最常用、也最容易被写乱的语句。很多开发者写查询都是"从头到尾顺着直觉写",但真正理解SELECT执行顺序的人,优化SQL时思路完全不一样。

MySQL执行一条SELECT的逻辑顺序是:FROM(确定基表)→ ON(JOIN条件过滤)→ JOIN(连接)→ WHERE(行级过滤)→ GROUP BY(分组)→ HAVING(分组后过滤)→ SELECT(列投影)→ DISTINCT(去重)→ ORDER BY(排序)→ LIMIT(分页)。这里最反直觉的是WHERE在SELECT之前执行,所以你在SELECT里给别名、在WHERE里用别名,MySQL会直接报错,因为执行到WHERE这一步时别名还不存在。

再比如很多人认为HAVING和WHERE差不多,实际上WHER在分组前过滤原始行,HAVING在分组后过滤聚合结果。有一个很典型的例子:想查"订单总额大于1000的客户",如果写WHERE SUM(amount) > 1000 直接报错,必须用HAVING。而"过滤掉金额小于100的订单"这种行级过滤永远应该放WHERE,把它放进HAVING虽然也能跑,但分组前的数据量没有缩减,性能差距在百万行表上会非常明显。

关于SELECT还有一个容易被问倒的细节:SELECT DISTINCT的实现方式和GROUP BY非常接近,如果你的查询只是想去重一两个字段,用GROUP BY的效率往往更稳定。而LIMIT深分页的问题我会在后面专门讲,这里只提醒一句:千万行表上不要直接写LIMIT 100000, 20,这个写法会让MySQL扫描十万行再扔掉前十万行。

2.3 UPDATE:不只改数据,还会动索引和锁

UPDATE是四条语句里最需要敬畏心的一条,原因有三个:影响行数不可控、加锁范围不可控、一旦写错恢复成本极高。先说语法层面,UPDATE的完整形态是 UPDATE 表名 SET 字段=值 WHERE 条件,SET后面可以跟多个字段,用逗号分隔。关键点在于:UPDATE执行时InnoDB会先按WHERE条件找到目标行,再对这些行加排他锁,然后修改字段值并写入新的数据版本。

这意味着如果你的WHERE条件没有走索引,InnoDB会扫描全表来"找"目标行,并且每扫描到一行符合条件的都会加锁。这就是著名的"UPDATE不带索引条件导致全表加锁"问题。举个例子:一张千万行的用户表,你执行 UPDATE user SET status=1 WHERE name='张三',而name字段没有索引,MySQL会做全表扫描,扫描过程中会把所有行都锁住,导致整张表在事务提交前无法被其他会话写入。你本意只改一行,结果锁了全表。

另外值得一提的是UPDATE和索引的关系:更新普通字段时,如果这个字段恰好是二级索引的一部分,InnoDB需要同步更新索引条目,产生额外的写放大。因此频繁更新的字段不适合放进索引,尤其是联合索引。还有人在UPDATE语句里同时更新主键值,这在InnoDB里会触发"删除旧行+插入新行"的操作,代价极大,尽量避免。

2.4 DELETE:不只是删行,表空间和锁同样有代价

DELETE在语法上很简单:DELETE FROM 表名 WHERE 条件 或者 DELETE FROM 表名 ORDER BY ... LIMIT n。但它的执行代价经常被低估。删除一行数据时,InnoDB并不会立刻把物理磁盘空间释放掉,而是在聚集索引里给这行数据标记为"已删除",同时写入undo log以便事务回滚。只有事务提交后,那些被标记的空间才可能被后续插入复用。这个机制决定了:频繁的DELETE和INSERT并存会让表产生碎片,表现为表文件很大、查询变慢。

DELETE和TRUNCATE经常被拿来做对比,很多人以为它们只是"删得快慢"的区别,其实完全不是一回事:

维度DELETETRUNCATE
条件过滤支持WHERE,可只删部分行不支持WHERE,清空全表
事务回滚支持,可回滚DDL操作,部分版本不可回滚
空间释放不立即释放,产生碎片直接重置表空间和数据页
锁影响逐行加锁表级排他锁
自增ID不重置重置为初始值
执行速度逐行删除,慢直接重建表,极快

开发里最担心的误删场景几乎都和DELETE有关,因为WHERE一旦漏掉,整张表就空了。关于误删之后的恢复手段,我会在第5节专门展开。

3. WHERE条件与索引:慢查询的真正根源常见于条件设计

3.1 隐式类型转换:直觉上没毛病,索引就是不生效

很多慢查询不是SQL写错了,而是WHERE条件设计时踩了隐式类型转换的坑。MySQL的规则是:当比较的双方一个是字符串、一个是整数时,会把字符串转换成数字再比较。如果你的表字段是VARCHAR类型,但查询条件里写的是数字,比如 phone = 13800138000,MySQL会先把phone字段的每个值都转换成数字再和这个数字比较。可怕的地方在于:对字段做函数或类型转换会让该字段上的索引失效,于是本该走索引的查询变成了全表扫描。

这个问题的隐蔽性在于:从结果上看,查询是"正确"的——只要表数据量不大,根本感觉不到差异。一旦表数据上了百万条,相同条件下索引走法和全表扫描可能是0.1秒和8秒的差距。排查办法也很简单:执行EXPLAIN看type列,如果是ALL就是全表扫描,走索引则是ref或range。

我建议项目里定一条规矩:条件参数的类型必须和字段定义严格一致。如果历史数据里已经混用了,整改方式要么是修改传入参数的类型,要么给字段加CAST,但绝对不推荐为了省事去写 WHERE CAST(phone AS CHAR) = '138...',因为对字段的函数操作同样让索引失效。更优的解法是利用MySQL对"字符串转数字"的隐式规则,把等号右边写成字符串字面量,让优化器把常量转成数字后直接和普通索引匹配,但这个技巧太依赖版本行为,不如直接统一类型稳妥。

3.2 LIKE、OR与函数包裹:三种常见的索引失效写法

实际项目里,索引失效的套路非常固定。第一种是LIKE '%关键字',MySQL在这类模糊匹配上无法用B+树的索引前缀定位,只能扫全表。反过来写LIKE '关键字%'就可以走索引范围扫描。所以如果你确定要查的是包含关系,做好全表扫描的心理准备;如果业务允许,把模糊查询改成前缀匹配是更优解。

第二种是OR条件。即使OR两边的字段都分别建了独立索引,MySQL在旧版本里也可能放弃索引合并,直接做全表扫描。更麻烦的是OR两边如果只有一边有索引,优化器几乎必然选择全表扫描。经验做法是:把OR拆分成语义等价的UNION ALL,每条子查询单独走自己的索引;或者把多个索引改成联合索引,让OR查询转换为索引区间合并。

第三种是对字段做函数包裹。时间范围查询是最常见的受害场景。很多人写 WHERE YEAR(create_time) = 2025,这是对create_time字段调用YEAR函数,索引直接失效。正确写法是 WHERE create_time >= '2025-01-01' AND create_time < '2026-01-01',这样create_time的索引可以正常走范围扫描。注意,这个写法不仅性能更好,语义上也更准确——它能精确覆盖"2025年全年"这个半开区间,避免时间戳里时分秒的边界遗漏。

3.3 ORDER BY与LIMIT深分页:排序和分页不是免费的

ORDER BY在CRUD里也常被当成"没什么好讲"的部分,但它在MySQL里的代价往往超出直觉。当排序字段正好是索引列时,InnoDB可以顺着索引顺序直接返回,完全不用额外排序,执行计划里Extra列会显示Using index。大多数情况下的排序字段不是索引列,MySQL会把查询结果导到内存或磁盘做filesort。结果集很大的时候,filesort会写临时文件,性能会差到让你怀疑是不是数据库卡死了。

深分页的问题更为常见。业务系统里翻到几十页之后,LIMIT 100000, 20这个写法会让MySQL扫描前面的100020行,再丢弃100000行。数据量越大,越靠后翻页,查询越慢。一种优化思路是把LIMIT的起始位置换成"上次返回的最后一条记录的ID",即 WHERE id > 上一页最大id ORDER BY id LIMIT 20,这种写法利用主键索引直接定位,不用扫描被丢弃的行,翻页越深优势越明显。另一种思路是用子查询先查ID再回表,比如 SELECT * FROM t WHERE id IN (SELECT id FROM t WHERE 条件 ORDER BY id LIMIT 100000, 20),虽然子查询也需要扫描,但比全行回表要轻量得多。

4. 事务与锁:并发场景下CRUD必须跨过的一道坎

4.1 事务与自动提交:每条CRUD都在事务里,只是你没觉察

很多人以为只有显式写了BEGIN或START TRANSACTION才算开启事务,这是个误解。MySQL默认的autocommit是开启的,意味着每一条INSERT、UPDATE、DELETE都会自动包装成一个事务并立即提交。如果业务场景是"先更新A表再更新B表,中途B表失败要回滚A表",不关掉自动提交就没法保证一致性。

显式事务的推荐写法是:SET autocommit = 0 或直接 START TRANSACTION,业务逻辑执行完再COMMIT,出错就ROLLBACK。这里有一个容易被忽略的细节:事务开启后,即使只是执行一条普通的SELECT,也可能产生一致性快照和锁的副作用,特别是在可重复读隔离级别下,事务过长会导致undo log无法清理。所以事务应该尽量短,做到"快开快提交",绝不允许在事务里做网络请求、文件读写这种耗时操作。

4.2 隔离级别与锁的分类:一张表看懂它们的关系

InnoDB默认的隔离级别是REPEATABLE READ(可重复读),这也是MySQL和很多其他数据库不太一样的地方。四种隔离级别分别是读未提交(READ UNCOMMITTED)、读已提交(READ COMMITTED)、可重复读(REPEATABLE READ)、串行化(SERIALIZABLE)。它们解决的并发问题各有侧重:

隔离级别脏读不可重复读幻读典型使用场景
READ UNCOMMITTED存在存在存在极少使用,几乎不推荐
READ COMMITTED解决存在存在大多数业务读场景足够
REPEATABLE READ(默认)解决解决大部分解决InnoDB默认,常用
SERIALIZABLE完全解决完全解决完全解决对一致性要求极高的场景

很多人把脏读、不可重复读和幻读搞混。脏读是读到别人未提交的数据;不可重复读是在同一个事务里两次查询同一行,结果不一样,因为别的事务提交了对这行的UPDATE;幻读是在同一个事务里两次范围查询,结果集条数不一样,因为别的事务插入了新行。InnoDB在可重复读隔离级别下通过MVCC解决了大部分幻读问题,但它对"当前读"(SELECT ... FOR UPDATE)场景仍然需要间隙锁来防幻读。

锁的分类这个话题在热搜里很火,其实核心就那几类。按模式分:共享锁(读锁)和排他锁(写锁)。按粒度分:行锁、表锁、间隙锁。按具体实现分:记录锁(锁定单条索引记录)、间隙锁(锁定一个索引区间但不包括记录本身)、临键锁(记录锁+间隙锁的组合,锁定区间及边界记录)。日常开发里不需要背这些术语,但要知道一个判断标准:WHERE条件走唯一索引一般只锁目标行;条件走普通索引或没索引,很可能会锁区间甚至锁全表。这就是为什么我总是强调——WHERE条件务必命中索引。

4.3 经典库存扣减:一条UPDATE引发的并发事故

讲一个开发面试里常考、线上也常出的场景:库存扣减。假设商品库存表里有一行 stock=100,两个用户同时下单,都执行 UPDATE stock SET stock=stock-1 WHERE id=1。在单条UPDATE自动提交的情况下,InnoDB会给这行记录加排他锁,第二条UPDATE会等待第一条提交后继续执行,所以两个用户分别把库存扣到99和98,数据是对的。

隐患出在"先SELECT后UPDATE"的写法上。如果业务先 SELECT stock FROM goods WHERE id=1,判断stock > 0,再执行UPDATE减库存,两个请求可能同时读到stock=100,都判定库存充足,然后都执行减1,最终结果也可能是99而不是98。这就是经典的并发超卖问题。解决办法有两条路:一是直接UPDATE语句里带上条件 UPDATE stock SET stock=stock-1 WHERE id=1 AND stock>0,让数据库原子地判断并更新;二是使用乐观锁,给表加version字段,UPDATE时带上版本号, UPDATE stock SET stock=stock-1, version=version+1 WHERE id=1 AND version=旧版本号,affected rows为0就说明并发冲突,需要重试。

顺带说明:UPDATE语句里的"stock=stock-1"是原子性的,不需要先查再算。很多人下意识先SELECT出来在应用层减好再UPDATE回去,这既多了一次网络往返,也引入了竞态窗口。能把计算交给MySQL的,就不要拿回应用层算。

5. 误操作场景还原:UPDATE不带WHERE的完整恢复链路

5.1 事故回顾:我的第一次全表UPDATE翻车

热搜词里有一项"mysql update 还原",我看一次就回忆一次当年的翻车事件。那是一个测试环境转生产环境的前夜,我要把某个用户的状态字段从1改成2,SQL写得很顺: UPDATE user SET status=2 WHERE id=123。结果在终端里敲命令时,WHERE那一行忘了粘贴,回车之后MySQL提示"Query OK, 90000 rows affected"。我盯着那个90000愣了几秒才反应过来——全表的用户状态都被改成2了。

幸运的是当时我上一条操作只是改了一行数据,误操作发生后没继续跑其他写操作。这让我有机会用两种手段把数据救回来。第一种是事务回滚,但那一次是在autocommit模式下执行的,无法直接ROLLBACK。第二种是走binlog,这是生产环境最通用的恢复手段。我当时的MySQL开了binlog,并且日志格式是ROW,这直接决定了能不能还原出精确到行的旧值。

5.2 binlog解析:误删误更新的标准恢复链路

要恢复一条误执行的UPDATE,前提条件是:binlog开启、日志格式为ROW、误操作发生前有完整的数据基础(备份或全量binlog)。检查是否开启的方法很简单: SHOW VARIABLES LIKE 'log_bin'; 如果结果是ON,继续查 SHOW VARIABLES LIKE 'binlog_format';。ROW格式的binlog会把每行变更前的值(before_image)和变更后的值(after_image)都记录下来,这让我们具备精确还原能力。

恢复思路分四步走:

  1. 找到误操作对应的时间点。用 mysqlbinlog 工具解析日志文件,配合 --start-datetime 和 --stop-datetime 参数圈出时间窗口。如果是线上大日志文件,建议先拷贝到本地再解析,避免在生产库上跑工具。

  2. 精确定位问题SQL。解析出的binlog内容里,能看到原生的SQL语句,也能看到ROW模式下的具体变更行。定位到执行那条UPDATE的GTID或日志位置编号,记录下它前后的pos位置。

  3. 生成反向SQL。ROW模式下,mysqlbinlog 加 --flashback 参数(如果是开源工具)可以生成反向SQL,把"UPDATE的after值"改回"before值"。如果没有flashback工具,手动把解析结果里每行的before_image整理成UPDATE语句也可以,但几千行数据会做到崩溃,所以生产环境一定要有binlog2sql这类工具。

  4. 小范围重放验证。先在测试库上执行反向SQL,比对目标行数据是否回到事故前状态,确认无误后再到生产库执行。执行之前老规矩:先备份一次当前状态。

5.3 比恢复更重要的:怎么让误操作不发生

恢复手段终究是亡羊补牢,真正能救命的是一套日常防线。我现在的习惯是永远遵守以下几条:生产环境账号不授予不带WHERE的UPDATE和DELETE权限,这需要从账号体系上卡;所有UPDATE和DELETE语句执行前,先跑一遍同样的WHERE条件做SELECT,确认影响行数;计划中的批量修改,必须先 SELECT COUNT(*) 看影响行数,再用事务包裹,先用ROLLBACK测试一次,确认无误再COMMIT;线上大表写操作务必放在低峰期,避免锁等待拖垮在线业务。

还有一条很容易被忽略:临时表和测试库的操作习惯会带进生产。有些人习惯在本地随便跑UPDATE不带WHERE,到了生产环境也顺手这么敲。我的做法是开发环境用客户端工具带"安全模式",或者干脆配置成强提醒,让工具在DELETE和UPDATE不带WHERE时弹确认框,多一道确认就多一分安全。

6. 索引、连接池与批量写:给CRUD提速的落地方案

6.1 EXPLAIN:每个写SQL的人都该养成的肌肉记忆

围绕CRUD的性能调优,第一件事永远是学会看EXPLAIN。格式很简单:在SELECT前面加EXPLAIN关键字,MySQL会返回一行执行计划。核心看四个字段:type、key、rows、Extra。

type列体现访问类型,从好到差依次是system、const、eq_ref、ref、range、index、ALL。看到ALL就说明全表扫描,多半该加索引。key列表示实际用到的索引,如果为NULL,说明这条查询没吃到任何索引。rows列是估算扫描行数,值越小越好。Extra列里如果出现Using filesort或Using temporary,说明排序或分组没走索引,存在优化空间。

我见过太多开发同学从来不执行EXPLAIN,凭感觉优化SQL,这很不科学。索引设计和SQL优化不是玄学,MySQL的执行计划会直接告诉你它打算怎么跑。给新同事惯用的方法:凡是超过100毫秒的查询,一律EXPLAIN拉出来看,慢的原因通常一眼就能定位。

6.2 索引设计:别把索引当成万能药

索引是CRUD性能的最强加速器,但也是双刃剑。每次INSERT和UPDATE都要同步维护索引,索引越多,写放大越严重。表上字段就那几个,很多人上来就给每个字段单建一个索引,结果一张表6个索引,写入性能惨不忍睹,查询优化器还可能在多个索引之间挑错。

设计索引的经验原则:优先给WHERE条件里的字段建索引;联合索引遵循最左前缀原则,比如索引(a,b,c),查询条件里带a可以命中,带a和b也可以命中,只带b或者c就命中不了;对ORDER BY和GROUP BY的字段建索引,能省掉排序;区分度低到离谱的字段(比如性别只有男女)不适合单独建索引,走全表扫描反而更快;不要在频繁更新的字段上建索引,更新索引的代价比你省下的查询时间还大。

还有一个容易被忽略但实际收益很高的技巧:用覆盖索引消除回表。比如 SELECT name FROM user WHERE status=1,如果联合索引是(status, name),查询用到的字段全部在索引里,MySQL直接索引返回,不需要回表查数据行,Extra列会显示Using index,速度会有数量级提升。

6.3 连接池与批量写:应用层的CRUD性能关键点

这一节回到应用视角。热搜词里的"mysql的数据库连接池",其实是每个做后端开发的人迟早要面对的问题。MySQL建立连接的成本很高,握手、鉴权、初始化会话都要耗时,所以生产环境不会用"每次请求新建连接"的方式,而是通过连接池复用连接。

常用的连接池参数有几个核心项:initialSize(初始连接数)、maxActive(最大连接数)、maxWait(获取连接的超时时间)、minIdle(最小空闲连接数)。常规经验:单机应用maxActive设置在20到50之间,不要贪多,数据库连接太多反而会把MySQL的连接数打满;maxWait一定要设置,否则高并发下请求会无限等待池里的连接;如果查询都是毫秒级,连接池大小其实不需要太大,真正需要大连接池的场景是慢SQL太多。

另外,批量写对连接池的影响常被忽略。如果你把1万条数据分成1000次单条INSERT循环执行,即使有连接池,这1000次操作仍然要反复获取和释放连接、发送SQL、等待响应,网络开销极其可观。用我第2节说的多值INSERT批量写,或者准备语句复用,能把连接和解析的开销摊薄到接近零。

从连接池再往外延伸一点:如果项目长期碰到连接不够用,不要急着加连接数,先查是不是有连接泄漏。最常见的泄漏点就是异常分支里没关闭PreparedStatement或ResultSet,导致连接归还不了。这属于CRUD代码层面的基本功,排查方法很简单:监控连接池activeCount是否持续居高不下,配合线程池看哪个业务线程长时间持有连接。

7. 一些只会在实战里学到的经验规则

最后分享几条我在实际项目里沉淀下来的经验,这些内容在官方文档里找不到,但每一条都来自实打实的教训。

第一,CRUD语句的统一入口比想象中重要。团队项目里如果每个人都按自己的风格写SQL,有的用JOIN,有的用IN,有的喜欢子查询,后期排查问题和做性能调优会非常痛苦。我们内部的做法是让所有查询走数据访问层统一封装的接口,不直接在业务代码里拼SQL,同时约定INSERT和UPDATE操作必须显式列出字段名,不允许用 INSERT INTO t VALUES (...),这样即使表结构变更,也能通过编译或接口约束第一时间发现。

第二,不要轻易相信"测试环境没问题"这句话。测试库的数据量通常只有生产环境的百分之一,索引失效和深分页问题在测试阶段几乎不会暴露,等上了生产直接把生产库打挂。如果你负责的查询可能要跑在百万行以上的表上,哪怕测试环境一切正常,也要自觉加索引、看EXPLAIN、考虑分批处理。

第三,定期做数据备份和对账演练。binlog恢复链路平时不演练,真出事的时候大概率手忙脚乱。我给团队定的规矩是每季度挑一张核心表做一次"模拟误删+恢复"的演练,把从备份恢复到binlog还原的完整流程跑一遍,这样到了真正的凌晨事故现场,才不会对着日志文件手足无措。

第四,关于学习路径,我一直觉得MySQL的四个操作就是一面镜子——你写的每条SELECT、UPDATE、DELETE,都在跟索引、锁、事务这些底层机制打交道。能把CRUD用明白的人,后面学存储过程、学性能调优、学高可用架构,地基都是稳的;反之,一上来就追着"存储过程怎么写""锁的分类有几种"背概念的人,往往遇到线上问题还是不知道怎么下手。

先写到这里。这些内容如果对你手头的项目有帮助,建议打开终端把今天聊到的EXPLAIN、事务、binlog相关的命令都亲手跑一遍。MySQL这个东西,看十篇文章不如自己动手敲一次,数据恢复手段更是只有练过才有底气。

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

遥感图像识别的四模并举:KNN/SVM/CNN/LSTM分层验证框架

简介&#xff1a;本资源是一套面向计算机专业本科生与AI初学者的遥感图像识别综合实践项目&#xff0c;聚焦课程设计、期末大作业及算法对比学习需求&#xff0c;完整实现KNN、SVM、CNN与LSTM四种主流模型在遥感图像分类任务中的建模、训练与评估全流程。压缩包共33个文件&…

作者头像 李华
网站建设 2026/10/3 9:23:48

React Native混合开发实战:存量Android应用集成与踩坑指南

1. 内容整体设计与思路拆解1.1 为什么要在存量原生应用里引入React Native最近一年多&#xff0c;我一直在维护一个已经上线三年多的Android原生应用&#xff0c;日活大概几十万规模。业务方的需求越来越离谱&#xff0c;从“下周上线一个新活动页”发展到“今天下午能不能先出…

作者头像 李华
网站建设 2026/10/3 9:23:46

基于Python的Boss直聘数据可视化分析系统实践

简介&#xff1a;一份基于 Python 的 Boss 直聘数据可视化分析系统源码&#xff0c;面向正在准备期末大作业、毕业设计或想提升数据综合能力的高校学生&#xff0c;解决招聘岗位数据从爬取、清洗到分析、展示的完整链路问题。压缩包仅 426KB&#xff0c;共 26 个文件&#xff0…

作者头像 李华
网站建设 2026/10/3 9:23:45

基于Neo4j与Flask的电影知识图谱问答系统实战:从爬虫到小程序全链路

简介&#xff1a;这是一套面向计算机相关专业毕业设计与课程设计场景的知识图谱电影问答系统完整项目源码&#xff0c;基于Python与Neo4j构建&#xff0c;适合正在准备毕设、需要项目实战练习的学生参考使用。项目采用前后端分离结构&#xff0c;后端以Flask搭建问答服务&#…

作者头像 李华
网站建设 2026/10/3 9:22:49

DEMON谱分析:从舰船辐射噪声中提取轴频的完整实践

简介&#xff1a;在复杂海洋环境下&#xff0c;水声信号常呈现非平稳、多调制特征&#xff0c;Demon谱分析作为一种经典的包络解调方法&#xff0c;能够在强噪声背景下剥离包络结构&#xff0c;是水下目标识别和声源特征提取的重要手段。这份以Demon谱分析为核心的仿真资源&…

作者头像 李华
网站建设 2026/10/3 9:22:49

本地知识库问答新方案:Ollama+Neo4j搭建GraphRAG系统

做本地知识库问答最尴尬的场景&#xff0c;不是模型效果不够好&#xff0c;而是你辛辛苦苦搭好了一套 RAG 流水线&#xff0c;结果问它“A 和 B 之间是什么关系”这类问题&#xff0c;它只能甩给你两段语义相近但互不关联的文本片段。传统向量检索擅长找相似段落&#xff0c;却…

作者头像 李华