Mysql占用内存过高 数据全在缓存?
作者:互联网
Mysql占用内存过高 数据全在缓存?
top
列 | 合计(G) | 说明 |
---|---|---|
VIRT | 14.236 | 进程申请的虚拟内存大小,申请不意味着分配,该值与实际内存消耗关系不大。 |
RES | 1.9747 | 进程常驻内存,包含进程间共享内存。 |
SHR | 0.0171 | 进程间共享内存,该值是推算出来的,存在误差,意义不大。 |
内存分为全局内存和线程内存
全局内存
- innodb_buffer_pool_size Innodb 在mysql启动的时候一次性分配整个内存给bufferpool。占得比重很大,要重点关注。这个内存和你数据库访问量connections无关。如果你的数据访问量不大,但内存占用很高的话,就要要重点关注buffer pool相关的参数。
- key_buffer_size。如果数据里面有很多myisam的表。这个参数就需要重点关注。所有相关表的请求都会占用到这块内存。
线程内存
首先这些参数并不是固定的,max_connections * 参数的值 然后加起来就可能是线程占用的缓存大小。如果你的访问量很大,连接数很多。下面参数就可能影响内存的占用。
每个参数都对应一种数据操作,所以你需要思考业务场景和sql代码到底哪一种操作多一些。
read_buffer_size 顺序读rows
sort_buffer_size 对表排序 sort by
read_rnd_buffer_size 随机读rows where条件多的
join_buffer_size 表关联join多的
tmp_table_size 临时表,通常是单行数据大,而且table整体很大的
什么是buffer/cache?
buffer和cache是两个在计算机技术中被用滥的名词,放在不通语境下会有不同的意义。在Linux的内存管理中,这里的buffer指Linux内存的:Buffer cache。这里的cache指Linux内存中的:Page cache。
翻译成中文可以叫做缓冲区缓存和页面缓存。在历史上,它们一个(buffer)被用来当成对io设备写的缓存,而另一个(cache)被用来当作对io设备的读缓存,这里的io设备,主要指的是块设备文件和文件系统上的普通文件。
但是现在,它们的意义已经不一样了。在当前的内核中,page cache顾名思义就是针对内存页的缓存,说白了就是,如果有内存是以page进行分配管理的,都可以使用page cache作为其缓存来管理使用。
当然,不是所有的内存都是以页(page)进行管理的,也有很多是针对块(block)进行管理的,这部分内存使用如果要用到cache功能,则都集中到buffer cache中来使用。
(从这个角度出发,是不是buffer cache改名叫做block cache更好?)然而,也不是所有块(block)都有固定长度,系统上块的长度主要是根据所使用的块设备决定的,而页长度在X86上无论是32位还是64位都是4k。
明白了这两套缓存系统的区别,就可以理解它们究竟都可以用来做什么了。
什么是page cache?
Page cache主要用来作为文件系统上的文件数据的缓存来用,尤其是针对当进程对文件有read/write操作的时候。
如果你仔细想想的话,作为可以映射文件到内存的系统调用:mmap是不是很自然的也应该用到page cache?在当前的系统实现里,page cache也被作为其它文件类型的缓存设备来用,所以事实上page cache也负责了大部分的块设备文件的缓存工作。
cache是为了弥补高速设备和低速设备的鸿沟而引入的中间层,最终起到加快访问速度的作用。
而buffer的主要目的是进行流量整形,把突发的大数据较小规模的I/O整理成平稳的小数量较大规模的 I/O,以减少响应次数(比如从网上下电影,你不能下一点点数据就写一下硬盘,而是积攒一定量的数据以后一整块一起写,不然硬盘都要被你玩坏了)。
这两者还是有相关性的。首先cache是缓存,buffer是缓冲,虽然翻译有那么一个字的不同,但这不是重点。
为了说明这个问题,让我将他们分开来说:read cache(读缓存),read buffer(读缓冲),write cache(写缓存),write buffer(写缓冲)。
无论缓存还是缓冲,其实本质上解决的都是读写速度不匹配的问题,从这个角度,他们非常相似。
首先讨论读缓存跟读缓冲。读缓存跟读缓冲的最大区别在于,读缓存的目标数据是始终有效的,如果不从缓存中读取,也可以直接读取实际数据,只不过实际数据读取会慢一些,当这个数据在缓存中,读取速度将会变快。当一个缓存中的数据被多次读取,实际上就减少了该数据从慢速设备中读取的量,这就存在某种算法去选择「什么数据需要保存在cache中」,因为尽可能多的让cache命中能提高性能。先进入cache的数据不一定先被读取,甚至说进入cache的数据有可能永远不被读取就被清除了,因此read cache呈现出非常明显的随机访问特性。
而读缓冲buffer的数据则不是始终有效,而是实时生成的数据流,每当buffer满或者主动flush buffer的时候触发一次读取,对于小数据,这样可以减少读取次数,对于大数据,这可以控制单次读取的数据量。换句话说,无论数据量大还是小,单次读取数据量都按照buffer尺寸进行归一化了。通常来说,先喂给buffer的数据一定会先被读取,所有buffer的数据几乎一定会被读取,这是很明显的顺序访问特性。
【注】从上面的情况看到,读缓存以及读缓冲很明确的反应出了我所说的表面特性。而其本质特性在于cache的目标是减少读取总量每次cache命中都减小了读取总量。而buffer并不能减少读取总量,只能规整化每次读取数据的尺寸大小。
说到write cache跟write buffer?
我们先说write buffer,write buffer是read buffer的对应,对于小数据的写入,它需要填满write buffer再进行一次写入,对于大数据,大数据会被分割到buffer尺寸的大小分批写入。因此,write buffer 的用处在于使得每次写入的数据量相对固定。如果一次写入4k对某个设备来说效率最高,那么把buffer定为4k,小数据积攒到4k写一次,大数据分割到每个碎片4k多次写入,这样就是write buffer的用处。最后我们来说write cache。所谓write cache,就是要设法减少写入次数。也就是说,如果某些数据需要产生多次写入,那么使用cache就可以只将最终数据写入,导致最终写入数据减少。在实际应用中,我们有时会使用到write buffer跟write cache的合体形态。buffer本身需要规整尺寸,与此同时,buffer还允许多次随机写入,使得多次写入的数据只用写入最后一次,这属于cache的特性。BT软件使用的写缓存往往具有类似特性,因而这种形态它同时既是buffer又是cache。正因为在写入场合buffer跟cache没有那么明显的分界,所以才会有产生buffer跟cache究竟有啥区别的疑问。
简单结论:
- Buffer不是缓存,国内常用的翻译是缓冲区。
- 其次,大部分场景中,Buffer是特指内存中临时存放的IO设备数据——包括读取和写入;而Cache的用处很多——很多IO设备(例如硬盘、RAID卡)上都有Cache,CPU内部也有Cache,浏览器也有Cache。
- Buffer并非用于提高性能,而Cache的目的则是提高性能。
- 涉及到IO设备读写的场景中,Cache的一部分本身就是Buffer的一种。如果说某些场合Buffer可以提升IO设备的读写性能,只不过是因为Buffer本身是Cache系统的一部分,性能提升来自于Cache机制。
- Buffer占用的内存不能回收,如果被强行回收会出现IO错误。Cache占用的内存,除实现Buffer的部分外都可以回收,代价则是下一次读取需要从数据的原始位置(通常是性能更低的设备)读取。
- 在IO读写过程中,任何数据的读写都必然会产生Buffer,但根据Cache算法,可能会有相当部分数据不会被Cache。
把整个MySQL加载进内存中_MySQL中的这个池子,读取极快
Mysql 中数据是要落盘的,这点大家都知道。读写磁盘速度是很慢的,尤其和内存比起来更是没的说。但是,我们平时在执行 SQL 时,无论写操作还是读操作都能很快得到结果,并没有预想中的那么慢。
可能你会说我有索引啊,有索引当然快了。但是铁子,索引文件也是存储在磁盘上的,查找过程会产生磁盘 I/O。如果同时对某行数据进行多次操作,那岂不是要重复产生很多次磁盘 IO 吗?
可能你想到了,那我把数据存在内存里不就可以了吗?内存速度比磁盘快,这准没毛病。没错,那该怎么存呢? 这就是我们今天所要讲的主题——缓冲池(buffer pool)。
初识缓冲池
上边我们提到过了,执行 SQL 对某一行进行操作时,总不能每次都直接进行磁盘操作吧。这样是极其损害硬盘的,而且读取速度很慢。所以有一个缓冲地带————缓冲池。简单来说就是一块内存区域。它就是为了访问数据时避免每次都去磁盘读取,把最常访问的数据放在缓存里,提高数据的访问速度。看一下缓冲池在整个MySQL架构里处于什么样的地位,有一个整体的认识。
它的内部组成部分。在缓冲池中,除数据页和索引页外还有多种类型:
缓冲池的应用
当执行一条SQL时它有什么作用?
如果是读操作,要查找的数据所在的数据页在内存中时,则将结果返回。否则会把对应的数据页加载到内存中,然后再返回结果。
对于写操作,如果要修改的行所在的数据页 在内存中,则修改后返回对应的结果。如果不在内存中,则会从磁盘里将该行所对应的数据页读到内存中再进行修改。
到这里已经知道,为何操作磁盘慢,但执行SQL不慢。是因为缓存池的存在,很大程度上减少了磁盘I/O带来的开销。
缓冲池的预读机制
我们可以看出来,只要不存在或减少磁盘 I/O,执行速度自然就会变快。那么对于加载数据页这种无法避免的磁盘 I/O 来说是否有更好的方式呢?既然避免不了,那减少磁盘 I/O 的次数总可以吧?
这就是我们要讲的 Mysql 中「预读」的新特性,它是 Innodb 通过在缓冲池中提前读取多个数据页来优化 I/O 的一种方式。因为磁盘读写的时候,是按照页的方式来读取的(你可以理解为固定大小的数据,例如一页数据为 16K),每次至少读入一页的数据,如果下次读取的数据就在页中,就不用再去磁盘上读取了,从而减少了磁盘 I/O。
可以在命令行通过如下命令查看对应的页大小:
show variables like 'innodb_buffer_pool_size';
缓冲池的空间管理
缓存池这么好,为何不把所有的数据都放在缓存池里面呢?
- 缓存的数据容易丢失
- 缓存池也是有大小限制的(通过参数的设置)
如果所有的数据页都存在缓存里,那我怎么管理这些数据页呢?当我新写入的数据超过了这个缓存池设定的大小,会不会把其他数据页挤出去?
传统 LRU 淘汰法
缓冲池是基于传统的 LRU 方法来进行缓存页管理的,我们先来看下如果使用 LRU 是如何管理的。
LRU,全称是 Least Recently Used,中文名字叫作「最近最少使用」。从名字上就很容易理解了。
这里分两种情况:
- 缓存页已在缓冲池中
这种情况下会将对应的缓存页放到 LRU 链表的头部,无需从磁盘再进行读取,也无需淘汰其它缓存页。
如下图所示,如果要访问的数据在 6 号页中,则将 6 号页放到链表头部即可,这种情况下没有缓存页被淘汰。
- 缓存页不在缓冲池中
缓存页不在缓冲中,这时候就需要从磁盘中读入对应的数据页,将其放置在链表头部,同时淘汰掉末尾的缓存页
如下图所示,如果要访问的数据在 60 号页中,60 号页不在缓冲池中,此时加载进来放到链表的头部,同时淘汰掉末尾的 17 号缓存页。
是不是看上去很简单,同时也能满足缓冲池淘汰缓存页的方法?但是我们来思考几个问题:
- 预读失效
上面我们提到了缓冲池的预读机制可能会预先加载相邻的数据页。假如加载了 20、21 相邻的两个数据页,如果只有页号为 20 的缓存页被访问了,而另一个缓存页却没有被访问。此时两个缓存页都在链表的头部,但是为了加载这两个缓存页却淘汰了末尾的缓存页,而被淘汰的缓存页却是经常被访问的。这种情况就是预读失效,被预先加载进缓冲池的页,并没有被访问到,这种情况是不是很不合理。
- 缓冲池污染
还有一种情况是当执行一条 SQL 语句时,如果扫描了大量数据或是进行了全表扫描,此时缓冲池中就会加载大量的数据页,从而将缓冲池中已存在的所有页替换出去,这种情况同样是不合理的。这就是缓冲池污染,并且还会导致 MySQL 性能急剧下降。
冷热数据分离
这样看来,传统的 LRU 方法并不能满足缓冲池的空间管理。因此,Msyql 基于 LRU 设计了冷热数据分离的处理方案。
也就是将 LRU 链表分为两部分,一部分为热数据区域,一部分为冷数据区域。
当数据页第一次被加载到缓冲池中的时候,先将其放到冷数据区域的链表头部,1s(由 innodb_old_blocks_time 参数控制) 后该缓存页被访问了再将其移至热数据区域的链表头部。
为什么要等 1s 后才将其移至热数据区域呢?你想想,如果数据页刚被加载到冷数据区就被访问了,之后再也不访问它了呢?这不就造成热数据区的浪费了吗?要是 1s 后不访问了,说明之后可能也不会去频繁访问它,也就没有移至热缓冲区的必要了。当缓存页不够的时候,从冷数据区淘汰它们就行了。
另一种情况,当我的数据页已经在热缓冲区了,是不是缓存页只要被访问了就将其插到链表头部呢?不用我说你肯定也觉得不合理。热数据区域里的缓存页是会被经常访问的,如果每访问一个缓存页就插入一次链表头,那整个热缓冲区里就异常骚动了,你想想那个画面。
那咋整呢?Mysql 中优化为热数据区的后 3/4 部分被访问后才将其移动到链表头部去,对于前 1/4 部分的缓存页被访问了不会进行移动。
MySQL配置参数优化
关于MySQL配置文件优化参数这里不多赘述....本文详细解说了缓存、缓冲及个别参数
MySQL优化-innodb重要参数优化
标签:缓存,buffer,缓冲,cache,全在,内存,Mysql,数据 来源: https://www.cnblogs.com/homjun/p/14971443.html