14

真香,MySQL 8.0.20,10倍性能提升!!!

 3 years ago
source link: http://mp.weixin.qq.com/s?__biz=MjM5MjIxNDA4NA%3D%3D&%3Bmid=2649739161&%3Bidx=1&%3Bsn=5b193204fc9979463699f440773bdd80
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

MbeQNbn.jpg!web

最新的MySQL  8.0.20版本添加了一个新的优化,即doublewrite性能优化,一举解决了困扰MySQL多时的写入性能瓶颈。 在极端I/O密集的应用场景下,能有超过10倍的写入性能提升

doublewrite机制是MySQL InnoDB存储引擎所独有的功能,用以解决物理硬件发生宕机时可能产生的partial write问题。简单来说,在MySQL InnoDB存储中,脏页列表的刷新,会 先顺序地写入到doublewrite,然后再随机写入到每个页应该所在的问题 。更详细的内容可参见《MySQL技术内幕:InnoDB存储引擎》一书。

有同学会说,为什么地球最强大的Oracle数据库没有采用类似机制?

这样的机制会导致一个脏页写入变成了2次写,即doublewrite。不得不承认, Oracle数据库没有InnoDB存储引擎在数据安全性方面严谨 ,至少在脏页刷新这块。 Oracle若发生partial write的问题,只能通过RMAN恢复物理页

由于脏页需要写2次,开启doublewrite特性会导致性能下降一半么?之前大多数DBA的理解是,由于doublewrite的128个页对象是先顺序写的,因此性能开销较小。一般在5% ~ 25%的性能开销。

然而,在极端并发场景下,doublewrite的性能退化会非常厉害,使得MySQL写入性能大幅退化,甚至远超之前5%~25%的认知。

在讲MySQL 8.0对于doublewrite的优化前,先看看这10年MySQL InnoDB存储引擎对于脏页刷新的性能优化措施。

MySQL 5.5

在MySQL 5.5版本中,脏页的刷新是在Master线程中定期完成。比如每秒刷多少脏页,每10秒刷新多少脏页,且在代码中硬编码规定每次刷新的脏页数量最多为200个。

此外,除了完成脏页的刷新工作,Master线程还需要负责undo回收,日志写入等工作。在SSD设备出现前,由于IOPS都很小,因此这个工作机制并没有太大的问题。

此外,除了脏页列表的刷新,还有一种称为LRU的脏页刷新,这种刷新机制是当LRU可替换页不多的情况下,需要刷新LRU最尾部的脏页。在MySQL 5.5版本中,这个刷新机制可能在用户线程触发,即当一个用户发起普通SELECT操作,都可能触发LRU脏页刷新,从而阻塞用户的查询。

Percona 5.5版本对比官方MySQL 5.5版本提升非常多,大多是针对上述的I/O刷新优化策略。可以说,Percona 5.5版本就是当时的网红版本,大多互联网公司的MySQL分支版本也是在这个时期萌芽和发展起来的。

MySQL 5.6

针对5.5版本的刷新性能瓶颈,MySQL 5.6版本做了几个优化工作。首先,引入变量innodb_io_capacity,设置每次能刷新最大的脏页数量。从而提升MySQL在SSD存储设备下的性能。调整变量innodb_log_file_size 4G大小限制,提升MySQL在刷新时的稳定性。

其次,引入Page Cleaner线程,将脏页刷新都交由此线程完成,减轻Master线程的负载。同时,新增自适应刷新多发,通过重做日志增速,对刷新脏页进行“补偿”。使得在I/O密集的场景下,性能更为平滑。

最后,将LRU脏页刷新放到后台线程完成,不再阻塞用户线程的查询。

zeAJZzf.png!web

到5.6版本时,Percona版本在刷新性能上已无太大优势。正是从这个版本开始,Percona最大的优势仅在移植MariaDB的线程池到MySQL。

MySQL 5.7

5.6版本引入变量innodb_buffer_pool_instances,拆分一个大缓冲池为多个缓冲池,降低缓冲池latch冲突,从而提升性能。但在引入这个机制后,原来的一个脏页列表,变为了多个脏页列表。但Page Cleaner刷新线程仅有一个,因此多个缓冲池机制未能充分发挥写入的并行性能。

在5.7版本中引入变量innodb_page_cleaner,可设置Page Cleaner线程的数量,从而提升脏页刷新的效率。

需要特别提及的是, 每个Page Cleaner线程并不是和缓冲池一一绑定的 。因为这样每个缓冲池依然是单个线程刷新,并不能充分发挥优势。因此Page Cleaner线程是由1个协调线程和多个工作线程组成,协调线程也可以是工作线程,可以负责脏页的刷新工作。

2QB7Fj6.png!web

上图中可以看到变量innodb_page_cleaners设置为了8,但其实是由1个协调线程和7个刷新的工作线程组成。具体工作机制可见源码中的函数 buf_flush_page_coordinator_thread

MySQL 8.0

到5.7版本为止,对MySQL脏页刷新本身的优化都已完成,需要进入到更为核心的底层优化,即对于latch锁的优化。这部分工作的难度相对前面来说,更大一些。不过,相信对于经验丰富的内核开发人员来说,也只是piece of breeze~~~

MySQL 8.0对InnoDB Redo模块进行全面的设计与重构,解决了之前log mutex这把困扰性能多年的“大锁”。在事务运行过程中,需要将redo重做日志写入到Log Buffer,这时需要log mutex这把锁的保护。很明显,任何用户线程的DML操作都会引发log mutex这把锁的获取和释放,从而导致性能热点与瓶颈。

2qi2Mva.png!web

在进行完Redo模块的重构后,MySQL在写入方面能有30%+的性能提升。

再见,doublewrite瓶颈

在最新的MySQL 8.0.20版本中,终于对doublewrite机制进行了彻底的优化。doublewrite最大的瓶颈在于虽然Page Cleaner线程已经是由多个线程组成并负责脏页的刷新,但最后这些脏页都需要先拷贝到doublewrite内存中,然后再写入到doublewrite物理存储中。而拷贝脏页到doublewrite内存中,需要持有doublewrite的mutex。同样的,这把锁也是竞争的热点,在大并发写入场景下,会导致性能瓶颈。

eENJBjF.png!web

8.0.20版本对于doublewrite的优化在于将一个doublewrite对象拆分为了多个独立doublewrite文件保存。每个文件中各有1个FLUSH_LIST脏页刷新doubewrite段(segment)和LRU_LIST脏页刷新doublewrite段。

imqI7f3.png!web

新引入的参数innodb_doublewrite_files,默认值为2,表示有2个doublewrite文件组成,每个doublewrite文件又有2个doublewrite对象。即,默认配置下,将原来的doublewrite对象拆分为了4个doublewrite对象,从而提升并发写入的性能。

EbeU3mA.png!web

可以看到在上述doublewrite优化措施下, 在并发128、256、512、1024线程下,8.0.20版本的性能不会有退化,对比8.0.19版本则性能可有10多倍的显著提升

总结

从MySQL 5.6版本开始,官方一直在对SSD这类超快存储设备下的写入性能和稳定性进行优化,目前MySQL 8.0.20版本下,InnoDB存储引擎整体的脏页刷新机制如下,多个缓冲池对象,多个脏页刷新线程,多个Doublewrite对象,多个异步I/O回调线程......可以发现优化措施无外乎 拆分 ,线程拆分,锁拆分,从而提升整体性能。

BVFBfub.png!web

MySQL 8.0.20版本对于doublewrite有了比较彻底的优化,相信未来doublewrite不会再成为拖累性能的元凶。然而,必须牢记的是, 所谓的10倍性能提升有且仅发生在大并发写入场景下

最后,姜老师想问,有同学还能想到InnoDB存储引擎性能优化点么?欢迎留下你的宝贵意见。

-----------------------

公众号:破产码农(ID:manongBR)

B站:Jiang老师

微博:破产码农

知乎:破产码农

长按下图二维码关注,将感受到一个有趣的灵魂,每篇文章都会有新惊喜。

yUniIny.jpg!web

-----------------------

感谢你的阅读,下面是1个抽奖链接按钮,6月13日晚上20点开奖,一共200元,88个红包,感谢大家的支持。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK