Quick Reference
| Topic | File |
|---|
| Index design deep dive | INLINECODE0 |
| Transactions and locking |
transactions.md |
| Query optimization |
queries.md |
| Production config |
production.md |
Character Set Traps
- -
utf8 is broken—only 3 bytes, can't store emoji; always use INLINECODE5 - INLINECODE6 for case-insensitive sorting;
utf8mb4_bin for exact byte comparison - Collation mismatch in JOINs kills performance—ensure consistent collation across tables
- Connection charset must match:
SET NAMES utf8mb4 or connection string parameter - Index on utf8mb4 column larger—may hit index size limits; consider prefix index
Index Differences from PostgreSQL
- - No partial indexes—can't
WHERE active = true in index definition - No expression indexes until MySQL 8.0.13—must use generated columns before that
- TEXT/BLOB needs prefix length:
INDEX (description(100))—without length, error - No INCLUDE for covering—add columns to index itself:
INDEX (a, b, c) to cover c - Foreign keys auto-indexed only in InnoDB—verify engine before assuming
UPSERT Patterns
- -
INSERT ... ON DUPLICATE KEY UPDATE—not standard SQL; needs unique key conflict - INLINECODE13 for auto-increment—no RETURNING clause like PostgreSQL
- INLINECODE14 deletes then inserts—changes auto-increment ID, triggers DELETE cascade
- Check affected rows: 1 = inserted, 2 = updated (counter-intuitive)
Locking Traps
- -
SELECT ... FOR UPDATE locks rows—but gap locks may lock more than expected - InnoDB uses next-key locking—prevents phantom reads but can cause deadlocks
- Lock wait timeout default 50s—
innodb_lock_wait_timeout for adjustment - INLINECODE17 exists in MySQL 8+—queue pattern
- InnoDB default isolation is REPEATABLE READ, not READ COMMITTED like PostgreSQL
- Deadlocks are expected—code must catch and retry, not just fail
GROUP BY Strictness
- -
sql_mode includes ONLY_FULL_GROUP_BY by default in MySQL 5.7+ - Non-aggregated columns must be in GROUP BY—unlike old MySQL permissive mode
- INLINECODE20 to silence error when you know values are same
- Check sql_mode on legacy databases—may behave differently
InnoDB vs MyISAM
- - Always use InnoDB—transactions, row locking, foreign keys, crash recovery
- MyISAM still default for some system tables—don't use for application data
- Check engine:
SHOW TABLE STATUS—convert with INLINECODE22 - Mixed engines in JOINs work but lose transaction guarantees
Query Quirks
- -
LIMIT offset, count different order than PostgreSQL's INLINECODE24 - INLINECODE25 and
<> both work; prefer <> for SQL standard - No transactional DDL—
ALTER TABLE commits immediately, can't rollback - Boolean is
TINYINT(1)—TRUE/FALSE are just 1/0 - INLINECODE32 instead of
COALESCE for two args—though COALESCE works
Connection Management
- -
wait_timeout kills idle connections—default 8 hours; pooler may not notice - INLINECODE35 default 151—often too low; each uses memory
- Connection pools: don't exceed max_connections across all app instances
- INLINECODE36 to see active connections—kill long-running with INLINECODE37
Replication Awareness
- - Statement-based replication can break with non-deterministic functions—UUID(), NOW()
- Row-based replication safer but more bandwidth—default in MySQL 8
- Read replicas have lag—check
Seconds_Behind_Master before relying on replica reads - Don't write to replica—usually read-only but verify
Performance
- -
EXPLAIN ANALYZE only in MySQL 8.0.18+—older versions just EXPLAIN without actual times - Query cache removed in MySQL 8—don't rely on it; cache at application level
- INLINECODE40 for fragmented tables—locks table; use pt-online-schema-change for big tables
- INLINECODE41 —set to 70-80% of RAM for dedicated DB server
快速参考
| 主题 | 文件 |
|---|
| 索引设计深入探讨 | indexes.md |
| 事务与锁机制 |
transactions.md |
| 查询优化 | queries.md |
| 生产环境配置 | production.md |
字符集陷阱
- - utf8 存在缺陷——仅支持3字节,无法存储表情符号;始终使用 utf8mb4
- utf8mb4unicodeci 用于不区分大小写的排序;utf8mb4_bin 用于精确字节比较
- JOIN 操作中排序规则不匹配会严重影响性能——确保表间排序规则一致
- 连接字符集必须匹配:SET NAMES utf8mb4 或连接字符串参数
- utf8mb4 列上的索引更大——可能触及索引大小限制;考虑使用前缀索引
与 PostgreSQL 的索引差异
- - 不支持部分索引——无法在索引定义中使用 WHERE active = true
- MySQL 8.0.13 之前不支持表达式索引——在此之前必须使用生成列
- TEXT/BLOB 需要前缀长度:INDEX (description(100))——缺少长度会报错
- 不支持覆盖索引的 INCLUDE——将列添加到索引本身:INDEX (a, b, c) 以覆盖 c
- 仅 InnoDB 自动为外键创建索引——在假设前需确认存储引擎
UPSERT 模式
- - INSERT ... ON DUPLICATE KEY UPDATE——非标准 SQL;需要唯一键冲突
- 使用 LASTINSERTID() 获取自增值——没有像 PostgreSQL 那样的 RETURNING 子句
- REPLACE INTO 先删除后插入——会改变自增 ID,触发 DELETE 级联
- 检查受影响行数:1 表示插入,2 表示更新(反直觉)
锁机制陷阱
- - SELECT ... FOR UPDATE 锁定行——但间隙锁可能锁定超出预期的范围
- InnoDB 使用临键锁——防止幻读但可能导致死锁
- 锁等待超时默认 50 秒——通过 innodblockwait_timeout 调整
- MySQL 8+ 支持 FOR UPDATE SKIP LOCKED——队列模式
- InnoDB 默认隔离级别为可重复读,而非 PostgreSQL 的读已提交
- 死锁是预期行为——代码必须捕获并重试,而非直接失败
GROUP BY 严格性
- - MySQL 5.7+ 默认 sqlmode 包含 ONLYFULLGROUPBY
- 非聚合列必须包含在 GROUP BY 中——不同于旧版 MySQL 的宽松模式
- 当确定值相同时,使用 ANYVALUE(column) 消除错误
- 检查旧数据库的 sqlmode——行为可能不同
InnoDB vs MyISAM
- - 始终使用 InnoDB——支持事务、行级锁、外键、崩溃恢复
- MyISAM 仍是一些系统表的默认引擎——不要用于应用数据
- 检查引擎:SHOW TABLE STATUS——使用 ALTER TABLE ... ENGINE=InnoDB 转换
- JOIN 中混合引擎可以工作,但会失去事务保证
查询特性
- - LIMIT offset, count 与 PostgreSQL 的 LIMIT count OFFSET offset 顺序不同
- != 和 <> 都有效;推荐使用 <> 以符合 SQL 标准
- 不支持事务性 DDL——ALTER TABLE 立即提交,无法回滚
- 布尔类型为 TINYINT(1)——TRUE/FALSE 只是 1/0
- 两个参数时使用 IFNULL(a, b) 替代 COALESCE——虽然 COALESCE 也可用
连接管理
- - waittimeout 会关闭空闲连接——默认 8 小时;连接池可能无法感知
- maxconnections 默认 151——通常过低;每个连接都消耗内存
- 连接池:所有应用实例的总连接数不要超过 max_connections
- 使用 SHOW PROCESSLIST 查看活动连接——使用 KILL 终止长时间运行的连接
复制注意事项
- - 基于语句的复制可能因非确定性函数而中断——UUID()、NOW()
- 基于行的复制更安全但带宽消耗更大——MySQL 8 默认使用
- 只读副本存在延迟——在依赖副本读取前检查 SecondsBehindMaster
- 不要写入只读副本——通常为只读,但需确认
性能
- - EXPLAIN ANALYZE 仅在 MySQL 8.0.18+ 中可用——旧版本仅支持 EXPLAIN,不显示实际执行时间
- MySQL 8 中移除了查询缓存——不要依赖它;在应用层进行缓存
- 对碎片化表使用 OPTIMIZE TABLE——会锁定表;大表使用 pt-online-schema-change
- innodbbufferpool_size——专用数据库服务器设置为内存的 70-80%