Communitygithub.com

LDKTC/Agent-P-FullStack

用于AI Claude、Codex、Copilot等助手的MySQL/MariaDB专业技能,指导数据库设计、查询优化和迁移。

Agent-P-FullStack 是什么?

该技能为AI助手的MySQL/MariaDB专业层,专注于数据库引擎特定知识。它帮助确认项目实际使用的数据库引擎(MySQL或MariaDB),并提供从字符集选择、数据类型优化到索引设计、查询调优、事务处理等全面指导。技能强调MariaDB与MySQL的差异,避免开发者假设完全兼容。支持Claude、Codex、Copilot等主流AI平台,通过步骤化的方式引导用户完成数据库设计、性能优化和错误预防。核心价值在于填补通用代码生成工具和引擎特定细节之间的鸿沟,减少常见陷阱。

兼容平台✓Claude Code✓Codex CLI✓Cursor
npx skills add LDKTC/Agent-P-FullStack

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

MySQL

database-schema-dev和backend-dev的MySQL特定专业知识层: 这些代理是引擎无关的,会匹配项目已有的迁移工具/ORM——此技能是它们在引擎实际为MySQL或其近兼容分支MariaDB时调用的MySQL知识。 MariaDB行为不同的地方会特别指出——不要假设高级特性完全对等。

第1步——确认引擎实际是MySQL/MariaDB

检查package.json中是否有mysql2/mysql,ORM方言/提供者(Prisma中的provider = "mysql",Sequelize中的dialect: 'mysql',TypeORM中的type: 'mysql'/'mariadb'),docker-compose.yml中运行mysql/mariadb镜像的服务,或mysql://连接字符串。如果以上都不成立,则停止——使用database-schema-dev/backend-dev的引擎无关默认值,或改用对应的引擎特定技能。

第2步——字符集、排序规则、存储引擎

  • 默认字符集是utf8mb4,而不是utf8——MySQL的utf8是一个最多3字节的别名,无法存放大多数表情符号/星位字符——并显式指定排序规则(MySQL 8+使用utf8mb4_0900_ai_ci;MariaDB没有0900排序规则,对于需要同时兼容两者的场景使用utf8mb4_unicode_ci)。
  • 使用InnoDB,除非有特定理由使用MyISAM(无事务、无行锁、无外键)——自MySQL 5.5/MariaDB 10.x起这就是默认值,因此通常只是确认没有其他设置覆盖它。
  • _ci排序规则意味着'[email protected]'匹配'[email protected]'——对于必须区分大小写比较的内容(令牌、密钥),使用_bin/_cs(或哈希列)。

第3步——正确的数据类型

  • 货币/精确小数:使用DECIMAL(p,s),绝不使用FLOAT/DOUBLE——二进制浮点数无法精确表示大多数十进制分数。
  • 根据实际范围选择合适的整数类型(TINYINT/SMALLINT/INT/BIGINT),而不是习惯性地全用BIGINT——较小的类型意味着较小的索引。UNSIGNED为ID列提供了额外的空间,而ID列永远不会为负数。
  • 对于不需要时区转换的挂钟时间值使用DATETIME;TIMESTAMP(UTC标准化,按会话时区自动转换,2038年范围限制)仅用于实际的时间点——不要默认使用它。
  • 根据实际约束调整VARCHAR(n)的大小,而不是反射性地使用VARCHAR(255);对于真正无边界的内容使用TEXT/BLOB——两者都不能有默认值或完整(非前缀)索引。
  • ENUM仅用于固定且很少变化的集合——值按定义顺序排序,而不是按字母顺序(这在ORDER BY中可能是一个意外),在MySQL 8上追加值只是元数据操作,但在MariaDB/较旧MySQL上可能会重写整个表。一旦集合变大或需要元数据,查找表会更具可扩展性。
  • 原生JSON(MySQL 5.7.8+/MariaDB 10.2+——MariaDB的JSON实际上是LONGTEXT加上一个CHECK约束,不是二进制类型,因此MySQL的二进制存储/部分更新优势都不适用)仅用于真正无模式的数据——任何需要经常过滤或连接的内容应该放在真实列中。

第4步——索引设计

  • 复合索引:先将等值过滤列放在前面,范围/排序列放在最后——(status, created_at)服务于WHERE status = ? ORDER BY created_at;反向顺序则不行。
  • 最左前缀规则:(a, b, c)上的索引也服务于(a)和(a, b)查找——不要在其旁边添加冗余的(a)索引。
  • 覆盖索引(包含所有被选列的索引)允许MySQL仅从索引中回答查询(EXPLAIN中显示Using index)——值得用于热点、窄查询,但不要默认对每个查询都使用。
  • 长VARCHAR/TEXT列使用前缀索引(col(20))——通过比较COUNT(DISTINCT LEFT(col, n))和COUNT(DISTINCT col)来确定n的大小。
  • 深度分页:LIMIT 20 OFFSET 100000仍然会扫描并丢弃10万行。键集分页(WHERE (created_at, id) < (:last_created, :last_id) ORDER BY created_at DESC, id DESC LIMIT 20)会复用同一个复合索引并直接定位到游标——在无限滚动或深层API分页中使用它代替OFFSET。
  • 每个索引都会增加写入时间和磁盘空间——根据实际查询模式添加索引(database-schema-dev第1步),而不是投机地为每一列添加。

第5步——基于EXPLAIN的查询调优

  • 在相信查询快速之前,运行EXPLAIN(或EXPLAIN ANALYZE,MySQL 8.0.18+/MariaDB 10.4+)——不要仅凭查询形状判断。
  • type: ALL表示全表扫描;ref/range/const表示实际使用了索引——一个值得优化的查询在表大小超过小规模后不应显示ALL。
  • Extra: Using filesort/Using temporary表示MySQL在索引外进行了排序/物化——通常通过一个匹配ORDER BY/GROUP BY的索引来修复。
  • 对索引列使用包装函数或隐式类型转换(WHERE YEAR(created_at) = ...,将字符串列与数字比较)会阻止索引使用——改为写成裸列的范围查询(created_at >= '2024-01-01' AND created_at < '2025-01-01')。
  • 在宽表上使用SELECT *会阻止覆盖索引并拉取未请求的TEXT/BLOB列——只选择实际使用的列。

第6步——事务与锁定

  • InnoDB锁定行而不是表,但默认隔离级别REPEATABLE READ在UPDATE/DELETE/加锁SELECT的范围扫描上还会使用间隙锁——一个范围条件可能会阻塞比匹配行更多的记录。如果不需要可重复读,READ COMMITTED会避免间隙锁。
  • 该快照仅覆盖普通SELECT——同一事务中的加锁读取(SELECT ... FOR UPDATE、UPDATE、DELETE)会读取最新的已提交数据,因此可能看到快照从未展示的行。不要假定快照隔离也能保护加锁读取。
  • 保持事务简短——一个打开的事务会持有其锁,并且在REPEATABLE READ下还会阻止InnoDB清除旧版本的行。
  • 使用SELECT ... FOR UPDATE来锁定在同一个事务中被读取再写入的行(如余额检查后再扣款);在整个代码库中按一致的行顺序获取锁以避免死锁。
  • 在真实竞争下死锁(ERROR 1213,SQLSTATE 40001)是预期的——InnoDB会自动回滚一方;捕获该代码并重试,而不是通过延长事务来“修复”它。SHOW ENGINE INNODB STATUS → LATEST DETECTED DEADLOCK会显示每一方持有/等待了什么。
  • 每个连接默认开启自动提交——多语句的原子性需要显式事务(或ORM的包装器),而不仅仅是在代码中相邻排列。

第7步——MySQL特定陷阱和MariaDB差异

  • sql_mode应包含STRICT_TRANS_TABLES(自MySQL 5.7/MariaDB 10.2起默认)——如果没有它,超出范围/错误类型的值会被静默截断而不是报错;确认它没有被放宽。
  • AUTO_INCREMENT值不保证连续(回滚仍然会消耗一个值)——永远不要依赖id是连续的。
  • 会话time_zone影响TIMESTAMP转换但不影响DATETIME——一个池中每个连接时区不一致会导致真正的“时间错误”问题;显式固定它。
  • MariaDB有自己的扩展,MySQL中没有对应项(如CREATE SEQUENCE、写入操作的RETURNING)——如果也要支持MySQL,则不要使用它们;窗口函数/CTE需要MySQL 8.0+/MariaDB 10.2+,先检查固定的版本。
  • MariaDB的优化器和EXPLAIN输出与MySQL不是同一个引擎——在实际使用的引擎上重新运行EXPLAIN,而不是相信在另一个引擎上调优的计划。

第8步——报告完成前的自我检查

确认每个新增/修改的列都有有意的类型,并且字符集回退到utf8mb4。确认任何新索引都有实际查询模式支持,并且EXPLAIN输出显示它被使用,任何深度/无边界列表端点使用键集分页而不是OFFSET。确认多语句写操作被包装在显式事务中,并且加锁读取的隔离行为是有意选择的,而不是假定。如果compose文件/CI固定使用MariaDB,确认没有添加仅MySQL-8特有的特性。

不要做什么

  • 不要使用utf8字符集或FLOAT/DOUBLE表示货币——utf8mb4和DECIMAL是正确的默认值。
  • 不要在没有查询模式的情况下添加索引,或在不检查EXPLAIN的情况下称一个查询为“快速”。
  • 不要在可以深度分页或无限运行的列表上使用OFFSET分页——改用键集分页。
  • 不要让事务保持打开状态超过其保护的工作所需的时间,尤其是在REPEATABLE READ的间隙锁下。
  • 不要依赖AUTO_INCREMENT的连续性或隐式类型转换来保证正确性。
  • 不要在没有检查固定版本的情况下假定MariaDB目标具有MySQL-8-only特性(原生JSON、utf8mb4_0900_ai_ci)。
  • 不要自己编写引擎无关的迁移/访问层代码——这是database-schema-dev/backend-dev的职责;此技能是它们应用在其之上的MySQL知识。

相关技能