MySQL 太好了
MySQL 是 OLTP 数据存储的典范:SQL、关系、事务、行锁定、MVCC、ACID,当然还有:快速。它非常擅长它是什么和做什么。
但也许它太擅长做什么了,因为它被用于各种其他目的。为什么?因为它在其他用途上的效果出奇的好。它就像一把锤子、扳手、钻头和螺丝刀合二为一。
很久以前,没有多少可行的选择。但在过去的 10 年中,数据存储出现了爆炸式增长。更重要的是,行业心态也发生了变化,将多语言数据作为常态。这意味着使用最好的数据存储来完成这项工作,而不仅仅是最普遍的数据存储——很长一段时间以来都是 MySQL。
如果你需要一位 MySQL 专家说不使用 MySQL 是可以的,相反,最好使用另一个数据存储,这里是:
不使用 MySQL 也没关系;使用另一个专门构建或更适合数据模型的数据存储。
经验法则:结构化和关系
根据经验,MySQL 最适合结构化和关系型数据。这应该不足为奇,因为它是一个 SQL 数据库,而“结构化”是 SQL 中的“S”。SQL 是在 EF Codd 的关系模型之上开发的查询语言。诚然,有人说 SQL 不适合关系模型,但对于我们的目的而言,这并不重要:SQL(和关系)赢了。
请注意,“OLTP”甚至只是“事务性”都不是经验法则的一部分。是的,MySQL 是一个 OLTP 优化的数据存储,但是由点 SELECT 主导的工作负载仍然是一个很好的用例,即使它几乎不是 OLTP 或从这两个术语的完整意义上来说是事务性的。
结构化和关系密切相关,但它们可以分开。例如,文档存储可以在数据中有关系,但文档没有固定的结构——这就是 NoSQL/文档存储如此有用的原因:灵活的“结构”。另一方面,像 DynamoDB 这样的键值存储在分区键和二级索引方面需要某种固定的结构,但实际上并没有关系。
当数据具有已知的结构并且结构化数据之间存在关系时,MySQL 能够找出如何有效地访问数据以进行无限数量的查询。直接说:刚性数据允许灵活查询。例如,DynamoDB不有灵活的查询:您必须使用分区键或少数二级索引之一,并且您不能连接表。数据非常灵活(在 DynamoDB 中转储您想要的任何内容),但查询不是。但是对于这种权衡,您可以获得极快的数据访问和水平可扩展性。MySQL 则相反。数据结构不灵活,但查询非常灵活(具有合理的索引),因为 MySQL 会根据固定结构计算出如何访问查询的数据——当然,您可以以任何您想要的方式连接表(并且MySQL 也可以)。
MySQL 本质上是一个关系数据存储。几乎所有关于它的东西 - 内部和外部 - 都被设计和优化为关系数据存储。因此,这是 MySQL 的最佳用例:结构化数据和关系数据。
如何不使用 MySQL
MySQL 适用于以下用例(有时非常好),但它们都没有展示结构化和关系数据。因此,在某种程度上,它们对 MySQL 起作用。
缓存
缓存通常是短暂的:仅在内存中。但是 MySQL 默认是持久的。这种持久性从根本上与内存缓存不一致(并且与之相比浪费了开销)。
缓存并不意味着事务性或关系性。它们通常要简单得多,例如键值或Redis可以做的所有事情。但是 MySQL 本质上是事务性和关系性的,使这两者发生的所有“机制”(再次)与缓存不一致。
尽管 InnoDB 缓冲池的作用类似于内存中的缓存,但您永远不应该这样想。如果您阅读《高效的 MySQL 性能》,就会很清楚为什么:缓冲池中的页面所做的不仅仅是缓存数据。
相反,请使用真正的缓存,例如memcached或Redis。
队列
队列适用于快速移动的瞬态数据。有些队列是持久的,有些则不是。通常对队列的唯一访问模式是“给我 N 条消息”——不查询队列。
真正让 MySQL 成为一个糟糕的队列的是,MySQL 中的每个查询都必须经过作为关系数据库中的事务的完整过程。因此,虽然队列只想快速输入和输出一条消息,但 MySQL 会说“等等。等等……让我们以 ACID 事务的全部尊严来对待这个查询……我们必须开始一个事务,建立一个一致的整体快照数据库、检查和获取锁、插入行、更新页面、可能拆分或合并索引树、提交事务、等待二进制日志、将脏页添加到列表中,以及……”等等。就像你想开车去杂货店买杏仁奶,但是你飞到世界各地的一家没有杏仁奶的商店,所以他们为你订购,同时你在异国他乡租了一家旅馆等待直到它到达,然后你飞回家。奇怪又可笑。所以不要使用 MySQL 作为队列。
OLAP
MySQL 是一个 OLTP 数据库。OLTP != OLAP。
MySQL是一个茶壶;不要用它来煮沸海洋。使用真正的 OLAP 数据存储。
大数据存储
我从开发人员那里得到的一个常见问题是 MySQL 可以存储多少数据。快速的技术答案是 64 TB,但请参阅15.22 InnoDB 限制。更现实的答案是(正如我在我的书第 5 章中所写的那样):
4 TB:对于特别优化的查询和访问模式,中高端硬件足以获得可接受的性能,但操作可能需要比可接受的稍长的时间。
是的,MySQL 可以存储 64 TB,但在此之前很久你就会遇到严重的问题。例如,您认为ALTER在 10 TB 上进行一次或在线模式迁移 (OSC) 需要多长时间?在繁忙的生产数据库上花费了很长时间。然后是工作集大小的问题:它有多大与你能买得起多少 RAM?
尽管如此,我管理着几个大约 7 TB 的数据库。(在我工作的地方,我们使用微服务,所以我们有数千个数据库,但大多数都很小。)但是这些工作是因为它大多是冷的、不经常访问的数据。如果要存储和访问数十 TB,则需要与 MySQL 不同的数据存储,尤其是取决于其他访问模式。MySQL 可以做到,但并不理想。
文档存储
我告诉你一个秘密:我不喜欢并且不鼓励使用JSON 数据类型。我不反对将 JSON blob 存储在BLOB列中,即使这开始将 MySQL 视为对象存储(如 Amazon S3)。但是使用 JSON 列类型进行类似文档的访问显然不是 MySQL 的设计目的。我们知道这一点是因为有专门构建的文档存储,例如MongoDB和Amazon DocumentDB(基于前者),它们与 MySQL 完全不同。一方面,真正的文档存储不使用 SQL。因此,用于文档访问的 SQL 语法是一个 hacky kludge——尤其是与真正的文档存储的查询语言相比。
文档存储水平扩展。这就是为什么它们在多年前被(有点讽刺地)称为“网络规模”的部分原因。如果您的数据是面向文档的,那么通过使用真正的文档存储,您将拥有更好的查询语言和内置的水平可伸缩性。记住:不使用 MySQL 也没关系。
键值存储
不可否认,当表只有一个主键时,MySQL 作为键值存储非常有效,因为作为 InnoDB 缓冲池中的聚集索引,这是可能的最快访问。MySQL 在单表主键查找方面速度极快。如果这是其他访问模式中的一种,否则数据是结构化的和相关的,那么没关系:它只是一种有效的点查找;它不会偷偷地将 MySQL 视为键值存储。
当这种类型的访问是有意的时,就会出现问题和次优用例:使用点查找进行键值访问,因为前者碰巧非常快。问题再次是,MySQL 中的每个查询都是一个事务,会产生所有事务性、关系性开销。
但更重要的是,就像将 MySQL 用于文档存储一样,您不会免费获得水平可伸缩性。如果您使用像 DynamoDB 或ScyllaiDB这样的真正的键值存储,您会这样做。
编程语言
很久以前(仍然在关系数据库世界的某些角落),将存储过程放在数据库中是很常见的。 例如PL/SQL。但是现在很容易在书中找到与此相关的各种问题。一方面,很难在存储过程中对代码进行单元测试,考虑到代码非常重要,这尤其糟糕:它与真实来源(数据库)的数据混为一谈。
以我的经验,这在 MySQL 中并不常见,即使它有存储过程。所以让我们保持这种方式:不要在 MySQL 中隐藏重要代码。将所有代码与您的应用程序代码、单元测试并保存在 Git(或您使用的任何 VCS)中。
另外,MySQL 内部的代码不能很好地扩展。当您的应用程序可能通过 Kubernetes 横向扩展时,为什么还要让一个可写的 MySQL 实例运行这样的代码呢?
原文标题:How Not to Use MySQL
原文链接:https://hackmysql.com/post/book-9/




