mysql-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMySQL Patterns
MySQL 模式
Use this skill when working on MySQL or MariaDB schema design, migrations,
slow-query investigation, queue-style transactions, connection pools, or
production database configuration. Prefer exact version checks before applying a
feature-specific pattern because MySQL and MariaDB have diverged in several SQL
details.
当你进行MySQL或MariaDB架构设计、迁移、慢查询排查、队列式事务、连接池配置或生产数据库配置时,可以使用本技能。在应用特定功能模式前,建议先精确检查版本,因为MySQL和MariaDB在若干SQL细节上已经出现差异。
Activation
适用场景
- Designing MySQL or MariaDB tables, indexes, and constraints
- Reviewing migrations before they run on large production tables
- Debugging slow queries, lock waits, deadlocks, or connection exhaustion
- Adding keyset pagination, upserts, full-text search, JSON columns, or queues
- Configuring application connection pools, read replicas, TLS, or slow logs
- 设计MySQL或MariaDB的表、索引与约束
- 在大型生产表上执行迁移前进行审核
- 调试慢查询、锁等待、死锁或连接耗尽问题
- 实现键集分页、Upsert、全文搜索、JSON列或队列功能
- 配置应用连接池、只读副本、TLS或慢查询日志
Version Check
版本检查
Start by identifying the engine and version:
sql
SELECT VERSION();
SHOW VARIABLES LIKE 'version_comment';Keep MySQL and MariaDB guidance separate when syntax differs:
- MySQL documents row aliases as the replacement for in
VALUES(col);ON DUPLICATE KEY UPDATEis deprecated there.VALUES(col) - MariaDB documents as the supported way to reference inserted values in
VALUES(col); use it for cross-engine compatibility.ON DUPLICATE KEY UPDATE - is appropriate for queue-like work only. It skips locked rows and can return an inconsistent view, so do not use it for general accounting or integrity-sensitive reads.
SKIP LOCKED
首先确认数据库引擎和版本:
sql
SELECT VERSION();
SHOW VARIABLES LIKE 'version_comment';当语法存在差异时,需区分MySQL和MariaDB的指导方案:
- MySQL文档中,行别名是中
ON DUPLICATE KEY UPDATE的替代方案;VALUES(col)在MySQL中已被弃用。VALUES(col) - MariaDB文档中,是
VALUES(col)中引用插入值的支持方式;若需跨引擎兼容,可使用该方案。ON DUPLICATE KEY UPDATE - 仅适用于类队列场景。它会跳过锁定行,可能返回不一致视图,因此不要用于常规会计或对完整性敏感的读取操作。
SKIP LOCKED
Schema Defaults
架构默认配置
sql
CREATE TABLE orders (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
account_id BIGINT UNSIGNED NOT NULL,
status VARCHAR(32) NOT NULL,
total DECIMAL(15, 2) NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at DATETIME NULL,
PRIMARY KEY (id),
KEY idx_orders_account_status_created (account_id, status, created_at),
KEY idx_orders_active (account_id, deleted_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;Default choices:
| Use Case | Prefer | Avoid |
|---|---|---|
| Surrogate primary keys | | |
| UUID lookup keys | | |
| Money and exact quantities | | |
| User-facing text | | MySQL |
| Application timestamps | | Assuming |
| Soft deletes | | Filtering soft-deleted rows without an index |
| Extensible status values | lookup table or constrained | |
sql
CREATE TABLE orders (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
account_id BIGINT UNSIGNED NOT NULL,
status VARCHAR(32) NOT NULL,
total DECIMAL(15, 2) NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at DATETIME NULL,
PRIMARY KEY (id),
KEY idx_orders_account_status_created (account_id, status, created_at),
KEY idx_orders_active (account_id, deleted_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;默认选择建议:
| 使用场景 | 推荐方案 | 避免方案 |
|---|---|---|
| 代理主键 | | 对于可能超过20亿行的表使用 |
| UUID查找键 | 搭配转换工具使用 | 在高频访问表上使用 |
| 金额与精确数量 | | |
| 用户可见文本 | 表和索引使用 | MySQL默认的 |
| 应用时间戳 | 由应用管理UTC时间的 | 假设 |
| 软删除 | | 过滤软删除行时不使用索引 |
| 可扩展状态值 | 查找表或带约束的 | 当值频繁变化时使用 |
Indexing
索引优化
Composite index order usually follows equality predicates first, then range or
sort columns:
sql
CREATE INDEX idx_orders_account_status_created
ON orders (account_id, status, created_at);
SELECT id, total
FROM orders
WHERE account_id = ?
AND status = 'pending'
AND created_at >= ?
ORDER BY created_at DESC
LIMIT 50;Use before adding or changing an index:
EXPLAINsql
EXPLAIN
SELECT id, total
FROM orders
WHERE account_id = 123 AND status = 'pending'
ORDER BY created_at DESC
LIMIT 50;Signals to investigate:
| Field | Risk Signal |
|---|---|
| |
| |
| Very high row estimate for an interactive path |
| |
Avoid adding indexes blindly. Each index increases write cost, migration time,
backup size, and buffer-pool pressure.
复合索引的顺序通常遵循:先等值条件列,再范围或排序列:
sql
CREATE INDEX idx_orders_account_status_created
ON orders (account_id, status, created_at);
SELECT id, total
FROM orders
WHERE account_id = ?
AND status = 'pending'
AND created_at >= ?
ORDER BY created_at DESC
LIMIT 50;添加或修改索引前使用分析:
EXPLAINsql
EXPLAIN
SELECT id, total
FROM orders
WHERE account_id = 123 AND status = 'pending'
ORDER BY created_at DESC
LIMIT 50;需要排查的风险信号:
| 字段 | 风险信号 |
|---|---|
| 大型表上出现 |
| 存在选择性条件时 |
| 交互式查询路径的预估行数极高 |
| |
避免盲目添加索引。每个索引都会增加写入成本、迁移时间、备份大小和缓冲池压力。
Query Patterns
查询模式
Upsert
Upsert
Cross-engine-compatible form:
sql
INSERT INTO user_settings (user_id, setting_key, setting_value)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE
setting_value = VALUES(setting_value),
updated_at = CURRENT_TIMESTAMP;MySQL row-alias form:
sql
INSERT INTO user_settings (user_id, setting_key, setting_value)
VALUES (?, ?, ?) AS new
ON DUPLICATE KEY UPDATE
setting_value = new.setting_value,
updated_at = CURRENT_TIMESTAMP;Use the row-alias form only after confirming the target is MySQL. Use
for MariaDB or mixed MySQL/MariaDB fleets.
VALUES(col)跨引擎兼容格式:
sql
INSERT INTO user_settings (user_id, setting_key, setting_value)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE
setting_value = VALUES(setting_value),
updated_at = CURRENT_TIMESTAMP;MySQL行别名格式:
sql
INSERT INTO user_settings (user_id, setting_key, setting_value)
VALUES (?, ?, ?) AS new
ON DUPLICATE KEY UPDATE
setting_value = new.setting_value,
updated_at = CURRENT_TIMESTAMP;仅在确认目标为MySQL时使用行别名格式。对于MariaDB或混合MySQL/MariaDB集群,使用。
VALUES(col)Keyset Pagination
键集分页
sql
SELECT id, name, created_at
FROM products
WHERE (created_at, id) < (?, ?)
ORDER BY created_at DESC, id DESC
LIMIT 50;Back it with an index that matches the cursor:
sql
CREATE INDEX idx_products_created_id ON products (created_at, id);Do not use deep pagination on large tables; it makes the server scan
and discard rows before returning the page.
OFFSETsql
SELECT id, name, created_at
FROM products
WHERE (created_at, id) < (?, ?)
ORDER BY created_at DESC, id DESC
LIMIT 50;为其创建匹配游标条件的索引:
sql
CREATE INDEX idx_products_created_id ON products (created_at, id);不要在大型表上使用深度分页;这会导致服务器扫描并丢弃大量行后才返回目标页面。
OFFSETJSON Fields
JSON字段
Use JSON columns for extension data, not for fields that need heavy relational
filtering or constraints.
sql
CREATE TABLE events (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
payload JSON NOT NULL,
event_type VARCHAR(64)
GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(payload, '$.type'))) STORED,
KEY idx_events_type (event_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;For frequently queried JSON paths, expose a generated column and index that
column. Keep foreign keys, ownership, tenancy, and lifecycle fields relational.
JSON列适用于扩展数据,不适用于需要大量关系型过滤或约束的字段。
sql
CREATE TABLE events (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
payload JSON NOT NULL,
event_type VARCHAR(64)
GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(payload, '$.type'))) STORED,
KEY idx_events_type (event_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;对于频繁查询的JSON路径,生成一个计算列并为该列创建索引。将外键、归属关系、租户信息和生命周期字段保留为关系型字段。
Full-Text Search
全文搜索
sql
ALTER TABLE articles ADD FULLTEXT KEY ft_articles_title_body (title, body);
SELECT id, title, MATCH(title, body) AGAINST (? IN NATURAL LANGUAGE MODE) AS score
FROM articles
WHERE MATCH(title, body) AGAINST (? IN NATURAL LANGUAGE MODE)
ORDER BY score DESC
LIMIT 20;Use external search when you need typo tolerance, complex ranking, cross-table
facets, or language-specific analysis beyond built-in full-text behavior.
sql
ALTER TABLE articles ADD FULLTEXT KEY ft_articles_title_body (title, body);
SELECT id, title, MATCH(title, body) AGAINST (? IN NATURAL LANGUAGE MODE) AS score
FROM articles
WHERE MATCH(title, body) AGAINST (? IN NATURAL LANGUAGE MODE)
ORDER BY score DESC
LIMIT 20;当你需要容错拼写、复杂排序、跨表分面或超出内置全文搜索能力的特定语言分析时,使用外部搜索工具。
Transactions
事务管理
Keep transactions short and lock rows in a consistent order:
sql
START TRANSACTION;
SELECT id, balance
FROM accounts
WHERE id IN (?, ?)
ORDER BY id
FOR UPDATE;
UPDATE accounts SET balance = balance - ? WHERE id = ?;
UPDATE accounts SET balance = balance + ? WHERE id = ?;
COMMIT;Deadlock and lock-wait checklist:
- Lock rows in a deterministic order across code paths.
- Do external API calls before opening the transaction, not inside it.
- Add indexes for predicates used in ,
UPDATE, and locking reads.DELETE - On deadlock, roll back and retry the whole transaction with a bounded retry budget.
- Capture soon after a deadlock; it is overwritten by later events.
SHOW ENGINE INNODB STATUS\G
Queue-style worker claim:
sql
START TRANSACTION;
SELECT id
FROM jobs
WHERE status = 'pending'
ORDER BY created_at
LIMIT 1
FOR UPDATE SKIP LOCKED;
UPDATE jobs
SET status = 'processing', started_at = CURRENT_TIMESTAMP
WHERE id = ?;
COMMIT;Use only for queue-like workloads where skipping a locked row is
acceptable. It is not a replacement for normal transactional consistency.
SKIP LOCKED保持事务简短,并以一致的顺序锁定行:
sql
START TRANSACTION;
SELECT id, balance
FROM accounts
WHERE id IN (?, ?)
ORDER BY id
FOR UPDATE;
UPDATE accounts SET balance = balance - ? WHERE id = ?;
UPDATE accounts SET balance = balance + ? WHERE id = ?;
COMMIT;死锁与锁等待检查清单:
- 在所有代码路径中以确定的顺序锁定行。
- 在开启事务前完成外部API调用,不要在事务内部执行。
- 为、
UPDATE和锁定读取中使用的条件添加索引。DELETE - 发生死锁时,回滚并在有限重试次数内重新执行整个事务。
- 死锁发生后立即捕获;该信息会被后续事件覆盖。
SHOW ENGINE INNODB STATUS\G
类队列工作者任务认领:
sql
START TRANSACTION;
SELECT id
FROM jobs
WHERE status = 'pending'
ORDER BY created_at
LIMIT 1
FOR UPDATE SKIP LOCKED;
UPDATE jobs
SET status = 'processing', started_at = CURRENT_TIMESTAMP
WHERE id = ?;
COMMIT;仅在类队列工作负载中使用,这类场景下跳过锁定行是可接受的。它不能替代常规事务一致性。
SKIP LOCKEDConnection Pools
连接池配置
SQLAlchemy example:
python
from sqlalchemy import create_engine
engine = create_engine(
"mysql+mysqlconnector://app:secret@db.internal/app",
pool_size=10,
max_overflow=5,
pool_timeout=30,
pool_recycle=240,
pool_pre_ping=True,
connect_args={"connect_timeout": 5},
)Node.js example:
mysql2javascript
import mysql from 'mysql2/promise';
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
enableKeepAlive: true,
keepAliveInitialDelay: 30000,
});
const [rows] = await pool.execute(
'SELECT id, total FROM orders WHERE account_id = ? LIMIT 50',
[accountId],
);Keep application pool recycling below the server . If the server
uses , a around 240 seconds is coherent;
still helps recover from network and failover events.
wait_timeoutwait_timeout = 300pool_recyclepool_pre_pingSQLAlchemy示例:
python
from sqlalchemy import create_engine
engine = create_engine(
"mysql+mysqlconnector://app:secret@db.internal/app",
pool_size=10,
max_overflow=5,
pool_timeout=30,
pool_recycle=240,
pool_pre_ping=True,
connect_args={"connect_timeout": 5},
)Node.js 示例:
mysql2javascript
import mysql from 'mysql2/promise';
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
enableKeepAlive: true,
keepAliveInitialDelay: 30000,
});
const [rows] = await pool.execute(
'SELECT id, total FROM orders WHERE account_id = ? LIMIT 50',
[accountId],
);保持应用连接池的回收时间低于服务器的。如果服务器设置,那么将设置为240秒左右是合理的;仍有助于从网络故障和故障转移事件中恢复。
wait_timeoutwait_timeout = 300pool_recyclepool_pre_pingDiagnostics
诊断工具
Useful first-pass commands:
sql
SHOW FULL PROCESSLIST;
SHOW ENGINE INNODB STATUS\G;
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';Enable the slow log in a controlled environment:
sql
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = 'ON';Use only when it is safe to execute the query. It runs the
statement and can be expensive on production-sized data.
EXPLAIN ANALYZE实用的初步排查命令:
sql
SHOW FULL PROCESSLIST;
SHOW ENGINE INNODB STATUS\G;
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';在受控环境中启用慢查询日志:
sql
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = 'ON';仅在安全的情况下使用执行查询。它会运行语句,在生产级数据上可能会消耗大量资源。
EXPLAIN ANALYZEReplication
复制配置
Read replicas can lag. Do not route read-your-own-write paths, checkout flows,
permission checks, or idempotency-key reads to a replica immediately after a
write.
sql
-- MySQL legacy terminology, still common in existing fleets
SHOW SLAVE STATUS\G;
-- Newer terminology where supported
SHOW REPLICA STATUS\G;Check the engine/version before standardizing on one command. Monitor replica
SQL thread health, IO thread health, and lag, not just whether the TCP
connection is alive.
只读副本可能存在延迟。写入操作完成后,不要立即将“读自己写的内容”路径、结账流程、权限检查或幂等键读取路由到副本。
sql
-- MySQL旧术语,在现有集群中仍常见
SHOW SLAVE STATUS\G;
-- 支持的新术语
SHOW REPLICA STATUS\G;在标准化命令前检查引擎/版本。监控副本的SQL线程健康状态、IO线程健康状态和延迟情况,而不仅仅是TCP连接是否存活。
Security
安全配置
sql
CREATE USER 'app'@'%' IDENTIFIED BY 'use-a-secret-manager';
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'app'@'%';
ALTER USER 'app'@'%' REQUIRE SSL;
SELECT user, host
FROM mysql.user
WHERE user = '';
DROP USER IF EXISTS ''@'localhost';
DROP USER IF EXISTS ''@'%';Security review points:
- Do not grant or
ALL PRIVILEGESto application users.*.* - Require TLS for application users when traffic crosses hosts or networks.
- Store credentials in the platform secret manager, not in examples, scripts, or repository files.
- Separate migration/admin users from runtime application users.
- Audit public network exposure and bind addresses before tuning performance.
sql
CREATE USER 'app'@'%' IDENTIFIED BY 'use-a-secret-manager';
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'app'@'%';
ALTER USER 'app'@'%' REQUIRE SSL;
SELECT user, host
FROM mysql.user
WHERE user = '';
DROP USER IF EXISTS ''@'localhost';
DROP USER IF EXISTS ''@'%';安全审核要点:
- 不要为应用用户授予或
ALL PRIVILEGES权限。*.* - 当流量跨主机或网络传输时,要求应用用户使用TLS。
- 将凭据存储在平台密钥管理器中,不要放在示例、脚本或仓库文件中。
- 将迁移/管理员用户与运行时应用用户分离。
- 在调优性能前,审核公网暴露情况和绑定地址。
Configuration
数据库配置
Example starting point for a dedicated database host:
ini
[mysqld]
innodb_buffer_pool_size = 4G
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1
max_connections = 300
thread_cache_size = 50
wait_timeout = 300
interactive_timeout = 300
innodb_lock_wait_timeout = 10
slow_query_log = ON
long_query_time = 1
log_queries_not_using_indexes = ON
log_bin = mysql-bin
binlog_format = ROW
binlog_expire_logs_seconds = 604800Treat configuration values as a prompt for review, not a universal preset. Size
memory, connections, log retention, and durability settings from workload,
hardware, backup policy, and recovery objectives.
专用数据库主机的示例初始配置:
ini
[mysqld]
innodb_buffer_pool_size = 4G
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1
max_connections = 300
thread_cache_size = 50
wait_timeout = 300
interactive_timeout = 300
innodb_lock_wait_timeout = 10
slow_query_log = ON
long_query_time = 1
log_queries_not_using_indexes = ON
log_bin = mysql-bin
binlog_format = ROW
binlog_expire_logs_seconds = 604800将配置值视为审核提示,而非通用预设。根据工作负载、硬件、备份策略和恢复目标调整内存、连接数、日志保留时间和持久性设置。
Anti-Patterns
反模式
| Anti-Pattern | Risk | Better Pattern |
|---|---|---|
| Over-fetching and brittle clients | Select explicit columns |
Deep | Linear scans and slow pages | Keyset pagination |
| No index on foreign-key joins | Slow joins and lock-heavy deletes | Index FK columns intentionally |
| Long transactions | Lock waits and large undo history | Commit small units of work |
Direct DML against | Grant-table corruption risk | Use |
| Application user with admin grants | High blast radius | Least-privilege runtime user |
Pool recycle above | Stale pooled connections | Recycle below timeout and pre-ping |
| Replica reads after writes | Stale user-facing state | Pin read-after-write flows to primary |
| 反模式 | 风险 | 更优方案 |
|---|---|---|
高频路径中使用 | 过度获取数据且客户端易出错 | 选择明确的列 |
深度 | 线性扫描且页面加载缓慢 | 键集分页 |
| 外键连接无索引 | 连接缓慢且删除操作锁密集 | 为外键列创建索引 |
| 长事务 | 锁等待和大量回滚历史 | 提交小单元的工作 |
直接对 | 权限表损坏风险 | 使用 |
| 应用用户拥有管理员权限 | 影响范围大 | 最小权限运行时用户 |
连接池回收时间高于 | 连接池中的连接失效 | 回收时间低于超时时间并启用预检测 |
| 写入后立即读取副本 | 用户可见状态过时 | 将写后读流程固定到主库 |
Output Expectations
输出预期
When this skill is used for review, return:
- Engine/version assumptions.
- Highest-risk correctness, lock, security, and migration issues.
- Exact SQL or code changes for the safe path.
- Validation plan: , migration dry run, lock/deadlock check, and rollback criteria.
EXPLAIN - Any MySQL/MariaDB syntax differences that affect the recommendation.
当使用本技能进行审核时,返回以下内容:
- 引擎/版本假设。
- 最高风险的正确性、锁、安全和迁移问题。
- 安全方案对应的精确SQL或代码修改。
- 验证计划:分析、迁移预演、锁/死锁检查及回滚标准。
EXPLAIN - 影响推荐方案的MySQL/MariaDB语法差异。
Related
相关内容
- Skill: - PostgreSQL-specific schema and query patterns
postgres-patterns - Skill: - migration planning and rollout safety
database-migrations - Skill: - API and service-layer patterns
backend-patterns - Skill: - secret handling, auth, and least privilege
security-review - Agent: - broader database review workflow
database-reviewer
- Skill: - PostgreSQL专属架构与查询模式
postgres-patterns - Skill: - 迁移规划与发布安全
database-migrations - Skill: - API与服务层模式
backend-patterns - Skill: - 密钥处理、认证与最小权限
security-review - Agent: - 更全面的数据库审核工作流
database-reviewer