Skip to content
Charles
Go back

MySQL 也可以做很多事

Edit page

引言

MySQL 经常被概括成“业务 CRUD 数据库”。这个说法没错,但不完整。以 InnoDB 为核心,MySQL 还提供事务、JSON、多值索引、全文检索、空间索引、事件调度和复制能力,足以覆盖很多中小型系统。

这篇文章沿用 PostgreSQL for EverythingSQLite for Everything 的思路:先使用已有数据库解决问题,只有真实需求超出边界时才增加组件。MySQL 的重点不是模仿 PostgreSQL,而是把它已经成熟的能力用好。

本文中的建表示例以 MySQL 8.0.16 及以上为前提。旧版本对 CHECK 约束、排序规则和部分 JSON 索引能力的支持不同。

稳定可靠

MySQL 使用时间长、生态大、运维经验丰富。InnoDB 的事务、行锁、索引和崩溃恢复已经是大量业务系统的默认底座。数据库的“无聊”是一种生产力:遇到问题时更容易找到文档、工具和有经验的人。

易于运行、安装和扩展

本地可以直接安装 MySQL 或使用 Docker,线上也有大量托管服务。主从复制、备份、监控和扩容有成熟工具可用。托管服务依旧需要关注连接数、慢查询、磁盘、binlog 保留和恢复演练,但不必自建所有控制面。

简化 IT 架构

MySQL 也不只有一组表和几个 CRUD 语句。很多中小型场景可以先让它同时承担搜索、文档字段、小型队列、定时维护和数据同步。

用户、订单、库存、支付这类数据需要原子更新和约束,首先应该使用 InnoDB、主键、唯一索引和明确的事务边界:

CREATE TABLE orders (
  id         BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  user_id    BIGINT UNSIGNED NOT NULL,
  status     VARCHAR(32) NOT NULL,
  amount     DECIMAL(18, 2) NOT NULL,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  KEY orders_user_created_idx (user_id, created_at),
  CONSTRAINT orders_amount_ck CHECK (amount >= 0)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_0900_ai_ci;

金额使用 DECIMAL,不要使用浮点数;时间和字符集在建表时确定;索引按照实际查询建立,而不是把每个字段都加上索引。数据库能保证的约束,优先交给数据库。

MySQL 可以替代 Solr 和 Elastic:FULLTEXT

InnoDB 支持 FULLTEXT 索引和 MATCH ... AGAINST 查询:

CREATE TABLE documents (
  id    BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  title VARCHAR(255) NOT NULL,
  body  TEXT NOT NULL,
  FULLTEXT KEY documents_search_idx (title, body) WITH PARSER ngram
) ENGINE = InnoDB;

SELECT id, title,
       MATCH(title, body) AGAINST ('数据库' IN NATURAL LANGUAGE MODE) AS score
FROM documents
WHERE MATCH(title, body) AGAINST ('数据库' IN NATURAL LANGUAGE MODE)
ORDER BY score DESC;

中文、日文、韩文需要使用 ngram parser 或其他分词方案,不能默认英文按空格切词的行为适合中文。上面的示例使用 WITH PARSER ngram,实际部署还要根据最大词长调整 ngram_token_size。站内文章、产品说明和后台文档可以先用 FULLTEXT;如果要做复杂相关性、拼写纠错、推荐召回或搜索集群独立扩容,再考虑专用搜索引擎。

MySQL 可以替代 MongoDB:JSON 文档

MySQL 的 JSON 列适合商品扩展属性、第三方响应、配置快照等结构变化比较频繁的数据:

CREATE TABLE products (
  id         BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  name       VARCHAR(255) NOT NULL,
  attributes JSON NOT NULL
);

INSERT INTO products(name, attributes)
VALUES ('键盘', '{"layout":"75%","wireless":true}');

SELECT id, name
FROM products
WHERE attributes->>'$.wireless' = 'true';

如果某个 JSON 路径变成高频过滤条件,就把它提升为生成列,或者使用合适的多值索引。JSON 不是逃避建模的借口:订单状态、用户 ID、金额、关联关系仍然应该是有类型和约束的普通列。

MySQL 可以替代 Kafka 和 RabbitMQ:小型队列

MySQL 8.0 之后,FOR UPDATE SKIP LOCKED 可以用于领取待处理任务:

START TRANSACTION;

SELECT id, payload
FROM jobs
WHERE status = 'pending' AND available_at <= CURRENT_TIMESTAMP
ORDER BY id
LIMIT 1
FOR UPDATE SKIP LOCKED;

UPDATE jobs
SET status = 'running', started_at = CURRENT_TIMESTAMP
WHERE id = ?;

COMMIT;

它适合和业务事务在同一个 MySQL 实例里的小型后台任务。要补上失败重试、超时回收和幂等键;如果任务量巨大、消费者很多、需要独立回放或消息保留,消息队列更合适。

MySQL 可以替代 ClickHouse:有限的时序场景

按时间分区、时间索引和批量写入足够覆盖订单流水、操作日志和中小型指标表。它适合时序数据需要和用户、订单等业务表一起查询的场景;如果主要负载变成高吞吐写入和大范围聚合,ClickHouse 或专用时序数据库更合适。

MySQL 可以替代向量数据库:只在有明确产品支持时

MySQL 本身不应被宣传成所有版本都自带的向量数据库。使用 MySQL HeatWave Vector Store、兼容扩展或旁路向量服务前,要确认版本、部署形态、索引能力和费用。普通业务搜索不要为了“AI”额外引入一套向量基础设施,简单 embedding 可以先和业务数据保持可追溯的关联。

MySQL 可以替代 Redis:有限的缓存场景

短期缓存可以用带过期时间的表,或者评估 MEMORY 引擎;但高并发、淘汰策略、内存上限和低延迟访问仍然是 Redis 更擅长的领域。MySQL 的优势是缓存和业务事务在同一个系统里,而不是它会自动变成内存数据库。

MySQL 可以替代文件系统:保存小型对象的元数据

小型 BLOB 可以放在 MySQL 中,但大文件、图片和视频通常应该进入对象存储。数据库保存对象地址、类型、权限和哈希值,通常比把整套文件系统塞进业务库更容易扩展。

MySQL 可以替代图数据库:树形数据

简单树形数据可以通过邻接表、路径字段或递归查询处理;复杂图遍历不应硬塞进 MySQL。另一方面,一些只负责查询数据库并返回 JSON 的薄微服务,可以直接用 SQL、视图或存储过程减少胶水代码,但认证、业务规则和跨服务边界仍应留在应用层。

MySQL 作为 GraphDB 和 Neo4J 的替代方案

MySQL 可以处理有限的关系遍历,但没有 PostgreSQL 的 AGE 这类图扩展,也不适合把复杂图算法硬写成 SQL。只有组织树、分类树和简单依赖关系时,使用递归 CTE 才是合理的简化。

MySQL 可以替代微服务

MySQL 的二进制日志可以支持复制和变更数据捕获。报表、搜索索引或异步同步可以从 binlog 构建下游流程,避免业务代码在每次写入时手动维护多份数据。

MySQL Event Scheduler 也能执行简单的定时 SQL,例如清理过期数据:

使用前需要确认 event_scheduler 已开启,并且当前账号拥有创建事件的权限。 如果通过 mysql 客户端执行,还需要先调整语句分隔符:

DELIMITER //

CREATE EVENT cleanup_sessions
ON SCHEDULE EVERY 1 HOUR
DO
  DELETE FROM sessions
  WHERE expires_at < CURRENT_TIMESTAMP//

DELIMITER ;

事件调度适合简单、低风险的数据库内维护任务。复杂工作流、重试编排和跨服务调用不要塞进 Event Scheduler;它应该保持短小且可观测。

MySQL 不能替代 PlayStation 5

用 SQL 写游戏可以当作演示,但和生产系统无关。数据库的能力边界应该由真实查询和运维指标决定。

结论

下面这些情况通常值得引入其他组件:

  1. 搜索需要复杂中文分词、相关性调优、海量索引和独立扩展。
  2. 缓存访问量远高于业务写入,并且需要专门的淘汰和内存策略。
  3. 消息需要高吞吐、长时间保留、多个消费组和独立回放。
  4. 时序数据主要是持续高频写入和大范围聚合,事务表已经成为瓶颈。
  5. 大文件应该进入对象存储,MySQL 只保存元数据和地址。

专用系统的引入成本并不只是安装一个服务,还包括权限、监控、备份、故障恢复和数据一致性。先用 MySQL 的事务、索引和 binlog 打通业务闭环,再依据指标拆分,通常更稳。

MySQL 的“简单”是优点:生态成熟、运维人员多、InnoDB 的业务模型清楚。它不需要承担所有搜索、缓存和消息场景,但在很多项目里,关系数据、JSON、全文检索、小队列和同步链路已经足够构成一个完整系统。

先问“能不能用 MySQL 的表、索引、事务或 binlog 解决”,再问“要不要新增一个服务”。答案如果是否定的,再引入专用工具;答案如果是肯定的,就少维护一套系统。

官方资料:InnoDB 全文索引全文检索ngram parser生成列和索引


Edit page
Share this post:

Previous Post
SQLite 也可以做很多事
Next Post
MongoDB 也可以做很多事