10.12.3.1 MySQL 如何使用内存
MySQL 分配缓冲区和缓存以提高数据库操作的性能。默认配置旨在允许 MySQL 服务器在具有约512MB RAM 的虚拟机上启动。你可以通过增加某些缓存和缓冲区相关系统变量的值来改进 MySQL 性能,也可以修改默认配置以在有限内存系统上运行 MySQL。
以下列表描述了 MySQL 如何使用内存。在可应用的地方,相关系统变量将被引用。一些项目是存储引擎或特性专门的。
-
InnoDB 缓冲池是一个内存区域,用于存储 InnoDB 表、索引和其他辅助缓存的数据。为了提高高容量读操作的效率,该缓冲池被分割成页面,每个页面可以包含多行数据。为了提高缓存管理的效率,该缓冲池实现为一个链表结构的页面;Rarely 使用的数据将被从缓存中淘汰,使用 LRU 算法的变体。更多信息请见第17.5.1节,“Buffer Pool”。
缓冲池的大小对系统性能非常重要:
-
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
定义了缓冲池实例的数量。 -
如果缓冲池太小,可能会导致页面在短时间内被flush到缓冲池,然后又被要求再次使用,从而引起过度 churn。
-
如果缓冲池太大,可能会导致交换,因为内存的竞争。
-
-
存储引擎接口使得优化器能够提供关于扫描时要使用的记录缓冲区大小的信息。该缓冲区大小可以根据估算值进行调整。
InnoDB
使用这个可变大小缓冲区能力来实现行预取和减少 latch 和 B 树导航的开销。 -
所有线程共享
MyISAM
键缓冲区。系统变量key_buffer_size
确定其大小。对于每个
MyISAM
表,服务器打开索引文件一次;数据文件则根据同时运行的线程数被打开。对于每个并发线程,分配一个表结构、每个列的结构和大小为3 *
的缓冲区(其中N
N
是最大行长,不包括BLOB
列)。一个BLOB
列需要五到八个字节加上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性能_schema是用于监控MySQL服务器执行的低级别特性。性能_schema动态地分配内存,根据实际服务器负载来调整内存使用,而不是在服务器启动时分配所需的内存。一旦内存被分配,它将不会释放直到服务器重新启动。更多信息,请见第29.17节,“性能_schema内存分配模型”。
-
服务器用于管理客户端连接的每个线程都需要一些线程特定的空间。以下列表指出了这些空间和控制它们大小的系统变量:
-
一个堆栈(
thread_stack
) -
一个连接缓冲区(
net_buffer_length
) -
一个结果缓冲区(
net_buffer_length
)
连接缓冲区和结果缓冲区每个开始大小都等于
net_buffer_length
字节,但是在需要时可以动态地扩展到max_allowed_packet
字节。结果缓冲区在每个SQL语句执行后将会缩小到net_buffer_length
字节。在语句正在运行时,也会分配当前语句字符串的副本。每个连接线程用于计算语句摘要的内存。服务器为每个会话分配
max_digest_length
字节。请参阅第29.10节,“性能_schema语句摘要和采样”。 -
-
所有线程共享同一个基础内存。
-
当线程不再需要时,分配给它的内存将被释放并返回到系统中,除非线程回到线程缓存中。在这种情况下,内存仍然保持分配状态。
-
每个执行顺序扫描表的请求都分配一个读缓冲区。
read_buffer_size
系统变量确定缓冲区大小。 -
在读取行时(例如,遵循排序),可能会分配一个随机读缓冲区以避免磁盘寻道。
read_rnd_buffer_size
系统变量确定缓冲区大小。 -
所有连接都在单个循环中执行,且大多数连接可以不使用临时表完成。最常见的临时表是内存基表。包含大量行长(计算为所有列长度之和)或包含
BLOB
列的临时表将被存储在磁盘上。 -
大多数请求在进行排序时分配一个排序缓冲区和零到两个临时文件,取决于结果集的大小。请参阅第B.3.3.5节,“MySQL中存储临时文件的位置”。
-
几乎所有的解析和计算都在线程本地和可重用的内存池中进行。对于小项目,不需要内存开销,从而避免了正常的慢速内存分配和释放。只有对大字符串进行内存分配。
-
对于每个具有
BLOB
列的表,缓冲区将动态地扩大以读取更大的BLOB
值。如果您扫描一个表,缓冲区将增长到最大的BLOB
值。 -
MySQL 需要内存和描述符来缓存表。Handler 结构对于所有在用表都保存在表缓存中,并以“First In, First Out”(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
语句释放,因此对于执行了许多导致缓存的语句的服务器,cached 内存使用将增加,除非使用FLUSH PRIVILEGES
来释放。 -
在复制拓扑结构中,以下设置影响内存使用,可以根据需要进行调整:
-
在复制源服务器上,
max_allowed_packet
系统变量限制了源服务器将消息发送给其副本的最大大小。这一设置默认为64M。 -
系统变量
replica_pending_jobs_size_max
在多线程副本中设置了等待处理的消息所占用的最大内存量。该设置默认为128M。当需要时才分配内存,但如果您的复制拓扑偶尔处理大事务,这可能会被使用。这是一个软限制,允许处理更大的事务。 -
在复制源或副本上,系统变量
rpl_read_size
控制从二进制日志文件和中继日志文件中读取的最小数据量(以字节为单位)。默认值为8192字节。为每个读取二进制日志和中继日志文件的线程(包括源端的dump线程和副本端的协调线程)分配一个大小为该值的缓冲区。 -
系统变量
binlog_transaction_dependency_history_size
限制了内存中保留的行哈希数量。 -
系统变量
max_binlog_cache_size
指定了单个事务使用的内存上限。 -
系统变量
max_binlog_stmt_cache_size
指定了语句缓存使用的内存上限。
-
ps 和其他系统状态程序可能报告 mysqld 使用了大量内存。这可能是由于线程栈在不同内存地址上引起的。例如,Solaris 版本的 ps 将栈之间的未使用内存计入使用内存。要验证此问题,请使用 swap -s
命令检查可用交换空间。我们使用多种内存泄露检测器(包括商业和开源版本),因此应该没有内存泄露。