☰
MySQL进阶:存储引擎、索引与触发器实战解析
2026/10/19 11:46:37 网站建设 项目流程

1. 内容整体设计与思路拆解

1.1 为什么DAY09要同时啃存储引擎、索引、触发器三块硬骨头

很多学MySQL的朋友,学到第九天会卡在一个尴尬的位置:单表增删改查已经很熟了,JOIN也凑合能写,但一面试、一接触真实业务,就被问到“你的表用什么引擎”“这个SQL为什么这么慢”“订单表怎么自动记录日志”这类问题。DAY09把这三块放在一起,其实是MySQL从“会写SQL”走向“会设计数据”的必经门槛,三者之间是相互关联的。

先说引擎,MySQL里最常用的两个引擎是InnoDB和MyISAM。我第一次接触时也懵,不知道它们是干什么的。简单说,存储引擎就是MySQL底层如何存储数据、如何处理并发、如何支持事务的一套“插件式”方案。同一个MySQL实例,可以让A表用InnoDB、B表用MyISAM,灵活归灵活,但选错了就是深坑。

索引就更不用说了,它是SQL查询提速的核心。为什么有些查询几百毫秒,加了索引之后变成几毫秒?因为索引本质上就是给数据建的“目录”。MySQL里最常见的B+树索引,能把几百万条记录的查找从全表扫一遍,变成走树查到位置,效率完全不在一个数量级。

触发器呢?它属于“自动化”能力,它允许你在表上定义INSERT、UPDATE、DELETE操作前后,自动执行一段SQL。想象一个场景:每当用户下单后就自动往日志表插一条记录,如果没有触发器,你得在业务代码里两个地方分别写插入逻辑,用触发器就可以在数据库层完成这件事。

把三块放一天学还有一个好处:它们本质上都和“性能与自动化”有关。引擎影响索引的底层实现方式(InnoDB的数据和索引一起存、MyISAM则是分开存),触发器又依赖表的引擎支持(MyISAM是不支持触发器的,而且InnoDB的索引结构会直接影响触发器的执行效率)。换句话说,这三者是一个知识环,拆开学反而容易产生理解断层。

所以这篇学习日记,我会用自己实际动手的过程,把每一块掰开揉碎,配上创建语句、参数解释、踩坑记录,尽量让看这篇日记的人,看完就能在自己电脑上复现出来,而不是只能干瞪眼看概念。

1.2 这套知识适合什么人,学完能解决什么实际问题

说实话,DAY09这个位置非常关键。适合的人群主要是三类:

第一类是刚学完基础SQL、正打算往“数据库设计”方向走的学习者。这类朋友最需要搞懂“为什么我的表越查越慢”,学了索引你就能从执行计划里找到答案。

第二类是正在准备面试的开发者。存储引擎的对比、索引失效场景、触发器的作用与限制几乎是数据库高频出题点。Day09把概念和应用场景结合起来,比背八股文扎实得多。

第三类是已经在写简单CRUD、但从未深入优化过数据库的初级后端。这类朋友常犯的毛病是给所有查询列都建索引、或者在频繁更新的表上滥用触发器,结果插入性能惨不忍睹。学完这一天的内容,起码能避开这些常见的低级问题。

学完之后能解决的实际问题也很具体:给慢查询表设计出合适的索引组合;在面试里能讲清楚为什么一定选InnoDB而不选MyISAM;在需要审计或级联操作时能用触发器实现自动化,而不是写一坨容易漏的执行代码。这些能力,都是在真实项目中马上就能用上的。

2. 存储引擎:选错引擎的表,上线第一天就可能被锁死

2.1 InnoDB与MyISAM的核心差异,以及怎么选

先说结论:日常项目里,绝大多数表都应该用InnoDB,尤其涉及订单、账户、日志审计这类要求数据一致性的场景;只有在读多写少、不做事务、数据也不需要恢复的特定场景(比如纯数据仓库里的维度表)才考虑MyISAM。

我实际测试过两种引擎在并发写入和事务回滚上的表现。MyISAM是表级锁定的,执行UPDATE时会把整张表锁住,其他会话的SELECT都会被阻塞,在那个场景下并发一上来,页面直接卡成狗。InnoDB用的是行级锁,多个会话更新不同的行相互不干扰,这在电商、论坛等场景下差距特别明显。

一个很关键的判断标准是:如果你的表没有事务需求,而且几乎只有查询操作(比如某些统计类中间表),MyISAM在读性能上确实有一定优势,因为它的索引与数据分离、.MYD文件顺序读对大范围扫描友好。但代价是崩溃恢复能力极差,断电后很容易出现表损坏,需要repair table修复。相比之下,InnoDB有redo log和doublewrite缓冲,崩溃后能做到自动恢复,数据安全性高很多。

我之前在公司给一个报表功能建中间表的时候,图省事用了MyISAM,结果某个凌晨任务跑挂了,第二天检查发现其中一张表标了crashed,花了半小时用备份才恢复。之后我就宁可多花点磁盘空间,也全选InnoDB。

2.2 查看与修改引擎的实操命令

当你接手一个数据库,搞不清某张表用的什么引擎,直接执行:

SHOW TABLE STATUS WHERE Name = 'your_table_name';

结果里有一个Engine字段,显示当前表的存储引擎。想一次看数据库中所有表,可以直接看系统表:

SELECT table_name, engine FROM information_schema.tables WHERE table_schema = '你的库名';

新建表时指定引擎很简单:

CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, nickname VARCHAR(50) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;

修改已有表引擎的语法:

ALTER TABLE t_user ENGINE = MyISAM;

如果碰到“整表加索引特别慢、锁等待频繁”的问题,也可以考虑用ALTER TABLE把引擎切到InnoDB,配合在线DDL特性,很多操作不会锁死读写,这点也是InnoDB远比旧版MyISAM好用的原因之一。

2.3 InnoDB所以然:事务、行锁、外键和MVCC的简单理解

InnoDB支持ACID事务,这是我们选它的最大理由。MySQL中事务的典型使用场景是银行转账:A账户扣钱、B账户加钱,作为一个原子操作,要么都成功要么都回滚。MyISAM强行做的话需要应用层写大量补偿逻辑,还容易出漏。

InnoDB的行级锁不是魔法,它依赖索引实现的:如果更新的行无法命中索引,InnoDB会退化为锁全表。我之前遇到过一个线上慢SQL,UPDATE语句的WHERE条件是一个未加索引的普通字段,导致一个很小的更新把所有行锁住,其他请求全部排队。后来给该字段加了索引,锁的范围立刻缩小,并发问题解决。学索引之前先记住这句话:InnoDB锁的是索引记录。

MVCC(多版本并发控制)是InnoDB实现高并发读的核心。简单理解,就是同一行数据在并发环境下会存在多个“历史版本”,读操作会拿到一个符合当前事务隔离级别的快照,不用阻塞写入。这也是为什么高并发读取通常不需要在SQL里强制加锁的底层原因。

2.4 引擎选择的避坑清单

如果让我总结几条真实的经验:

  • 不要迷信MyISAM的查询快,那是建立在没有崩溃恢复的前提上。生产环境数据重要,一点故障都可能让你熬夜恢复数据。
  • 如果业务里有“写多读少”的日志表,优先考虑InnoDB。配合行锁和批量插入,性能不差,还能保证一致性。
  • 老生常谈但值得重复的:外键约束只有InnoDB支持。需要物理外键的场景(虽然很多公司禁用物理外键,但某些内部系统仍需要)必须用InnoDB。
  • 把Compatibility相关的系统变量搞清楚,比如sql_mode。引擎切换时,老表里某些字段类型兼容性可能出问题。

3. 索引:别急着所有字段都加索引,先弄懂B+树和索引失效

3.1 索引为什么快:从目录到B+树的类比

如果要给小白讲索引是什么,我会说它和书的目录一样:没有目录,你要一页页翻;有了目录,你先跳到章节页,再翻到具体位置。在MySQL里,一页页翻就是全表扫描,走索引是沿着B+树的分支快速定位。

B+树有几个关键特性:非叶子节点只存索引键,不存数据,所以每一层能容纳大量键值,树高很小(3层左右就能撑起上百万条记录);叶子节点按顺序排列,并且通过链表串联在一起,适合范围查询。比如你WHERE age > 18 AND age < 30,MySQL会从根节点一路定位到叶子链表,然后顺序扫描直到越界。

B+树相对于哈希索引的优势也在这里:哈希索引只能做等值查询,做不了范围查询。MySQL的Memory引擎默认哈希索引,但常规InnoDB表默认就是B+树,实用度最高。

3.2 最常用索引类型与创建语句

我们实际建索引时,主要有以下几类。

主键索引(PRIMARY KEY):一张表只能有一个,InnoDB会自动用主键组织数据(聚簇索引)。如果没有主键,InnoDB会生成隐藏主键,但性能不如显式主键。

唯一索引(UNIQUE):保证列或列组合值唯一,可以允许NULL(NULL不算重复)。典型用于手机号、邮箱这些唯一业务字段。

普通索引(INDEX):只加速查询,不限制值唯一性。

复合索引:也就是联合索引,多个字段组合成一个索引。比如INDEX (class_id, student_name)。它有“最左前缀”原则——查询条件中必须出现最左字段,否则索引可能失效。

创建索引的SQL如下:

ALTER TABLE t_user ADD INDEX idx_nickname (nickname); ALTER TABLE t_user ADD UNIQUE INDEX uk_mobile (mobile); CREATE INDEX idx_class_name ON t_student (class_id, student_name);

查看一个表的索引:

SHOW INDEX FROM t_user;

3.3 索引失效的经典场景与排查方法

光会建索引不够,很多朋友建了索引,发现SQL照样慢,查执行计划一看,type是ALL(全表扫描),索引压根没用上。我把自己实战里容易踩的坑列在这里。

第一个坑:对索引列做了函数运算或隐式类型转换。比如:

SELECT * FROM t_user WHERE DATE(create_time) = '2024-12-01'; SELECT * FROM t_user WHERE age = '18'; -- age是数值列,传入字符串

DATE()函数包住了索引列,MySQL没法直接按索引有序查找,只能全扫。解决思路是改写范围条件,或者别在列上套函数。

第二个坑:LIKE以通配符开头。WHERE name LIKE '%张'因为没法从索引最左侧匹配,会失效;而LIKE '张%'则能用索引。

第三个坑:联合索引没有使用最左前缀。我给class_id和student_name建了联合索引,但查询条件是WHERE student_name = '张三',MySQL没法利用这个联合索引,因为最左边的字段没参与条件。

排查索引是否生效,最直接的方法是执行:

EXPLAIN SELECT * FROM t_user WHERE nickname = 'admin';

看输出里的type字段,如果从ALL变成ref或range,说明索引生效了;再看key字段,确认实际用的索引名是不是预期的那一个。

3.4 索引太多反而是负优化

一句话:索引不是越多越好。每建一个索引,写操作(INSERT/UPDATE/DELETE)需要额外维护B+树的叶子节点和中间节点,写入性能会下降。同时占磁盘空间。

我曾经在一张线上高频更新的订单表上犯了错误:为了各种查询方便,一口气加了六个索引,结果业务高峰时写库的平均耗时几乎翻了一倍,后来删掉三个不常用的索引,写入速度才恢复。之后我一直提醒自己:先测量再优化,索引针对最常出现的慢查询加,不要“预防性”地给所有字段组合都加上索引。

3.5 关于回表、覆盖索引的进一步理解

InnoDB 的索引分成聚簇索引和二级索引。聚簇索引的叶子节点直接存整行数据,所以WHERE id = 1查主键时数据就在索引里,一步到位。二级索引的叶子节点存的是主键值,不是整行数据。

因此,走二级索引查数据时,先查二级索引得到主键,再回到聚簇索引里找整行,这个动作叫“回表”。如果能避免回表,查询会更快,这就是“覆盖索引”的价值:你要查询的列全部包含在某个索引里,就不用回表了。

比如有索引(nickname, age),执行:

SELECT age FROM t_user WHERE nickname = 'admin';

所需的age和nickname都在二级索引里,MySQL直接返回,免去回表消耗。但如果SELECT * 或查一个没在索引内的字段,就非要回表不可。实战中给高频查询设计“覆盖索引”是一个很好的调优手段。

3.6 什么时候该考虑用全文索引和空间索引

当你在文章正文、长文本字段里做模糊搜索时,普通的LIKE '%关键词%'在数据量大时性能很差。MySQL自带的全文索引(FULLTEXT)可以对这类文本做分词检索,性能和效果比模糊查询好很多。注意,InnoDB从较新的版本开始也支持全文索引了,但中文分词方面最好借助专门的中文分词工具或外部的搜索引擎方案。

空间索引(SPATIAL)很少用到,一般只在地理位置系统中使用,基于几何类型字段建立。对于大多数业务系统,先不用关注。

4. 触发器:让数据库在背后自动干活,但别让它变成定时炸弹

4.1 触发器是什么,以及它的逻辑架构

触发器是一段与表关联的SQL程序,在表上执行INSERT、UPDATE、DELETE时自动触发。MySQL的触发器有六个触发时机:BEFORE INSERT、AFTER INSERT、BEFORE UPDATE、AFTER UPDATE、BEFORE DELETE、AFTER DELETE。

也就是说,你可以在“数据变更之前”拦截并修改要插入的值,也可以在“数据变更之后”去同步另一张表。

最经典的例子是日志审计:往用户表里更新了一个人的手机号,如果你想记录“谁在什么时候把旧值改成了新值”,完全可以在业务代码里额外写一条日志插入逻辑,但那样容易漏。用触发器,每次UPDATE后自动往日志表写入数据,业务代码不用任何改动。

4.2 动手实操:一个用户表日志触发器

假设我有两张表:

CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, nickname VARCHAR(50), mobile VARCHAR(20) ); CREATE TABLE t_user_log ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT, old_mobile VARCHAR(20), new_mobile VARCHAR(20), changed_at DATETIME );

我想实现“修改用户手机号时,自动记录旧值和新值到日志表”。创建触发器:

DELIMITER // CREATE TRIGGER trg_user_mobile_update AFTER UPDATE ON t_user FOR EACH ROW BEGIN INSERT INTO t_user_log(user_id, old_mobile, new_mobile, changed_at) VALUES (NEW.id, OLD.mobile, NEW.mobile, NOW()); END// DELIMITER ;

这里有个容易踩的基础坑:MySQL的命令行客户端默认用分号作为语句结束符。存储过程、函数、触发器这类BEGIN...END内部有多条语句时,需要临时改掉默认结束符,否则客户端会在第一个分号处误认为SQL已结束,导致结构错乱。所以上面用了DELIMITER //和//结束。很多新手第一次写触发器报语法错误,多半是没注意这一点。

创建完触发器后,试一下:

UPDATE t_user SET mobile = '13800001111' WHERE id = 1;

然后查询:

SELECT * FROM t_user_log;

能看到记录已经自动写入,整个过程业务层完全无感知。

查看触发器定义:

SHOW TRIGGERS;

也可以从系统表查:

SELECT * FROM information_schema.TRIGGERS WHERE TRIGGER_NAME = 'trg_user_mobile_update';

4.3 触发器里的NEW与OLD到底怎么用

这俩是触发器里最难绕开的伪行记录。

  • INSERT触发器中只有NEW,表示将要插入或刚插入的新数据行。
  • DELETE触发器中只有OLD,表示即将被删除的旧数据行。
  • UPDATE触发器中NEW和OLD都有,NEW是更新后的值,OLD是更新前的值。

在BEFORE INSERT触发器中,可以通过SET NEW.mobile = '123'直接改变即将写入的值,这是一种常见的“入库前自动清洗”方式。比如统一把手机号中的空格去掉:

CREATE TRIGGER trg_user_before_insert BEFORE INSERT ON t_user FOR EACH ROW SET NEW.mobile = REPLACE(NEW.mobile, ' ', '');

注意:在AFTER触发器中,数据变更已经发生,你再改NEW的值也没用,不会有任何效果。

4.4 触发器的限制与应用禁忌

触发器看上去很美好,但实际生产环境里要非常克制地使用。

第一个限制是性能开销:触发器执行一次,相当于隐式多执行了一次或多次SQL。如果一张高频表上加了好几个触发器,每个更新都多耗几倍数据库资源,很容易拖慢写入链路。我见过有人在一张每天写入百万记录的日志表上加AFTER INSERT触发器,结果触发器内部还做了一个全表统计,把插入线程直接堵死。

第二个限制是维护成本:触发器分散在数据库层,业务团队改代码时很容易忽略它们的存在。一旦上游表结构变化,或者批量修复数据时,触发器可能会意外执行,制造不明来路的脏数据。因此在做数据修复之前,建议先暂时禁用或删除无用的触发器,修完再重建。

第三个限制是嵌套与递归:MySQL默认不支持存储过程函数那种大规模递归调用触发器,如果触发器内部再操作同一张表,可能导致递归循环,有最大递归深度限制。复杂业务不建议用触发器,宁可放到应用层的事件脚本里去处理。

第四个限制是日志类场景里,触发器写的日志会影响原表的事务提交时间,造成主从复制延迟。因为复制SQL在从库执行时也会触发同样的触发器,这不是双倍消耗吗?所以大型高并发项目里,审计日志通常会由代码层显式写入,而不是依赖数据库触发器。

4.5 删除与修改触发器

删除:

DROP TRIGGER IF EXISTS trg_user_mobile_update;

注意:MySQL没有ALTER TRIGGER这种直接改触发器的语法,要修改只能先DROP再重新CREATE。所以在迭代开发中最好把触发器创建语句固化成SQL脚本,像项目代码一样纳入版本管理,不然数据库迁移时很容易漏。

4.6 触发器的替代方案:什么时候用事件、什么时候放代码层

MySQL有事件调度器(EVENT),可以定周期执行任务,比如“每天凌晨清理过期数据”。它和触发器的区别在于:事件按时间触发,触发器按数据变更触发。不要在两者之间纠结谁更好,它们解决的是不同问题。

真正需要斟酌的,是数据库触发器与业务代码二选一的场景。比如“下单后同步库存快照”。你可以在数据库触发器里做,但业务逻辑复杂时数据库层难以维护,应用层反而更清晰。我个人的建议是:简单、稳定、跨模块的同步(比如审计日志、敏感字段变更记录)可以放触发器;涉及复杂业务判断、第三方接口调用、状态机转换的,就老老实实写代码。

5. 三者的联动:一个实际项目场景里的组合运用

5.1 场景描述:一个简单的商城订单库

为了把当天学的知识串起来,我在电脑上模拟了一个迷你商城库。

现有四张表:商品表、用户表、订单表、订单日志表。

  • 商品表:存商品基础信息和库存数量。
  • 用户表:存用户昵称、手机号、账户余额。
  • 订单表:存下单记录,包括用户ID、商品ID、订单金额、订单状态。
  • 订单日志表:记录订单创建、状态变更的操作痕迹。

在这个场景里,我把存储引擎选型得很明确:所有业务表都用InnoDB,因为涉及金额、库存、状态,数据一致性极其重要。这刚好用到第2节讲的InnoDB事务、行锁、崩溃恢复能力。

5.2 索引设计:按真实查询语句逐个分析

模拟查询一:查某用户最近订单,按创建时间倒序。

SELECT * FROM t_order WHERE user_id = 100 ORDER BY created_at DESC;

这个查询主条件是user_id,排序条件是created_at,所以建一个联合索引:

ALTER TABLE t_order ADD INDEX idx_user_created (user_id, created_at);

为什么不用单列(user_id)索引?因为联合索引的叶子节点同时维护了user_id和created_at的顺序,对于同一用户的记录,created_at在索引上已经是有序的,ORDER BY不额外排序。这是“索引即排序”的思路。

模拟查询二:查某商品在某个时间段内的订单量。

SELECT COUNT(*) FROM t_order WHERE product_id = 22 AND created_at BETWEEN '2024-12-01' AND '2024-12-05';

同样建联合索引:

ALTER TABLE t_order ADD INDEX idx_product_created (product_id, created_at);

如果商品ID本身区分度不够高(比如就那么十几个商品),索引即使创建了可能也不一定有用,MySQL优化器会自己判断。这提醒了我:索引价值的核心在于列的区分度,区分度太差的列,索引收益很低,不如不做。

5.3 触发器:自动写入订单变更日志

在订单表上创建两个触发器:一个在INSERT后写“订单创建”日志,一个在UPDATE后写“状态变更”日志。

DELIMITER // CREATE TRIGGER trg_order_after_insert AFTER INSERT ON t_order FOR EACH ROW BEGIN INSERT INTO t_order_log(order_id, log_type, old_status, new_status, log_time) VALUES (NEW.id, 'CREATE', NULL, NEW.order_status, NOW()); END// CREATE TRIGGER trg_order_after_update AFTER UPDATE ON t_order FOR EACH ROW BEGIN IF OLD.order_status <> NEW.order_status THEN INSERT INTO t_order_log(order_id, log_type, old_status, new_status, log_time) VALUES (NEW.id, 'UPDATE', OLD.order_status, NEW.order_status, NOW()); END IF; END// DELIMITER ;

为什么在UPDATE触发器里要加IF OLD.order_status <> NEW.order_status这个判断?我踩过坑后明白:UPDATE触发器在任意列发生变化时都会触发,哪怕你只更新了一个金额备注、一个收货地址,它也会执行。如果连日志表没有status变更也写一条,日志表会充满无意义的记录。加上判断,只有状态确实变化时才写。这是一个实战经验,文档中不会提醒你。

5.4 这个场景里我验证的三者联动关系

我在执行完所有测试后,特意又检查了一遍索引与触发器的关系:订单日志表用InnoDB,里面也有索引(order_id, log_time),保证按订单ID查日志时走索引;而且日志INSERT写入频繁,InnoDB的行锁机制保证并发插入多个不同订单日志时不会互相阻塞。这就是引擎、索引、触发器三个知识点在一个场景中的实际联动。

5.5 一个容易翻车的点:触发器与批量UPDATE

假设你某天用UPDATE一次更新了上千个订单的状态,触发器会执行一千次,日志表会被写入一千行。这本身没问题,但如果更新语句的执行计划没有走索引,会导致大量行锁竞争,触发器又会放大每行变更的代价。批量更新完以后,务必检查执行计划、观察锁等待和触发器带来的额外写入量。

6. 学习日记的反思与踩坑速查表

6.1 我在DAY09实操中遇到的几个典型问题

问题一:忘记DELIMITER导致触发器创建失败。这个在小白阶段几乎人人都会遇到。错误信息看似是语法错误,实际是客户端把BEGIN块里第一条分号当作结束符了。解决办法就是我前面提到的临时切换DELIMITER,执行完后恢复成默认的分号。

问题二:触发器创建成功,但UPDATE后日志表没有写入。排查时发现是UPDATE没有匹配到任何行,因为WHERE条件写错了。FOR EACH ROW的触发器只在实际受影响的行上触发,没有行被更新就不会触发,这是正常的,别以为是触发器坏了。

问题三:给大表加索引时把数据库卡住了。在一张几百万行的表上执行ALTER TABLE添加索引,默认情况下会锁表,线上业务直接一大波超时。后来我用了在线DDL特性,并选择业务低峰期执行,把影响降到最低。但这仍然是需要运维介入的操作。

问题四:使用EXPLAIN发现走了主键扫描,但与预期不符。有时候是因为数据量太小,MySQL优化器认为扫全表比走索引更快,所以放弃了二级索引。这不是索引失效,是优化器基于统计信息做出的判断。

6.2 常见问题速查表

问题现象可能原因排查指令或处理方法
EXPLAIN显示type=ALL且用不上索引WHERE条件对索引列做了函数或类型转换改写SQL,避免在索引列上套函数,或调整参数类型
联合索引没生效查询条件未遵循最左前缀原则调整查询条件顺序,或重新设计索引字段顺序
触发器创建报语法错误DELIMITER没有正确切换使用DELIMITER //,结束时恢复为DELIMITER ;
UPDATE没写日志WHERE没有匹配到行、触发器被禁用检查受影响行数;SHOW TRIGGERS确认状态
批量更新超慢还锁表触发器和行锁被放大分批UPDATE,并确保WHERE条件命中索引
同一个字段重复加索引没有统一管理索引脚本用SHOW INDEX FROM定期排查,删除冗余索引
表用MyISAM但事务不生效引擎不支持事务改用InnoDB,并验证START TRANSACTION

6.3 给后续学习者的实践建议

DAY09的内容压缩下来就是一句话:数据安全选InnoDB,查询提速靠索引,自动化看触发器。但写文章不能只讲一句话,我特别建议你把今天学到的三个知识点,分别做一个独立的小实验:

实验一:建一张10万行数据的测试表,分别以MyISAM和InnoDB跑同一组查询与更新,记录时间,关注锁等待。

实验二:在同一张表上对比有无索引的查询耗时,并用EXPLAIN记录type字段的变化。

实验三:用触发器做一张审计日志表,然后手动执行几种不同情况的UPDATE,观察日志表里的数据是否符合预期,尤其注意BEFORE与AFTER触发器的区别。

每一个实验做完,记录一到两句心得。等你写到DAY20、DAY30的时候再回头看,这些笔记会非常有价值,因为技术文档可以直接搜到,但个人踩坑日志只有自己留得住。

6.4 一个小技巧:给索引和触发器建立版本管理习惯

这是我吃了亏之后养成的习惯:把所有索引创建语句、触发器创建语句都存成独立的SQL脚本文件,和项目代码一样放入版本管理。你可以这样组织目录:

db/ migrations/ V001__init_tables.sql V002__add_index_on_t_order.sql V003__add_order_triggers.sql

这样做的原因也很简单:日常开发中经常要在多套环境(本机、测试、预发布、生产)执行数据库变更,如果靠手工在命令行敲,很容易出现“测试环境改了,生产环境漏了”的尴尬。固化脚本后,环境同步只需要按序执行脚本,触发器重命名、索引调整都有据可查。

我个人在实际操作中体会最深的一点:存储引擎、索引、触发器这三样东西,如果只看文档,你很难感受到它们的存在价值,但只要一接触真实的数据量、并发的写入、偶发的崩溃恢复,立刻就能理解为什么MySQL的默认配置和最佳实践都指向InnoDB和恰到好处的索引,而触发器则需要克制地使用。希望这篇DAY09日记,能让你少走一点我当时走过的那些弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询