MySQL数据库性能优化有哪些技巧?

MySQL数据库性能优化有哪些技巧?,第1张

1.存储引擎的选择如果数据表需要事务处理,应该考虑使用InnoDB,因为它完全符合ACID特性。如果不需要事务处理,使用默认存储引擎MyISAM是比较明智的。并且不要尝试同时使用这两个存储引擎。思考一下:在一个事务处理中,一些数据表使用InnoDB,而其余的使用MyISAM.结果呢?整个subject将被取消,只有那些在事务处理中的被带回到原始状态,其余的被提交的数据转存,这将导致整个数据库的冲突。然而存在一个简单的方法可以同时利用两个存储引擎的优势。目前大多数MySQL套件中包括InnoDB、编译器和链表,但如果你选择MyISAM,你仍然可以单独下载InnoDB,并把它作为一个插件。很简单的方法,不是吗?

2.计数问题如果数据表采用的存储引擎支持事务处理(如InnoDB),你就不应使用COUNT(*)计算数据表中的行数。这是因为在产品类数据库使用COUNT(*),最多返回一个近似值,因为在某个特定时间,总有一些事务处理正在运行。如果使用COUNT(*)显然会产生bug,出现这种错误结果。

3.反复测试查询查询最棘手的问题并不是无论怎样小心总会出现错误,并导致bug出现。恰恰相反,问题是在大多数情况下bug出现时,应用程序或数据库已经上线。的确不存在针对该问题切实可行的解决方法,除非将测试样本在应用程序或数据库上运行。任何数据库查询只有经过上千个记录的大量样本测试,才能被认可。

4.避免全表扫描通常情况下,如果MySQL(或者其他关系数据库模型)需要在数据表中搜索或扫描任意特定记录时,就会用到全表扫描。此外,通常最简单的方法是使用索引表,以解决全表扫描引起的低效能问题。然而,正如我们在随后的问题中看到的,这存在错误部分。

5.使用“EXPLAIN”进行查询当需要调试时,EXPLAIN是一个很好的命令,下面将对EXPLAIN进行深入探讨。

LRU机制在实际运行过程中,是会存在巨大的隐患的:

MySQL的预读机制带来的隐患:所谓的预读机制,就是当你从磁盘加载一个数据页的时候,可能会连带着把这个数据页相邻的其它数据页也加载到缓存里去。

例如,现在有两个空闲的缓存页,然后在加载一个数据页的时候,连带着把他的一个相邻的数据页也加载到缓存里了,正好每个数据页放入一个空闲的缓存页。但是实际上只有一个缓存页被访问了,另外一个通过预读机制被加载进来的缓存页,其实并没有人访问,但是此时这两个缓存页都是放在LRU链表的前面。这个时候没有空闲的缓存页,如果要加载新的数据,就要把LRU链表队尾的缓存页刷入磁盘,而不是无人访问的那个缓存页。

会触发预读机制的场景:

为了解决简单的LRU链表的问题,MySQL在设计LRU链表的时候,实际上采取的是冷热数据分离的思想;之前的问题都是因为所有缓存页都混在了一个LRU链表中导致的。

真正的LRU链表,会被拆分成热数据和冷数据两个部分,冷热数据的比例是由innodb_old_blocks_pct参数控制的,默认是37,也就是说冷数据占比为37%。这个时候,LRU链表实际上看起来是下面这个样子的:

数据页第一次被加载到缓存的时候,其实是被放在冷数据链表的头部,后面1秒之后,如果你再次访问这个缓存页,那这个缓存页会被移动到热数据链表的头部,这个时间是有innodb_old_blocks_time这个参数控制的,默认1000ms。

也就是说,必须是一个数据页被加载到缓存页之后,在1s之后,你访问这个缓存页,他才会被挪动到热数据区域的链表头部去。

因为假设你加载了一个数据页到缓存去,然后过了1s之后你还访问了这个缓存页,说明你后续很可能会经常访问它,这个时间限制就是1s,因此只有1s后你访问了这个缓存页,他才会给你把缓存页放到热数据区域链表的头部去。

预读机制以及全表扫描加载进来的一大堆缓存页,他们会放在哪里?肯定是放在LRU链表的冷数据区域的前面,假设这个时候热数据区域已经有很多被频繁访问的缓存页了,你会发现热数据区域还是存放被频繁访问的缓存页的,只要热数据区域有缓存页被访问,他还是会被移动到热数据区域的链表头部去。

所以此时预读机制和全表扫描加载进来的一大堆缓存页,此时都在冷数据区域里,跟热数据区域里的频繁访问的缓存页,没有关系。

如果仅仅是全表扫描的查询,此时你肯定是在1s内就把一大堆缓存页加载进来,然后访问了这些缓存页一下,通常这些 *** 作1s内就结束了,所以基于目前的一个机制,可以确定的是,那些缓存页是不会从冷数据区域转移到热数据区域的。

除非在冷数据区域的缓存页,在1s之后还被访问了,那么此时他们就会判定为未来可能会被频繁访问的缓存页,然后移动到热数据区域的链表头部去。

假设此时缓存页不够用了,需要淘汰一些缓存页,此时会怎样?

直接就是可以找到LRU链表中的冷数据区域的尾部的缓存页,他们肯定是之前被加载进来的,而且加载进来1s过后都没人访问过,说明这个缓存页压根儿就没人愿意去访问他,他就是冷数据。所以此时就直接淘汰冷数据区域的尾部的缓存页,刷入磁盘就可以了。

之前提到如果一个缓存页被访问了,就会把他移动到LRU链表的热数据区域首位,这么频繁的移动会导致性能不是很好。所以MySQL对LRU链表热数据区域的访问规则做了优化,只有在热数据区域的后3/4部分的缓存页被访问了,才会被移动到链表头部;链表前面1/4的缓冲页被访问,是不会被移动的。

首先,并不是在缓存页满的时候,才会挑选LRU冷数据区域尾部的几个缓存页刷入磁盘,而是有一个后台线程,每隔一段时间就会把LRU链表的冷数据区域尾部的一些缓存页刷入磁盘,然后清空这几个缓存页,并把他们加入到free链表中。

所以大家会发现,只要有这个后台线程定时运行,可能你的缓存页都还没有用完呢,就给你把一批冷数据的缓存页刷入磁盘,清空出来一批缓存页,那么你就多了一批可以使用的空闲缓存页了。

如果仅仅只是把LRU链表中的冷数据区域的缓存页刷入磁盘,明显是不够的;

LRU链表中的热数据区域里的很多缓存页可能会被频繁的修改,这些数据不可能永远放在内存中,后台线程会在MySQL不繁忙的时候,把flush链表中的缓存页都刷入磁盘中,这样,被修改过的数据就被刷入到磁盘文件中了。

只要flush链表中的一些缓存页被刷入磁盘,那这些缓存页也会从flush链表和lru链表中移除,然后加入到free链表中。

所以,一边不停的加载数据到缓存页中,不停的查询和修改缓存数据,然后free链表中的缓存页不停的在减少,flush链表中的缓存页不停的在增加,lru链表中的缓存页不停的在增加和移动。

另外一边,后台线程不停的把LRU链表的冷数据区域的缓存页及flush链表的缓存页刷入到磁盘,来清空缓存页,然后flush链表和LRU链表中的缓存页不停的在减少,free链表中的缓存页在不停的增加。

如果实在没有空闲的缓存页,那就会把LRU链表冷数据区域尾部的缓存页刷入磁盘,然后清空。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/7407942.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-05
下一篇 2023-04-05

发表评论

登录后才能评论

评论列表(0条)

保存