菜菜

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

MySQL如何使用内存

MySQL使用buffer和cache提升数据库操作性能。可以通过提高buffer和cache相关的系统变量来提升MySQL的性能。你也可以通过修改这些值来让MySQL运行在内存有限的系统上。

以下的列表列出了MySQL使用内存的几个方式。并且也列出了相关的系统变量。某些点是存储引擎相关的特色。

  • InnoDB buffer pool 用于缓冲InnoDB表的数据,索引,还有其他相关的buffer。为了高效的支持读操作,buffer pool被分成多个page,每个page都可以保存多行。为了能够高效的管理缓存,使用列表数据结构实现buffer pool,使用LRU逐出很少用的缓存。更多的信息参考
    14.4.3.1-InnoDB buffer pool 章节。

    buffer pool的大小对系统性能影响很大

    • 建议把innodb_buffer_pool_size大小配置成系统内存的百分之50到百分之75,在服务器启动时,InnoDB 使用malloc()函数一次性申请完buffer pool大小的内存。使用innodb_buffer_pool_size控制buffer pool大小,更多信息参考配置InnoDB buffer pool size
    • 在大内存系统中,把buffer pool切分成多个buffer pool instances可以提高并发性能。使用innodb_buffer_pool_instances控制buffer pool数目。
    • 太小的buffer pool可能会导致随后使用的page从buffer中刷出。
    • buffer pool太大可能会导致过多的内存页交换。
  • 所有线程共享MyISAM key buffer,使用key_buffer_size变量控制它的大小。

    当打开MyISAM数据表时,每个并发运行的线程都会打开一份数据文件,索引文件。对每个并发线程,都有一个表结构,每个列的列结构,3*N的buffer(N是最大的行长度,不包括BLOB列)。BLOB列真实字节数是原BLOB长度加5到8字节。MyISAM存储引擎维护一个内部使用的行buffer

  • myisam_use_mmap 系统变量设置成 1 会打开MyISAM表的内存映射。

  • 如果内部内存表太大(根据tmp_table_sizemax_heap_table_size),MySQL自动把数据表转换成磁盘临时表,基于MyISAM存储引擎。你可以增大最大临时表大小,更多参考8.4.4-MySQL内部临时表

    对于使用create table语句建立的MEMORY存储引擎的表,使用变量max_heap_table_size控制它的大小,超过这个限制后,内存表也不会转换成磁盘临时表。

  • MySQL Performance Schema是低级别监控MySQL的功能。出于性能的考虑,在服务启动时给Performance Schema设置一个固定的内存,也不要在运行时修改它的大小。

  • 每个管理客户端连接的线程还需要一些线程内部的空间。下面列出这些信息和相关的控制参数。

    每个连接用的buffer,结果用的buffer初始值是net_buffer_length 字节,最大动态增长到max_allowed_packet 字节数。在每个SQL statement 执行后, 结果用的buffer会收缩到net_buffer_length 字节数目。当statement 运行时,还需要开辟空间保存当前statement的字符串副本。
    每个连接线程使用内存计算statement 摘要。 参考22.10-Performacen Schema Statement Digests。在 MySQL 5.6.24 每个会话最多使用max_digest_length字节计算摘要。

  • 所有线程共享相同的基本内存。

  • 当某个线程不再需要时,如果线程不是返回线程缓存,线程的内存会返还给系统。这样的话,内存需要重新申请。

  • 执行顺序扫表的请求都会申请一个read buffer(read_buffer_size变量控制)

  • 任意顺序读表时(比如,排序),使用random-read buffer避免磁盘寻道(使用变量read_rnd_buffer_size控制buffer大小)。

  • 所有的连接操作都在一个过程中执行,大多数连接操作都可以在不需要临时表的情况下完成。大多数临时表是基于内存的hash表,如果行太长(所有列长度的总和)或者包含BLOB列,使用磁盘临时表。

  • 大多数请求需要执行sort操作,执行sort操作时,根据结果集大小需要开辟一个sort buffer和0到多个临时文件,更多请参考4.3.5-MySQL在哪里存储数据

  • 几乎所有解析和计算操作都在线程内,并且复用内存池。此时对于小item不需要重新申请空间,也能省去开辟空间,释放空间的过程。仅在大String时才需要申请空间。

  • 对于有BLOB的列,使用一个动态扩张的buffer来读取大的BLOB值。如果扫描一个表,buffer的大小和最大BLOB值相等。

  • MySQL正在使用的表缓存(table cache)需要内存和文件描述符。使用FIFO数据结构保存这个数据。初始化表缓存大小使用系统变量table_open_cache控制。更多请参考8.4.3.1-MySQL怎么打开关闭数据表

    MySQL同样需要内存保存表定义缓存。table_definition_cache系统变量定义了最大可保存的表定义(来自.frm文件)缓存。如果你使用的表数目多,你可以调整这个变量的值相对大一点来加速表打开的速度。和表缓存不同,表定义缓存不需要文件描述符,使用的内存也更少。

  • FLUSH TABLES语句和mysqladmin flush-tables命令关闭所有未使用的表,并且在并发线程执行完毕后,标记所有的表已经关闭,这个操作有效的释放了大部分在使用的内存。FLUSH TABLES语句只在所有的表都已经关闭后才会返回。

  • 执行GRANT,CREATE USER,CREATE SERVER,INSTALL PLUGIN语句产生服务器层缓存。执行REVOKE,DROP USER,DROP SERVER,UNINSTALL PLUGIN也不会释放这些缓存,多次执行这些语句会这个缓存内存使用变高。执行FLUSH PRIVILEGES语句可以清除这个缓存。

ps和其他某些系统命令可能会报告mysqld占用了大量内存。这个可能是因为不同内存地址的线程栈导致的。比如,Solaris系统的ps命令把栈之间未使用的空间当做使用的空间统计。如果需要确认这个,使用swap -s。我们使用过多个内存测试工具测试mysqld,所以应该不会出现memory leaks。

开启大内存页(large page)的支持

某些硬件和操作系统体系结构支持的内存页大小比默认页(通常是4KB)还大。实际的真实情况由底层的硬件和操作系统。应用程序通常会执行许多内存访问,提高内存页的大小来减少TLB(翻译后备缓冲器)缓存缺失,通常能提高性能。

在MySQL中,InnoDB能使用大的内存页来开辟缓冲池和其他内存池所需的空间。

MySQL中使用的标准页最大可到4MB。在最近发布的Solaris的super large pages功能最大可以到256MB。使用--super-large-pages打开此功能,关闭使用--skip-super-large-pages选项。

MySQL同样也支持Linux实现的large page(Linux中的Huge TLB)。

在使用Linux的large page功能之前,必须先开启内核对此功能的支持,也需要配置Huge TLB的内存池。更多请参考Huge TLB的文档,它通常在Linux 资源的/Documentation/vm/hugetlbpage.txt中。

最近Red Hat发布的Linux企业版中,默认开启此功能。可以使用如下命令确认。

shell> cat /proc/meminfo | grep -i huge
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 4096 kB

输出非空表示支持large page功能,值是0表示并未配置这个功能。

如果你需要配置large page功能,查看hugetlbpage.txt。

假设你的Linux内核支持large page,使用以下命令配置MySQL使用large page。通常,你需要把它放在rc文件中,或者放在有相同功能的启动文件中,这样在系统每次重启后都能执行它。这些命令需要在MySQL 服务启动之前启动。根据你的系统,配置合适的大小。

# Set the number of pages to be used.
# Each page is normally 2MB, so a value of 20 = 40MB.
# This command actually allocates memory, so this much
# memory must be available.
echo 20 > /proc/sys/vm/nr_hugepages
# Set the group number that is permitted to access this
# memory (102 in this case). The mysql user must be a
# member of this group.
echo 102 > /proc/sys/vm/hugetlb_shm_group
# Increase the amount of shmem permitted per segment
# (12G in this case).
echo 1560281088 > /proc/sys/kernel/shmmax
# Increase total amount of shared memory. The value
# is the number of pages. At 4KB/page, 4194304 = 16GB.
echo 4194304 > /proc/sys/kernel/shmall

对于MySQL的场景,正常来说通常把shmmax设置成接近shmall的值。

可以使用如下命令检查你的配置。

shell> cat /proc/meminfo | grep -i huge
HugePages_Total: 20
HugePages_Free: 20
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 4096 kB

使用hugetLb_shm_group的最后一步是给mysql用户一个"unlimited"的内存边界值。编辑/etc/security/limits.conf或者在mysqld_safe脚本内添加如下命令。

ulimit -l unlimited

在切换mysql用户之前。mysqld_safe中添加的ulimit命令会导致root用户设置的内存边界是unlimited(假设启动mysqld_safe的用户是root)。

MySQL默认关闭对large page的支持。启动参数添加--large-pages开启这个功能。比如,你可以在my.cnf中添加如下参数。

[mysqld]
large-pages

添加这个选项后,InnoDB的缓冲池和额外的内存池自动使用large page。如果InnoDB不能使用这个功能,回退到正常的page,然后打印一条错误日志:
Warning: Using conventional memory pool

确认正在使用的large page信息,请再次检查/proc/meminfo:

shell> cat /proc/meminfo | grep -i huge
HugePages_Total: 20
HugePages_Free: 20
HugePages_Rsvd: 2
HugePages_Surp: 0
Hugepagesize: 4096 kB
posted on 2020-05-05 17:39  好吧,就是菜菜  阅读(223)  评论(0编辑  收藏  举报