MySQL 为提高数据库操作的性能分配缓冲区和缓存。默认配置旨在允许 MySQL 服务器在具有大约 512MB RAM 的虚拟机上启动。您可以通过增加某些缓存和缓冲区相关的系统变量的值来提高 MySQL 性能。您也可以修改默认配置以在内存有限的系统上运行 MySQL。
以下列表描述了 MySQL 使用内存的一些方式。在适用情况下,会参考相关的系统变量。某些项目特定于存储引擎或功能。
InnoDB
缓冲池是一个内存区域,用于保存InnoDB
表、索引和其他辅助缓冲区缓存的数据。为了提高大容量读取操作的效率,缓冲池被划分为可以潜在地包含多个行的 页面。为了提高缓存管理的效率,缓冲池被实现为一个页面的链接列表;很少使用的數據會使用 LRU 算法的变体从缓存中淘汰。有关更多信息,请参见 第 17.5.1 节,“缓冲池”。缓冲池的大小对于系统性能至关重要
InnoDB
使用malloc()
操作在服务器启动时为整个缓冲池分配内存。innodb_buffer_pool_size
系统变量定义了缓冲池的大小。通常,推荐的innodb_buffer_pool_size
值为系统内存的 50% 到 75%。innodb_buffer_pool_size
可以在服务器运行时动态配置。有关更多信息,请参见 第 17.8.3.1 节,“配置 InnoDB 缓冲池大小”。在具有大量内存的系统上,您可以通过将缓冲池划分为多个 缓冲池实例 来提高并发性。
innodb_buffer_pool_instances
系统变量定义了缓冲池实例的数量。缓冲池太小会导致过度翻滚,因为页面被刷新出缓冲池,然后很快又需要再次使用。
缓冲池太大可能会导致交换,因为内存争用。
存储引擎接口使优化器能够提供有关用于扫描的记录缓冲区大小的信息,优化器估计这些扫描可能会读取多行。缓冲区大小可能会根据估计的大小而变化。
InnoDB
使用这种可变大小的缓冲能力来利用行预取,并降低闩锁和 B 树导航的开销。所有线程共享
MyISAM
键缓冲区。key_buffer_size
系统变量决定了其大小。对于服务器打开的每个
MyISAM
表,索引文件只打开一次;数据文件只为访问表的每个并发运行的线程打开一次。对于每个并发线程,会分配一个表结构、每个列的列结构和一个大小为3 *
的缓冲区(其中N
N
是最大行长度,不包括BLOB
列)。BLOB
列需要 5 到 8 个字节加上BLOB
数据的长度。MyISAM
存储引擎为内部使用维护一个额外的行缓冲区。myisam_use_mmap
系统变量可以设置为 1 以启用所有MyISAM
表的内存映射。如果内部内存临时表变得太大(由
tmp_table_size
和max_heap_table_size
系统变量确定),MySQL 会自动将表从内存格式转换为磁盘格式,使用InnoDB
存储引擎。您可以按照 第 10.4.4 节,“内部临时表在 MySQL 中的使用” 中所述,增加允许的临时表大小。对于使用
CREATE TABLE
显式创建的MEMORY
表,只有max_heap_table_size
系统变量决定表可以增长多大,并且不会转换为磁盘格式。MySQL Performance Schema 是用于在低级别监控 MySQL 服务器执行情况的特性。Performance Schema 动态地增量分配内存,根据实际服务器负载调整其内存使用,而不是在服务器启动时分配所需的内存。一旦内存被分配,它就不会被释放,直到服务器重启。有关详细信息,请参阅 第 29.17 节,“Performance Schema 内存分配模型”。
服务器用来管理客户端连接的每个线程都需要一些特定于线程的空间。以下列表指示这些空间以及哪些系统变量控制它们的大小:
一个栈 (
thread_stack
)一个连接缓冲区 (
net_buffer_length
)一个结果缓冲区 (
net_buffer_length
)
连接缓冲区和结果缓冲区都以等于
net_buffer_length
字节的大小开始,但根据需要动态扩展到max_allowed_packet
字节。每个 SQL 语句执行后,结果缓冲区缩小到net_buffer_length
字节。在语句执行期间,还会分配当前语句字符串的副本。每个连接线程都使用内存来计算语句摘要。服务器为每个会话分配
max_digest_length
字节。请参阅 第 29.10 节,“Performance Schema 语句摘要和采样”。所有线程共享相同的基内存。
当不再需要线程时,分配给它的内存将被释放并返回给系统,除非线程返回到线程缓存。在这种情况下,内存仍然分配着。
每个执行表顺序扫描的请求都会分配一个 读取缓冲区。
read_buffer_size
系统变量决定缓冲区大小。当以任意顺序读取行(例如,遵循排序)时,可能会分配一个 随机读取缓冲区 以避免磁盘查找。
read_rnd_buffer_size
系统变量决定缓冲区大小。所有联接都在一次通过中执行,大多数联接甚至不需要使用临时表。大多数临时表是基于内存的哈希表。行长度很大的临时表(计算为所有列长度的总和)或包含
BLOB
列的临时表存储在磁盘上。大多数执行排序的请求都会分配一个排序缓冲区,并根据结果集大小分配 0 到 2 个临时文件。请参阅 第 B.3.3.5 节,“MySQL 存储临时文件的位置”。
几乎所有解析和计算都在线程本地和可重用内存池中完成。对于小项目不需要内存开销,从而避免了正常的慢速内存分配和释放。内存仅分配给意外的大字符串。
对于每个包含
BLOB
列的表,一个缓冲区会动态扩展以读取更大的BLOB
值。如果您扫描一个表,缓冲区会增长到与最大的BLOB
值一样大。MySQL 需要内存和描述符来为表缓存。所有正在使用的表的处理程序结构都保存在表缓存中,并以 “先进先出” (FIFO) 的方式进行管理。
table_open_cache
系统变量定义了初始表缓存大小;请参阅 第 10.4.3.1 节,“MySQL 如何打开和关闭表”。MySQL 还需要内存用于表定义缓存。
table_definition_cache
系统变量定义了可以存储在表定义缓存中的表定义数量。如果您使用大量表,您可以创建更大的表定义缓存来加快表的打开速度。表定义缓存占用更少的空间,与表缓存不同,它不使用文件描述符。FLUSH TABLES
语句或 mysqladmin flush-tables 命令会关闭所有未使用的表,并将所有正在使用的表标记为在当前执行线程完成时关闭。这实际上释放了大多数正在使用的内存。FLUSH TABLES
不会在所有表关闭之前返回。服务器会根据
GRANT
、CREATE USER
、CREATE SERVER
和INSTALL PLUGIN
语句的结果,在内存中缓存信息。相应的REVOKE
、DROP USER
、DROP SERVER
和UNINSTALL PLUGIN
语句不会释放此内存,因此对于执行许多会导致缓存的语句实例的服务器来说,缓存内存的使用量会增加,除非使用FLUSH PRIVILEGES
来释放它。在复制拓扑中,以下设置会影响内存使用,可以根据需要进行调整:
复制源上的
max_allowed_packet
系统变量限制了源发送到其副本以进行处理的最大消息大小。此设置默认为 64M。多线程副本上的系统变量
replica_pending_jobs_size_max
设置了用于保存等待处理的消息的最大内存量。此设置默认为 128M。内存仅在需要时分配,但如果您的复制拓扑偶尔处理大型事务,则可能会使用它。这是一个软限制,可以处理更大的事务。复制源或副本上的
rpl_read_size
系统变量控制从二进制日志文件和中继日志文件中读取的最小数据量(以字节为单位)。默认值为 8192 字节。对于每个从二进制日志和中继日志文件中读取的线程,包括源上的转储线程和副本上的协调器线程,都会分配一个大小为该值大小的缓冲区。复制源上的
binlog_transaction_dependency_history_size
系统变量限制了作为内存历史记录保存的行哈希数。复制源上的
max_binlog_cache_size
系统变量指定了单个事务的内存使用量上限。复制源上的
max_binlog_stmt_cache_size
系统变量指定了语句缓存的内存使用量上限。
ps 和其他系统状态程序可能会报告 mysqld 使用了很多内存。这可能是由不同内存地址上的线程堆栈引起的。例如,Solaris 版本的 ps 会将堆栈之间的未使用内存计为已使用内存。要验证这一点,请使用 swap -s
检查可用交换空间。我们使用多个内存泄漏检测器(商业和开源)测试 mysqld,因此不应该有内存泄漏。