[MySQL 5.6] Percona Server 5.6.15(及之前版本)的优化点

Percona版本的MySQL 5.6目前已经GA了几个版本,本文大概理一下Percona版本相比Oracle版本的主要不同之处。

本文基于Percona Server 5.6.15,不排除后续有更多的新特性。
总的来说,Percona版本的5.6最吸引我的地方在于,强化了后台线程的优先级(例如Page cleaner),让后台线程多干活,同时弱化用户线程对全局资源(buffer pool)的管理介入;这在大负载写入的场景下,可以有助于性能稳定(例如通常我们在redo log推进到74%左右的时候,会发现急剧的性能下降,性能下降一半都是常有的事儿…)


1.社区PORT的特性

 

似乎最初port自mariadb,并做了持续的优化,具体的针对线程池的分析见我另外一篇博客(http://mysqllover.com/?p=826);值得一提的是,那篇博客中提到的blueprint也差不多在新版本中实现了,总的来说,更加接近Oracle版本线程池的实现方式;
#超时SQL (statement timeout)
该特性port自 Twitter MySQL
允许设定一条SQL的最大执行时间,当到达设定的时间,如果SQL还没有执行完的话,就会自动中断SQL;
可以针对用户grant权限时设置超时时间,也可以通过变量设置;
该特性的好处时,可以避免大量烂SQL并发长时间运行造成实例负载飙升甚至不可用。
Port 自Mariadb。支持对FIO设备的原子写,需要硬件支持,据说该特性无需开启double write buffer,没有测试过
原生版本不支持创建列类型为TEXT/BLOB的memory表
貌似port自社区,线上不使用memory引擎,不关心,有兴趣的可以点开连接细看,文档有实现的细节


2.从Percona 5.5 port过来的特性

降低AHI锁btr search latch竞争;
#对trx_sys和创建read view的优化)
另外一篇博客有讲这部分内容:http://mysqllover.com/?p=834
#可以调整redo log块的大小,默认为512字节,但我们可以把redo 调整到4086字节以适应SSD的块大小
这也是我们在5.5时代的默认配置,表示使用O_DIRECT去打开数据文件和日志,但只对数据文件做fsync
#优化了函数BUF_READ_AHEAD_AREA,对应的bug#54814 ,用于表示预读的page数
MySQL5.6中每次都会去计算该值,实际上这个值基本上是不会变动的;将计算后的值保存下来,每次直接用即可
注:在MySQL5.7.4,这个问题被fix掉了。
语法:show slave status nolock;代价就是显示的位点信息可能不准确
避免应用失误,事务不提交,进而可能影响其他事务(例如存在行锁竞争的情况)
#为show engine innodb status扩展了大量Innodb内部信息
对问题诊断非常有用;另外也扩展了一些新的information schema表
.
.
.
3.新开发特性
#允许选择使用CRC32来做redo日志的checksum算法,在写入负载下,具有更好的性能
.
看起来主要用于Percona自家的另外一个工具Xtrabackup,做备份恢复用
.
#扩展了processlist表,支持显示毫秒级别的SQL执行时间,以及记录行信息
.
#支持为单独的SQL设置变量,扩展了新的语法
.
SET STATEMENT <variable=value> FOR <statement>;
例如:
root@sbtest 08:48:08>SET STATEMENT sort_buffer_size = 163840 FOR select * from sbtest1 order by pad limit 10;
设置的变量只对当前的SQL有效
在MySQL5.6中引入了单独的Page cleaner线程来刷脏页,但原生版本的Page cleaner线程可能存在效率问题,Percona对LRU的刷新做了优化;主要修改包括:
a.优化刷LRU流程buf_flush_LRU_tail,该函数由page cleaner线程调用
原生的逻辑是依次flush 每个buffer pool instance,每次扫描的深度通过参数innodb_lru_scan_depth来配置。而在每个instance内,又分成多个chunk来调用
.
修改后的逻辑为:每次flush一个buffer pool的LRU时,只刷一个chunk,然后再下一个instance,刷完所有instnace后,再回到前面再刷一个chunk。简而言之,把集中的flush操作进行了分散,其目的是分散压力,避免对某个instance的集中操作;
.
chunk的大小在Percona版本里是可配置的;
.
这里有一个特殊情况,如果某个bufer pool instance上的free list非常短时,会持续的flush该bp instance,直到高出free list的某个设定低水位值:

        ulint   free_list_lwm = srv_LRU_scan_depth / 100

                * srv_cleaner_free_list_lwm;
.
在分块flush的过程中,如果发现一次flush操作(buf_flush_LRU)没有刷出脏页或驱逐page(由参数innodb_cleaner_eviction_factor控制),则结束当前buffer pool instance的flush操作;
.
另外有一个细节看的不是很明白,如果当前驱逐的Page数比上次的要少,那么下一波刷该bp LRU时,scan的page数就不受innodb_lru_scan_depth限制(但扫描的LRU深度依然不会超过该值);不太理解这么设计的目的,文档也没找到任何描述
.
.
类似的在刷flush list时也采用了类似的分块flush策略(函数buf_flush_list)
.
.
.
.
Oracle版本的furious flush受限于innodb_io_capacity_max,每次loop sleep 1秒; 而Percona版本则突破了该限制,当free list上的block太少,或者在redo到达或接近同步checkpoint点时,允许跳过或减少sleep的时间
.
具体的计算sleep时间的算法,可以参考函数:page_cleaner_adapt_flush_sleep_time 和page_cleaner_adapt_lru_sleep_time
.
c.允许设定刷LRU/FLUSH LIST的超时时间,防止flush操作时间过长导致别的线程(例如尝试做single page flush的用户线程)stall住;
当到达超时时间时,page cleaner线程退出flush;
.
更具体的可以参阅这个bug report:http://bugs.mysql.com/bug.php?id=70453 
.
对af_get_pct_for_lsn的计算进行了调整;根据文档的描述,当进行了上述的page cleaner优化后,自适应flush操作可能会变的更加频繁(page_cleaner_flush_pages_if_needed)并保持checkpoint age在一个稳定的水位,从而因为减少了写合并而导致性能丢失;
.
注: MySQL5.7.4开始支持多个page cleaner线程,我们是否可以做这样的改动:让每个page cleaner线程,单独只负责一个buffer pool instance.
.
.
根据文档的描述,该特性主要解决如下场景:
—>buffer pool的free list的消耗速度超过Page cleaner刷page并释放block到free list上的速度;
—>当free list接近耗光时,可能有大量的用户线程阻塞在这里,检查是否有空闲block,然后sleep,做single page flush,重试…这增加了大量全局锁的竞争(buf_LRU_get_free_block);
—>即使当page cleaner线程成功释放了部分block,在将其返还到free list上时,还可能要和大量用户线程竞争锁 
.
Percona版本里,将刷新的操作主要留给了page cleaner线程,弱化用户线程对buffer pool的操作;同时增加page cleaner等后台线程的CPU调度优先级
.
Percona的修改包括:
.
a.调整当free list为空时的处理逻辑(参数控制,兼容原生)
当从磁盘读取一个page到buffer pool时,需要获得一个空闲的block,调用函数buf_LRU_get_free_block,主要修改集中在该函数中:
当free list为空时,如果使用新的逻辑,用户线程直接睡一会再重试,另外这块的计算逻辑也比较有意思,先pthread_yield几次,重试后还是拿不到空闲块,再调用os_thread_sleep,sleep的时间也有讲究。
.
b. 当需要推进bp的LSN(例如做同步checkpoint时)时,选择让page cleaner线程来做刷脏操作,用户线程直接sleep等待,这可以避免大量线程同时进行buffer pool刷脏引起的锁竞争; 另外在Page cleaner线程经过优化后,可以知道系统当前处于同步刷新状态,可以去做更激烈的刷脏(furious  flush),用户线程参与到其中,可能只会起到反作用。
通过参数控制,兼容原生逻辑;详细参见函数log_preflush_pool_modified_pages
.
c.允许设置后台线程的CPU调度优先级,包括page cleaner线程purge线程, IO线程Master线程
实际上就是设置线程的nice值,只在linux平台上生效;
.
d.让后台线程优先获得mutex,同样包括page cleaner线程purge线程IO线程Master线程
对Innodb底层的mutex库做了修改
.
在mutex_spin_wait里,需要进入等待队列时,获取一个slot(sync_array_get_and_reserve_cell),设置其类型为SYNC_PRIO_MUTEX(如果是rw lock,则是PRIO_RW_LOCK_EX/PRIO_RW_LOCK_SHARED)
.
而在释放mutex时,如果当前有高优先级等待的线程(mutex->high_priority_waiters),则优先唤醒对应的condition event(ib_prio_mutex_t->high_priority_event),否则唤醒低默认优先级的线程(ib_prio_mutex_t->base_mutex->event)
.
#优化了函数buf_flush_common,只有在Page_count大于0时,才去调用buf_dblwr_flush_buffered_writes,减少了dbl write buffer的锁竞争
参阅该Bug:http://bugs.mysql.com/bug.php?id=69170

参考:

Percona Server 5.6.15

 

原创文章,转载请注明: 转载自Simple Life

本文链接地址: [MySQL 5.6] Percona Server 5.6.15(及之前版本)的优化点

Post Footer automatically generated by wp-posturl plugin for wordpress.


Comments

Leave a Reply

Your email address will not be published. Name and email are required


Current month ye@r day *