MySQL内存分配

原文链接: MySQL Memory Allocation -- by Rick James

原文日期: Created 2010; Refreshed Oct, 2012, Jan, 2014

翻译人员: 铁锚

翻译日期: 2014年5月28日

MySQL 内存分配—— 快速设置方案

如果仅使用MyISAM存储引擎,设置 key_buffer_size
为可用内存的20%,(再加上设置 innodb_buffer_pool_size = 0 )

如果仅使用InnoDB存储引擎,设置 innodb_buffer_pool_size
为可用内存的 70%, (设置 key_buffer_size = 10M,很小但不是0.)

调优mysql的实践经验:

  • 首先拷贝 my.cnf / my.ini 文件副本.
  • 根据使用的存储引擎及可用内存,设置 key_buffer_size 和innodb_buffer_pool_size.
  • 慢查询(Slow queries)的修正一般是通过添加索引(indexes),改变表结构(schema),改变 SELECT 语句 来实现,而不是通过数据库调优.
  • 不要随便设置查询缓存(Query cache),除非你真正掌握它的优缺点以及适用场景.
  • 不要改变其他的参数,除非你遇到了相应的问题(如最大连接数问题, max connections).
  • 确保修改的是 [mysqld] 这一节下的内容,而不是其他部分.

下面向您展示一些实际的细节. (本文不涉及 NDB Cluster)

什么是索引缓存(key_buffer)?

MyISAM引擎的缓存分为两部分.

  • 索引块(Index blocks,每个1 KB,BTree结构、存放于 .MYI 文件) 缓存到 “key buffer” 中.
  • 数据块缓存(Data block caching, 存放于 .MYD 文件中)交给操作系统负责, 所以确保留下了适量的空闲内存(给操作系统).

警告: 某些类型的操作系统总是报告说内存使用超过90%,虽然实际上还有很多的空闲内存.

SHOW GLOBAL STATUS LIKE ‘Key%‘; 执行后计算 Key_read_requests / Key_reads 的值, 如果比值较大(比如大于10), 那么 key_buffer 就足够了.

什么是缓存池(buffer_pool)?

InnoDB将所有缓存都放在 “buffer pool” 中, 缓存池的大小通过 innodb_buffer_pool_size
控制. 包含被打开表(open tables)中的 16KB一块的数据/索引块,此外还有一些附加开销.

MySQL 5.5(以及带插件的 5.1版本)允许您指定 块大小(block size)为 8 KB或4 KB. MySQL 5.5可以有多个缓冲池,因为每个缓存池有一个互斥锁, 所以设置多个池可以缓解一些互斥锁瓶颈.

更多InnoDB调优信息

另一种计算缓存大小的方法

将主缓存(main cache)设置为最小值; 如果同一台机器上有许多其他应用在跑, 并且/或者RAM内存小于2GB, 那么可以这样指定.

SHOW TABLE STATUS;  显示各个数据库中所有表的状态.

  • 计算所有MyISAM表的 Index_length 值的总和. 让 key_buffer_size 小于等于这个和值.
  • 计算所有 InnoDB表 Data_length + Index_length 值的总和. 设置 innodb_buffer_pool_size 为不超过总和值的110%.

如果有内存交换(swapping发生),需要将两个参数适量地按减小一些.

执行下面的SQL语句查看适合的参数值. (如果有很多表,可能耗时几分钟.)

SELECT  ENGINE,
        ROUND(SUM(data_length) /1024/1024, 1) AS "Data MB",
        ROUND(SUM(index_length)/1024/1024, 1) AS "Index MB",
        ROUND(SUM(data_length + index_length)/1024/1024, 1) AS "Total MB",
        COUNT(*) "Num Tables"
    FROM  INFORMATION_SCHEMA.TABLES
    WHERE  table_schema not in ("information_schema", "performance_schema")
    GROUP BY  ENGINE;

互斥锁瓶颈

MySQL 是单核CPU时代设计的,且可以很容易移植到不同的硬件体系架构中. 不幸的是,这导致了对连结锁(interlock)操作的凌乱. 在几个重要的流程中存在少量(非常少)的“互斥(mutexes)”. 包括:

  • MyISAM的 key_buffer
  • 查询缓存(Query Cache)
  • InnoDB的buffer_pool

随着多核CPU的盛行,互斥问题引起了MySQL的性能问题. 一般来说,CPU超过 4~8 核越多,则MySQL变得越慢,而不会更快. MySQL 5.5 中 InnoDB 的增强版 Percona XtraDB 对多核CPU的支持要好很多; 实际的限制大致是
32核, CPU核心超过这个数后性能会达到瓶颈 ,但不再下降. MySQL 5.6版声称最多可以支持48核.

超线程和多核CPU

简单的处理方式:

  • 禁用超线程(HyperThreading)
  • 停用超过8个核心以上的部分
  • 超线程这里主要是指以前的超线程技术,因此此部分可能不一定正确.

超线程适合拿来做营销宣传,但对(专用应用的)性能极不友好. 有两个处理单元在共享同一个物理缓存. 如果这两个线程在做同样的事情,缓存会相当高效. 如果这俩线程在干不同的事,他们会相互妨碍到另一个(超)线程的缓存项.

总的来说MySQL在多核处理上并不占优势. 所以,如果禁用超线程(HT),剩下的核心将会运行得更快一点.

32位操作系统和MySQL

(译者注: 肯定64位的MySQL在 32位OS上跑不起来...)

首先,操作系统(以及硬件?) 会限制进程不能使用4GB RAM中的全部,如果有 4G内存的话. 如果物理 RAM 超过 4 GB, 超过的部分在32位操作系统中不可访问,也是不可用的.

其次,操作系统可能会限制单个进程最大使用多少内存.

例如:FreeBSD的 maxdsiz ,默认为512 MB.

示例:

$ ulimit -a
...
max memory size (kbytes, -m) 524288

因此,确定了 mysqld有多少可用内存, 就可以设置为 20% ~ 70%,但需要适当的减少一些.

如果系统报错,例如 [ERROR] /usr/libexec/mysqld: Out of memory (Needed xxx bytes) , 可能是MySQL申请了超过操作系统允许的内存范围. 需要减小缓存设置.

64位OS与32位MySQL

64位操作系统不受4 GB内存的限制,但32位MySQL依然受这个限制.

如果你有 4 GB以上的内存,那么可以设置:

  • key_buffer_size = 20%(所有RAM的),但不要超过3 GB.
  • buffer_pool = 3G

当然最好的办法是将MySQL换成64位版本.

64位OS与64位MySQL

  • 只使用MyISAM引擎: (5.0.52 ~ 5.1.23之前的)key_buffer_size有 4GB的硬性限制. 详情请参考
    MySQL 5.1 限制(restrictions)               
                   在更高版本中,设置 key_buffer_size 为 20%的RAM. 在(my.cnf / my.ini)中加上 innodb_buffer_pool_size = 0.
  • 只使用InnoDB引擎: 设置 innodb_buffer_pool_size = 70%的RAM. 如果内存很大,并使用 5.5(及以上)版本,可以考虑使用 多个缓存池. 推荐设置 1 - 16 个  innodb_buffer_pool_instances, 每个都不小于1 GB. (很抱歉,没有最优设置为多少个的具体参考指标;但应该不能设置太多).

与此同时,设置 key_buffer_size = 20M(很小,但不是零)

如果你在数据库中混合使用多个引擎,将两个值都降低一些.

最大连接数,线程栈

(max_connections,thread_stack)

每个“线程”都要占用一定的内存. 通常为 200 KB左右; 因此 100个线程大概就是 20 MB. 如果设置 max_connections
= 1000,那大概就需要 200 MB,或者更多. 同时连接数太大可能会引起其他某些问题,这点需要注意.

在5.6(或 MariaDB5.5)中,可以选择线程池与 max_connections 交互. 这是一个高级话题.

线程栈溢出很少出现. 如果确实发生了,可以设置: thread_stack = 256K

点击查看更多关于max_connections, wait_timeout,连接池的讨论

table_cache(table_open_cache)

(某些版本中名字不一样).

操作系统对单个进程能打开的文件数有限制. 打开每个表需要 1-3个文件. 每个表分区(PARTITION)等价于一个表. 在分区表上的多数操作都会打开所有的分区.

在 *nix中, ulimit 显示文件限制是多少. 最大值一般是上万,但有可能被设置为 1024. 这就限制了只能打开300个左右的表.  更多关于ulimit的讨论请点击这里

(这一段是有争议的.) 另一方面,表缓存(过去?)的实现方式很低效 —— 查找通过线性扫描来完成. 因此,设置 table_cache 为几千确实会使得 mysql变慢. (基准测试也证明了这一点.)

你可以通过  SHOW GLOBAL STATUS; 查看系统的性能信息, 并计算 每秒打开数(opens/second): Opened_files /Uptime , 如果这个值较大,例如大于 5, 那么应该加大 table_cache; 如果很小,比如是 1,通过减小 table_cache 值,可能会对性能有所改善.

查询缓存(Query Cache)

简短的回答: 设置 query_cache_type = OFFquery_cache_size = 0

QC(Query Cache)实际上是将 SELECT语句与结果集(resultsets)进行散列映射.

详细的回答…… 关于“查询缓存”有许多种观点; 其中许多是负面的.

  • 新手警告! QC与key_buffer和buffer_pool完全无关.
  • 当命中时, QC速度快如闪电. 要创建一个运行快1000倍的基准测试并不难.
  • 在QC中只有一个互斥锁(译者注: 锁越少,就是锁钥匙越少,高并发时就会激烈竞争/等待).
  • 除非将QC设置为OFF与0,否则每次查询都会去对比一遍.
  • 真相,互斥锁会发生碰撞,即使 query_cache_type = DEMAND (2).
  • 真相,互斥锁会发生碰撞,即便设置了 SQL_NO_CACHE.
  • 查询语句只要变了一点点(即使多了个空格)都可能导致在QC中生成多个不同的缓存项.

修改”是代价高昂与频繁的:

  • 在一个表中发生任何 write 事件, QC中对应到这个表的所有条目都会被清除.
  • 即便在只读从服务器(readonly Slave)上也是这样.
  • 清除使用的是线性算法来执行,所以QC较大(比如200MB)则会导致速度明显地变慢.

要查看QC的执行效率如何,执行 SHOW GLOBAL STATUS LIKE ‘Qc%‘; 然后计算read的命中率: Qcache_hits / Qcache_inserts, 如果大于5,则 QC的效率还不错.

如果QC适合你的应用,那么我推荐:

  • query_cache_size = 不超过50M
  • query_cache_type = DEMAND
  • 在所有 SELECT 语句中指明 SQL_CACHE 或 SQL_NO_CACHE, 根据哪些查询可能会从QC缓存中命中.

深入了解Query Cache

thread_cache_size

这是一个很小的调优项. 设置为 0 会降低线程(连接)创建的速度. 设置为较小的值(比如 10) 是比较好的. 该选项对RAM没有多少影响.

它是服务器额外保持的线程数量,不会影响实际线程数; 起限制作用的是 max_connections.

二进制日志

如果为 复制(replication) 或 时间点恢复(point-in-time recovery) 启用二进制日志(通过 og_bin开启), 则服务器将一直记录二进制日志(binary logs). 也就是说,可能慢慢地占用磁盘. 建议设置
expire_logs_days = 14 ,只保留14天的日志记录.

swappiness

RHEL,非常英明地,允许用户自己控制 OS 如何进行预先内存交换分配. 总的来说这是很好的策略,但对MySQL来说则是一个灾难.

(感觉翻译的有点不流畅,本段原文为: RHEL, in its infinite wisdom, decided to let you control how aggressively the OS will preemptively swap RAM. This is good in general, but lousy for MySQL)

MySQL期望相当稳定的内存分配 —— 缓存(大部分)是预先分配的; 线程(大都)是限制数量的. 任何内存交换都可能极大地损害MySQL的性能.

设置很高的swappiness值,会丢失一些内存,因为操作系统试图为以后的分配保留大量的自由空间(MySQL一般是不需要的).

设置swappiness = 0,不交换,在内存不足时操作系统可能会崩溃,. 我宁愿MySQL一卡一卡的,也不希望他崩了.

对于MySQL-only(专用)服务器, 中间数(比如5 ?)可能是一个很好的值.

NUMA

OK,是时候了解一些CPU管理内存的架构了. 我们先看 NUMA(Non-Uniform Memory Access, 非统一内存寻址). 每个CPU(或多路服务器中的每个socket(CPU插座)) 都挂载有一部分内存. 这使得访问本地(local) RAM 非常快, 而访问挂载在其他 CPU下的RAM要慢上数十个周期.

接着看操作系统. 在(RHEL ?)很多情形下,有两个行为:

  • OS分配的内存固定到 “first(第一个)” CPU名下.
  • 接着分配的其他内存也默认分配到第一个CPU名下,直到它满了.

现在问题来了.

  • OS与MySQL分配完了第一个 CPU的所有RAM.
  • MySQL分配了第二个 CPU的部分内存.
  • 操作系统OS还需要分配一些其他内存.

Ouch —— 一个CPU需要分配内存,但自己名下控制的RAM已经耗尽了,所以它将MySQL的部分内存置换出去. 渣渣!

可能的解决方案:配置BIOS内存分配为 “interleave”(交错). 这将防止过早交换(premature swapping),代价是有一半左右的 RAM 访问要跨CPU(off-CPU). 嗯,不论如何访问的代价都较大, 如果真的要使用所有内存的话.

整体性能损失/收益:几个百分点.

大内存分页(huge pages)

这里有另一个硬件性能陷阱.

CPU访问RAM,特别是将64位地址映射到某个地方, 比如 128 GB 或“真实”的RAM,会使用TLB. (TLB =Translation Lookaside Buffer,旁路转换缓冲.) TLB是硬件实现的内存关联查找表; 将64位的虚拟地址转换到实际的物理地址.

因为TLB是一个小的,虚拟寻址的缓存,有时会发生 “misses”(未命中),那就会进入物理RAM来查找. 这是两次查找是很费时的操作,所以应该避免.

通常,内存被 “分页” 为 4 KB一页,TLB实际上将高位的(64 - 12)位映射到一个特定页面. 而低12位通过虚地址转换得到完整的地址.

例如,128 GB的RAM按 4 KB分页需要 32M(3200万个) page-table条目. 这太大了, 远远超过TLB的容量. 所以陷入了“Huge page”的骗局.

随着硬件与操作系统的支持,使部分RAM成为巨型页面成为可能 ,比如说4 MB(而不是4 KB). 这使得TLB条目剧减,对这部分RAM来说分页单元是4 MB. 因此,巨大的页面相当于是不分页的(non-pagable).

现在内存被分为 pagable 和 non pagable 两部分; 哪些部分 non pagable 是合理的? 在MySQL中, innodb_buffer_pool 就是一个完美的使用者. 通过正确地配置这些,InnoDB能跑得更快一点:

  • 启用 Huge pages
  • 通知操作系统分配适当的数量(和 buffer_pool 个数一致)
  • 通知MySQL使用huge pages

innodb memory usage vs swap 该帖包含有很多需要关注点以及如何设置的细节.

整体性能收益:几个百分点. Yawn.

MEMORY引擎(ENGINE=MEMORY)

这是一个不常用的存储引擎,算是MyISAM和InnoDB的替代品. 其数据不是持久的,所以其应用范围相当有限. 内存表的大小受限于 max_heap_table_size ,默认值是16 MB. 我提起它,以防你将此值修改得太大;这会偷偷地占用可用的RAM.

如何设置变量(VARIABLEs)

在文本文件my.cnf中(Windows上是my.ini),添加一行,例如

innodb_buffer_pool_size = 5G

即: 变量名,等号“=”,变量的值. 有些值允许缩写,如M代表 million(1048576),G代表billion.

要让服务器看到这些设置,必须将其放到配置文件的 “[mysqld]”节下.

对 my.cnf 或 my.ini的设置不会立即生效,需要你重启服务器.

大多数的设置可以通过 root 账号登陆后在线修改  (其他 SUPER权限账号也可以),例如:

SET @@global.key_buffer_size = 77000000;

注意:此处不允许设置 M 或 G 等单位.

查看全局变量的设置信息:

mysql> SHOW GLOBAL VARIABLES LIKE "key_buffer_size";
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| key_buffer_size | 76996608 |
+-----------------+----------+

注意,这部分设置MySQL会向下取整,对齐到一定的数字.

你可能需要修改两个地方(执行SET 并修改my.cnf),以使修改立即生效,并且下次重启后依然是同样的值(不管是手动,还是其他原因重新启动)

Web服务器

像Apache这样的web服务器使用多线程来处理. 如果每个线程打开一个 MySQL连接,可能会超过允许的最大连接数. 确保将web服务器的 MaxClients (或类似参数) 设置为一个合理的值(如50以下).

工具

MySQLTuner

TUNING-PRIMER

上面是几个对内存设置建议的工具. 其中有一个误导性条目:

Maximum possible memory usage: 31.3G (266% of installed RAM)

可能使用的内存最大值为: 31.3G (可能是物理内存的 266%)

不要让它吓到你,这些工具使用的公式过于保守了. 他们假设所有 max_connections 都在使用并且处于活跃状态,并正在执行一些内存密集型的工作.

Total fragmented tables: 23

有碎片的tables: 23 个

这意味着 OPTIMIZE TABLE 可能会有作用. 我建议对表设置高百分比的 “free space”(见SHOW TABLE STATUS) 或者你知道对什么表做了大量的删除/更新操作. 不过,不必费心频繁地对table进行OPTIMIZE 优化整理. 一个月一次可能就够了.

文章修改记录

2010创建;2012年10月更新,2014年1月更新;

更深入的文章:

MySQL 5.6的调优

InnoDB性能优化的基本知识(终极版)

MySQL安装后的10项优化设置

通过 MySQL论坛::性能 联系作者 ——里克·詹姆斯

里克·詹姆斯的MySQL相关文档

提示,调试、howto、优化相关等等……

Rick‘s RoTs (Rules of Thumb -- lots of tips)

Memory Allocation (caching, etc)

Character Set and Collation problem solver

Converting from MyISAM to InnoDB -- includes differences between them

Big DELETEs - how to optimize

Compound INDEXes plus other insights into the mysteries of INDEXing

Partition Maintenance (DROP+REORG) for time series

Entity-Attribute-Value -- a common, poorly performing, design patter; plus an alternative

Find the nearest 10 pizza parlors (efficient searching on Latitude + Longitude)

Alter of a Huge table

Latest 10 news articles -- how to optimize the schema and code for such

Pagination, not with OFFSET, LIMIT

Data Warehouse techniques (esp., Summary Tables)

Techniques on efficiently finding a random row (On beyond ORDER BY RAND())

GUID/UUID Performance (type 1 only)

IP Range Table Performance

MySQL Limits

Galera Limitations (with Percona XtraDB Cluster / MariaDB)

Rollup Unique User Counts

Best of MySQL Forum

MySQL内存分配

时间: 2024-10-10 06:30:58

MySQL内存分配的相关文章

18、MySQL内存体系架构及参数总结

内存结构: Mysql 内存分配规则是:用多少给多少,最高到配置的值,不是立即分配 图只做大概参考 全局缓存包括: global buffer(全局内存分配总和) = innodb_buffer_pool_size                      -- InnoDB高速缓冲,行数据.索引缓冲,以及事务锁.自适应哈希等 +innodb_additional_mem_pool_size    -- InnoDB数据字典额外内存,缓存所有表数据字典 +innodb_log_buffer_si

MySQL内存调优

原文链接: MySQL Memory Allocation -- by Rick James原文日期: Created 2010; Refreshed Oct, 2012, Jan, 2014 翻译人员: 铁锚翻译日期: 2014年5月28日 MySQL 内存分配-- 高速设置方案假设仅使用MyISAM存储引擎,设置 key_buffer_size为可用内存的20%,(再加上设置 innodb_buffer_pool_size = 0 ) 假设仅使用InnoDB存储引擎,设置 innodb_bu

mysql innodb 引擎内存分配项

MySQL做为最流行的开源数据库,innodb在5.6开始已成为默认引擎,在5.7中系统吧引擎也有myisam改为innodb,可以看出innodb引擎现在已经可以满足绝大部分的业务需求,内存分配直接影响MySQL整体运行效率,有innodb_buffer,还有各种cache_size,这些选项都需要合理分配,合理利用服务器有限的内存让MySQL跑的更嗨皮,直接影响的选项有下面几个: innodb_buffer_pool_size:缓冲区,存放表数据.索引数据,大小至关重要,直接影响sql执行时

MYSQL,innodb_buffer_pool_size内存分配

为MYSQL.innodb_buffer_pool_size=8G.MySQL一起动就会将占用掉8G内存(觉得TOP能够看到内存被使用了8G),可是近期才细致研究一下.原来不是这种(可能自己对Linux malloc内存分配也仅仅是知道了个皮毛吧).MySQL启动时实际仅仅是在虚拟内存中分配了地址空间,而并没有真正的映射到物理内存上. 由于malloc分配内存是先在虚拟内存中分配地址的,到实际使用时才真正的映射到物理内存 因此这个地方.假设因为机器内存使用不当.到了MySQL真正要映射物理内存时

MYSQL,innodb_buffer_pool_size内存分配方式

以前一直以为MYSQL,innodb_buffer_pool_size=8G,MySQL一起动就会将占用掉8G内存(认为TOP可以看到内存被使用了8G),但是最近才仔细研究一下,原来不是这样的(可能自己对Linux malloc内存分配也只是知道了个皮毛吧),MySQL启动时实际只是在虚拟内存中分配了地址空间,而并没有真正的映射到物理内存上. 因为malloc分配内存是先在虚拟内存中分配地址的,到实际使用时才真正的映射到物理内存 因此这个地方,如果由于机器内存使用不当,到了MySQL真正要映射物

MySQL 内存监控

上一篇blog介绍了因为sql查询information_schema表而导致内存暴涨的case. 今天顺便做了一个thd内存的监控: 先来介绍下MySQL的内存: 1. 线程内内存:thd->mem_root, 线程在执行sql的过程中,申请的内存从thd->mem_root进行分配,在sql结束的时候释放. 2. 线程外内存:对象专有的mem_root; 比如,table, table_share,st_transactions等都有专有的mem_root. 所以: 对于sql引起的内存暴

mysql内存参数整理和条调优以及内存统计

date:20140530auth:Jin 参考:http://dev.mysql.com/doc/refman/5.5/en/server-status-variables.html#http://dev.mysql.com/doc/refman/5.5/en/memory-use.htmlhttp://blog.csdn.net/wyzxg/article/details/7268122http://blog.csdn.net/wyzxg/article/details/7268175 *.

Tcmalloc优化Mysql内存管理

TCMalloc是什么? TCMalloc(Thread-Caching Malloc)与标准glibc库的malloc实现一样的功能,但是TCMalloc在效率和速度效率都比标准malloc高很多.TCMalloc是google-perftools工具中的一个(gperftools四个工具分别是:TCMalloc.heap-checker.heap-profiler和cpu-profiler),这个工具是开源的,以源码形式发布.如果觉得自己维护一个内存分配器麻烦的话,可以考虑将TCMalloc

线上mysql内存持续增长直至内存溢出被killed分析

来新公司前,领导就说了,线上生产环境Mysql库经常会发生日间内存爆掉被killed的情况,结果来到这第一天,第一件事就是要根据线上服务器配置优化配置,同时必须找出现在mysql内存持续增加爆掉的原因,虽然我主业已经不是数据库更不是dba了. 这个业务上基本山算是OLTP,盘中都是很简单的SQL,所以性能上虽然有些SQL有些慢,但看过slow-log和performance_schema,可以忽略不计. 初步了解,应用是java开发的,但是应用端没有出现过OOM的情况,也不见卡死或者越来越慢的情