访问模式引起了我的兴趣,似乎每个人都了解并会谈论它们,但是在MySQL文献中,关于MySQL 的访问模式的相关文章却很少。这就是为什么我开始研究访问模式列表(特定于MySQL)。由于访问模式没有明显的标准,我无法衡量我的列表,由于使用MySQL时间很长,我知道这一点:在评估和提高MySQL性能时,有必要考虑这些访问模式。简而言之:您不能忽略应用程序如何访问MySQL。
访问模式
吞吐量
吞吐量 (QPS) 是一种奇怪的访问模式,因为它是“访问”吗?由于我们在“访问模式”一词中缺乏对“访问”的精确定义,我将声称“吞吐量”是一种访问模式,因为它肯定会影响许多考虑因素。或者这样看:没有理智的 DBA 会说您可以简单地忽略吞吐量。
尽管吞吐量不是性能,但它是几乎所有事物的一个重要考虑因素——尤其是其他访问模式。例如:
-
读写访问模式:
低吞吐量写入:容易。
高吞吐量写入:非常困难。 -
并发访问模式:
低吞吐量并发:容易。
高吞吐量并发:非常困难。
一般来说,我认为吞吐量是工作量的粗略“缩放因素”。在非常低的 QPS 下,几乎一切都很容易。但是 QPS 增加得越多,性能就越困难。但是它必须与所有其他访问模式一起考虑——QPS 本身几乎没有意义。
读/写
读取与写入可能是第一个也是最明显的访问模式:应用程序是读取数据、写入数据还是两者兼而有之?每个有多少:是 90% 的读取和 10% 的写入,反之亦然?这是一个非常简单但非常强大的访问模式,因为:
- 尤其是在使用更多内存的情况下,读取相对容易扩展。
- 即使使用更好的存储 I/O,写入也难以扩展。
它也是引起技术辩论的好工具。有时我会遇到关于某某性能的争论(例如,云中的存储 I/O 延迟),但没有人问过:对于读取、写入还是两者兼而有之?读取繁重的应用程序的性能与写入繁重的应用程序的考虑因素非常不同。在进行此类辩论时,请确保每个人都清楚这种访问模式。
读取一致性
说到读取:应用程序真的需要先读后写吗?如果不是,那么在缓存和其他处理最终一致性读取的技术方面会拥有无限可能。当读取 QPS 非常高时,这是必要的。
并发
并发可能是最困难的访问模式:
高并发读取:多亏了 MVCC(多版本并发控制),这使得读取很容易。 (一定要避免长时间运行的事务。)
高并发写入:非常困难;没有高效的解决方案; MySQL 必须在某个时候序列化。
高并发写入会导致分片,这对某些开发人员来说并非易事。如果有什么神奇之法,那就是:分片。否则,这种访问模式(高吞吐量 + 高并发)的组合就会催生像 RocksDB 这样的新技术:写优化的键值存储。
此外,不要使用不必要的 SELECT…FOR UPDATE 或 SELECT…FOR SHARE 来破坏读取并发性,它们基本上会将非锁定读取转换为锁定读取。
事务隔离
在 MySQL 中,每个查询都是一个事务(一般来说),这会产生相当多的成本。如果应用程序不需要事务或严格的事务隔离,那么这种成本就是浪费。
实际上,MySQL 的事务处理非常快速、高效,但是我几乎从未遇到过明确知道应用程序是否需要特定事务隔离级别的开发人员。通常他们只写一次性查询,或者他们确实使用事务来实现原子性,但不一定是隔离。如果查询实际上不需要隔离,那么至少使用 READ COMMITTED(读取已提交)来避免在整个事务期间有 MVCC 快照。在极端情况下不要使用事务性数据存储。
数据时代
假定的标准是总数据大小远大于总 RAM。但是添加到工作集则是:应用程序经常访问的一小部分数据。 MySQL 有许多操作方法都可以将工作集保存在内存中,并根据需要在内存中交换数据。将工作集保存在内存中对性能来说至关重要,因为它可以避免损害磁盘。但是如何做到这一点,并知道它是否正在完成这一项呢?这在 MySQL 中是难以精准确量的。有一些用于页面驱逐的低级服务器指标,一般的衡量标准是我在第 6 章中详细介绍的“缓冲池效率”。现在重点是很难真正衡量工作集以及是否停留在存储中,基本上很少有人这么操作。
有些人猜想:应用程序是否会一遍又一遍地访问相同的数据?例如:一旦某行以某种方式被使用,它是否“被时间遗忘”并且几乎再也不会被访问?又或者应用程序是否会不断查找(访问)很久以前和最近的行,以及介于两者之间的所有内容?
开发人员通常知道以下的这一点。例如:在我工作的地方(金融科技),虽然人们在结算后查找交易并不常见,但是人们通常可以访问过去几天或几周的金融交易。当然有时也会存在结算后查找交易的情况,但关键是这种行为很少会导致工作集太快。所以MySQL 在将最新数据保存在内存中并根据需要缓慢驱逐旧数据方面做得很好。
但有些应用程序非常不同。因为它们随时访问大量数据,如果RAM仍然足以存储所有数据,那就还好,但事实并非如此,因为RAM相对有限。例如:频繁访问500G表中的所有行,这在128G RAM上将很困难。(这只是数据:二级索引呢?其中可能有一些,例如:数据+索引可能是700G。那么问题就变成了:你能负担得起256G或384G或512G的RAM吗?或者您能否以某种方式更改此访问模式以避免改动,从而减小工作集的大小呢?)
行访问
从广义上讲,存在三种类型的行访问:
- 点访问:单行
- 范围访问:两个值之间的有序行
- 随机访问:任意顺序的几行
这不仅仅是一个简单的分类,当它与其他访问模式结合使用时,在性能方面也具有合规意义。例如,高吞吐量读取点访问基本上是 MySQL 充当缓存:MySQL 会将单行保留在内存中(因此它也与数据年限有关)。但是将其更改为随机访问,它并不那么容易,原因有两个。首先,如果它真的是随机的,那可能会影响工作集(数据时代)。其次,可能会影响 MySQL 根据基数(以及 MySQL 估计查询将匹配多少行)选择哪个索引。
写入可能是另一回事。点读很容易,但在高并发和吞吐量下,点写可能是最困难的挑战之一,因为根本无法绕过对单行进行某种程度的序列化访问。范围或随机写入可能会更好,但是如果行访问重叠,它们就会开始干扰来自其他写入的数据锁。
因此,与其他访问模式一样,行访问的类型是综合考虑性能的一个重要维度。
结果集
对结果集进行分组、排序或限制是一种非主要但仍然重要的访问模式。最好的办法是消除前两个并使用最后一个限制,因为这有助于减少MySQL的工作量。然而事情并没有那么简单,在前面的文章中,我提到了让 MySQL 进行分组或排序更好的方法。无论采取哪种方式,重要的是要知道这种访问模式何时会产生什么情况。例如:某些数据存储以不同的方式处理限制,这在迁移时发生了!
数据模型
MySQL 是一种关系型、事务型、OLTP 优化的数据存储。用于其他任何场景时,性能会变得“奇怪”。这就像使用小型私人飞机通勤上班,而您的办公室距离道路只有 20 分钟路程。
我个人认为不应该使用 JSON 列类型,因为 MySQL 不是文档存储。同样,我经常看到开发人员使用 BLOB 列将数据转储到 MySQL 中,这变得非常“有趣”,因为默认情况下 binlog_row_image = full,所以所有这些 blob 都被复制过。此外,即使使用 MySQL可以以令人难以置信的速度和效率对主键进行点读取,但是它作为简单的键值存储效果也并不理想。
我建议工程师将 MySQL 应用于特定场景,但我很清楚 MySQL 几乎可以用于任何情况,因为它非常擅长存储数据并使其真正持久耐用。但严肃地说,当您的数据模型不是关系型和事务型时,考虑使用其他数据存储会更好。
识别
识别访问模式是使用 MySQL 一次就能完成的事情(因为团队正在考虑转移到不同的数据存储)。相比之下,Amazon 发布了 DynamoDB 的访问模式列表,因为在迁移到 DynamoDB 之前识别它们至关重要。
访问模式必须由开发人员识别(并且可能写下来)。一般来说,每个查询都是一个访问模式。因此我们查看单个访问模式(查询),但我们也需要考虑访问模式的整体以及对 MySQL 的影响。例如;单个高吞吐量写入可能不会造成任何问题,但是许多这样的访问模式就会导致很难扩展。
尽管性能可以依据查询响应时间来判定,但是您可以通过简单地确保所有响应时间都在可接受的范围之内(并将响应时间作为您的关键指标进行优化),这样您就能大幅提高 MySQL的性能。专业软件工程师进行开发和维护时,需要了解严重依赖并涉及MySQL的应用程序,在这种应用程序中,与MySQL相关的所有访问模式。简而言之:您需要知道应用程序如何访问 MySQL。
评论
