zoukankan      html  css  js  c++  java
  • 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时代设计的,且能够非常easy移植到不同的硬件体系架构中. 不幸的是,这导致了对连结锁(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中相应到这个表的全部条目都会被清除. 
    • 即便在仅仅读从server(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没有多少影响. 
    它是server额外保持的线程数量,不会影响实际线程数; 起限制作用的是 max_connections. 
    二进制日志
    假设为 复制(replication) 或 时间点恢复(point-in-time recovery) 启用二进制日志(通过 og_bin开启), 则server将一直记录二进制日志(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(专用)server, 中间数(比方5 ?)可能是一个非常好的值.
    NUMA
    OK,是时候了解一些CPU管理内存的架构了. 我们先看 NUMA(Non-Uniform Memory Access, 非统一内存寻址). 每一个CPU(或多路server中的每一个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. 
    要让server看到这些设置,必须将其放到配置文件的 “[mysqld]”节下.
    对 my.cnf 或 my.ini的设置不会马上生效,须要你重新启动server. 
    大多数的设置能够通过 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),以使改动马上生效,而且下次重新启动后依旧是相同的值(无论是手动,还是其它原因又一次启动)
    Webserver
    像Apache这种webserver使用多线程来处理. 假设每一个线程打开一个 MySQL连接,可能会超过同意的最大连接数. 确保将webserver的 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 

  • 相关阅读:
    -bash: fork: Cannot allocate memory 问题的处理
    Docker top 命令
    docker常见问题修复方法
    The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)
    What's the difference between encoding and charset?
    hexcode of é î Latin-1 Supplement
    炉石Advanced rulebook
    炉石bug反馈
    Sidecar pattern
    SQL JOIN
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5220010.html
Copyright © 2011-2022 走看看