MySQL · 8.0版本更新 · 性能优化篇

MySQL · 8.0版本更新 · 性能优化篇,第1张

概述介绍《MySQL · 8.0版本更新 · 性能优化篇》开发教程,希望对您有用。

《MysqL · 8.0版本更新 · 性能优化篇》要点:
本文介绍了MysqL · 8.0版本更新 · 性能优化篇,希望对您有用。如果有疑问,可以联系我们。

本文主要总结下MysqL在8.0版本和性能相关的一些改动,随着新的小版本的发布,本文将不断进行更新,直到正式GA.

已更新版本

MysqL 8.0.0

MysqL 8.0.0

WL#9387: InnoDB: Group purging of rows by table ID

这个问题最早是facebook的工程师Domas报的一个BUG,InnoDB使用多线程来进行Undo Purge *** 作,但分配undo的策略不太合理,直接轮询分配.这意味着如果从一张表上删除大量数据,这N个purge线程可能产生大量的索引锁冲突(例如索引页合并及重组织)

在WL#9387中,在parse undo log时,通过table_ID进行分组存储,在分发时确保同一个table ID的记录被分配给同一个线程.(参考函数 trx_purge_attach_undo_recs)

当然这也意味着合理的不会产生冲突的单表 *** 作,无法利用到多线程purge了,也算是一个弊端.

WL#8423: InnoDB: Remove the buffer pool mutex

这个算是众望所归的改进了,由Percona贡献的补丁(BUG#75534),主要是对InnoDB的buffer pool mutex这个大锁进行了拆分,降低锁冲突:

nameDesc
buf_pool_t::LRU_List_mutex用于保护LRU链表,例如从LRU链表上刷脏或驱逐Page
buf_pool_t::free_List_mutex保护free List及withdraw List(online resize)
buf_pool_t::zip_free_mutex保护zip_free数组,该数组用于维护对压缩表产生的非标准page size的内存维护,ref buf/buf0buddy.cc
buf_pool_t::zip_hash_mutex保护zip_hash,其中存储压缩页block
buf_pool_t::flush_state_mutex保护init_flush,n_flush,no_flush等数组

分配空闲block(buf_LRU_get_free_block):

从free List获取: buf_pool_t::free_List_mutex

从unzip_lru/lru上驱逐一个空闲page,需要buf_pool_t::LRU_List_mutex

批量扫描LRU(buf_do_LRU_batch): buf_pool_t::LRU_List_mutex

批量扫描FLUSH_List(buf_do_flush_List_batch): buf_pool_t::flush_List_mutex

脏页加入到flush_List(buf_flush_insert_into_flush_List): buf_pool_t::flush_List_mutex

脏页写回磁盘后,从flush List上移除(buf_flush_write_complete): buf_pool_t::flush_state_mutex/flush_List_mutex

从LRU上驱逐Page(buf_LRU_free_page):buf_pool_t::LRU_List_mutex,及buf_pool_t::free_List_mutex(buf_LRU_block_free_non_file_page)

buf_flush_LRU_List_batch 使用mutex_enter_Nowait 来获取block锁,如果获取失败,说明正被其他session占用,忽略该block.

有些变量的修改从通过buf_pool_t::mutex保护,修改成通过memory barrior来保护(os_rmb or os_wmb),例如下面几个函数中均有体现:

btr_search_enable()buf_resize_thread()buf_get_withdraw_depth()buf_LRU_get_free_block()

通过对锁的拆分,降低了全局大锁的竞争,提升了buffer pool的扩展性,这个特性其实在Percona Server中很多年前就有了,但直到MysqL8.0版本才合并进来.

WL#7170: InnoDB buffer estimates for tables and indexes

主要是用于为优化器提供更准确的信息,即数据是存在与磁盘还是内存中,这样优化器可以更准确的做出代价计算.

增加一个全局对象(buf_stat_per_index_t)来管理所有的索引页计数

为了避免引入新的全局锁开销,实现并使用一个lock-free的hash结构("include/ut0lock_free_hash.h)来存储索引信息,key值为索引ID.(目前索引ID具有唯一性,但不排除未来可能发生改变).

增加计数:

1. Page刚从磁盘读入内存 (buf_page_io_complete --> buf_page_monitor)

2. 创建一个新的page (btr_page_create)

递减计数: Page从LRU上释放时进行递减(buf_LRU_block_remove_hashed)

增加新的information_schema.innodb_cached_indexs 打印每个索引在内存中的page个数,其结构如下:

MysqL> show create table INNODB_CACHED_INDEXES\G*************************** 1. row ***************************

table: INNODB_CACHED_INDEXES

Create table: CREATE TEMPORARY table `INNODB_CACHED_INDEXES` (

`SPACE_ID` int(11) unsigned NOT NulL DEFAulT '0',

`INDEX_ID` bigint(21) unsigned NOT NulL DEFAulT '0',

`N_CACHED_PAGES` bigint(21) unsigned NOT NulL DEFAulT '0'

) ENGINE=MEMORY DEFAulT CHARSET=utf8

1 row in set (0.00 sec)### 和表名/索引名关联SELECT

tables.name AS table_name,

indexes.name AS index_name,

cached.n_cached_pages AS n_cached_pages

FROM

information_schema.innodb_cached_indexes AS cached,

information_schema.innodb_sys_indexes AS indexes,

information_schema.innodb_sys_tables AS tables

WHERE

cached.index_ID = indexes.index_ID

AND

indexes.table_ID = tables.table_ID;

相关worklog: WL#7168: API for estimates for how much of table and index data that is in memory buffer

WL#9383: INNODB: ADD AN OPTION TO TURN OFF/ON DEADLOCK CHECKER

增加选项,可以动态关闭死锁检测,这对诸如热点更新这样的场景效果显著,之前已专门写了篇博客,感兴趣的自取

BUG#77094

这个优化来自alisql的贡献,主要是优化了InnoDB Redo的扩展性问题,通过双buffer机制,允许在写日志到磁盘的同时,也允许进行mtr commit,具体参阅我写的这篇月报

WL#7093: Optimizer provIDes InnoDB with a bigger buffer

为了减少对Btree的锁占用,InnoDB在读取数据时实际上是有一个小的缓存buffer.对于连续记录扫描,InnoDB在满足比较严格的条件时采用row cache的方式连续读取8条记录(并将记录格式转换成MysqL Format),存储在线程私有的row_prebuilt_t::fetch_cache中;这样一次寻路就可以获取多条记录,在server层处理完一条记录后,可以直接从cache中取数据而无需再次寻路,直到cache中数据取完,再进行下一轮.

在WL#7093中引入了新的接口,由于优化器可以估算可能读取的行数,因此可以提供给存储引擎一个更合适大小的row buffer来存储需要的数据.大批量的连续数据扫描的性能将受益于更大的record buffer.

Record buffer由优化器来自动决定是否开启,增加新的类Record_buffer进行管理,Record buffer的大小最大不超过128KB,目前是hard code的,不可以配置.

判断及分配record buffer函数: set_record_buffer,并通过新的API接口(handler::ha_set_record_buffer)传到引擎层

buffer本身是引擎无关的,在sever层分配,通过handler成员m_record_buffer传递到引擎层.

增加新的接口,判断是否支持Record buffer,目前仅InnoDB支持,需要满足如下条件 (ref set_record_buffer):

access type 不是 ref,ref_or_null,index_merge,range,index 或者ALL

不是临时表

不是loose index scan

进入InnoDB引擎层判断((row_prebuilt_t::can_prefetch_records))

return select_lock_type == LOCK_NONE // 只读查询

&& !m_no_prefetch // 允许prefetch

&& !templ_contains_blob // 没有BLOB,TEXT,JsON,GEOMETRY这些大列

&& !templ_contains_fixed_point // 不是空间数据类型DATA_POINT

&& !clust_index_was_generated // 需要用户定义的primary key 或者唯一索引(被隐式的用作Pk)

&& !used_in_HANDLER // 不是通过HANDLER拜访的

&& !innodb_API // 不是通过类似innodb memcached拜访的

&& template_type != ROW_MysqL_DUMMY_TEMPLATE //不是check table

&& !in_fts_query; // 不是全文索引查询

在InnoDB中,当record buffer被配置时,就使用server层提供的record buffer,而不是row_prebuilt_t::fetch_cache

官方博客对此改进的介绍:http://MysqLserverteam.com/MysqL-8-0-faster-batch-record-retrIEval/

WL#9250: Split LOCK_thd_List and LOCK_thd_remove mutexes

该Worklog的目的是改进短连接场景下的性能,对thd List的 *** 作可能导致比较高的锁竞争.

办理方案也比较传统,就是进行分区,将链表thd_List划分成多个数组,目前为8个分区,相应的锁LOCK_thd_removeLOCK_thd_List锁也进行了分区.

内存溢出PHP培训学院每天发布《MysqL · 8.0版本更新 · 性能优化篇》等实战技能,PHP、MysqL、liNUX、APP、Js,CSS全面培养人才。

总结

以上是内存溢出为你收集整理的MySQL · 8.0版本更新 · 性能优化篇全部内容,希望文章能够帮你解决MySQL · 8.0版本更新 · 性能优化篇所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

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

原文地址: http://outofmemory.cn/sjk/1156662.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-01
下一篇 2022-06-01

发表评论

登录后才能评论

评论列表(0条)

保存