本文共 9248 字,大约阅读时间需要 30 分钟。
最近上游发布了MySQL 5.6.26版本,从release note来看,MySQL5.6版本已经相当成熟,fix的bug数越来越少了。本文主要分析releae note上fix的相关bug,去除performance scheama、mac及windows平台、企业版、package相关内容。
问题描述:
在类unix平台上,当innodb_flush_method设置为O_DIRECT时,函数os_file_create_simple_no_error_handling_func没有使用O_DIRECT方式打开数据文件。例如在函数fil_node_open_file中,可能先以函数os_file_create_simple_no_error_handling_func打开文件,确定文件的大小,然后关闭文件;再以os_file_create打开数据文件,前者使用Buffered IO,后者使用DIRECT IO。这种混合使用可能引发性能问题。根据man手册建议:
Applications should avoid mixing O_DIRECT and normal I/O to the same file, and especially to overlapping byte regions in handles the coherency issues in this situation, overall I/O the same file. Even when the filesystem correctly throughput is likely to be slower than using either mode of files with direct I/O to the same files." alone. Likewise, applications should avoid mixing mmap(2)
(Bug #21113036, Bug #76627)
解决:
在函数os_file_create_simple_no_error_handling_func 中禁止OS Cache(函数os_file_set_nocache)补丁:
问题描述:
在将一个脏页从非压缩page拷贝到压缩页后,在写page到文件时(buf_flush_write_block_low),在设置压缩页的修改LSN之前先调用了函数page_zip_verify_checksum,由于此时压缩页上的LSN为0,而计算出来的checksum也可能为0,此时page_zip_verify_checksum认为要尝试写入一个空page,返回false,导致断言失败(Bug #21086723)解决:
先设置LSN,再调用page_zip_verify_checksum补丁:
问题描述:
当以如下序列执行时,实例会crash create databaseb
; use b; create table #mysql50#q.q
select 1; drop database b
; 在创建表时,发现非法的表名,表名被reset成一个空字符串,传递到引擎层就是”dbname/”, 而引擎层的数据词典定义中,是通过“dbname/tablename”这样的字符串来定位的,这就违反了数据词典的约定。 随后如果执行drop database, 会去遍历以db名作为前缀的数据词典项,触发crash。PS:即使重启实例,drop database,也无法执行清理操作,用户线程会不停的在drop db的逻辑里loop(Bug #19929435)
解决:
在引擎层拒绝创建空的表名补丁:
问题描述:
在函数innobase_get_foreign_key_info中,需要根据子表中存储的父表表名去打开父表,但子表上是根据系统字符集system_charset_info存储的,而innodb是使用my_charset_filename存储表名和库名,因此如果包含父表包含特殊字符,就会造成无法打开父表,导致报错。(Bug #21094069)解决:
将系统字符集的表名和库名转换成my_charset_filename格式(tablename_to_filename)补丁:
问题描述:
解决:
在进行fil_io时,如果表空间正在被删除(space->stop_new_ops被设置为true),不允许异步读操作,但允许写操作和同步读操作。补丁:
问题描述:
当表上的索引存在前缀索引时(prefix index),对表进行export,再import tablespace可能会失败,并报Schema mismatch错误,错误码为ER_TABLE_SCHEMA_MISMATCH。test case见bug#76877 (Bug #20977779, Bug #76877) 原因是cfg文件和表的索引定义相匹配时逻辑错误,例如如下表:在索引对象中定义了4个列:(c1, prefix_len=16), (DB_TRX_ID), (DB_ROLL_PTR),(c1, prefix_len=0)。
cfg和表索引对象相比较时,其实两者是一样的,但cfg在取列时,如果存在相同列名的,总是取第一个,如上例,在比较第四个列的schema是否一致时,取的实际上是第一个,从而产生报错。参考函数:row_import::match_index_columns ((Bug #20977779, Bug #76877))
解决:
一个列一个列的依次校验。补丁:
问题描述:
考虑如下场景:can_delete = !row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, &mtr) || !row_vers_old_has_index_entry(TRUE, btr_pcur_get_rec(&node->pcur), &mtr, index, entry, node->roll_ptr, node->trx_id);
row_purge_reposition_pcur定位到聚集索引上,node->found_clust设置为true,定位到clust index上的cursor存储在node->pour上。
但是步骤c中,即时二级索引没有被标记删除,在函数row_purge_poss_sec也返回了true,这是因为重新定位cursor的逻辑错误。
函数row_purge_reposition_pcur
: if (node->found_clust) { ibool found; found = btr_pcur_restore_position(mode, &node->pcur, mtr); return(found); } else { node->found_clust = row_search_on_row_ref( &node->pcur, mode, node->table, node->ref, mtr); if (node->found_clust) { btr_pcur_store_position(&node->pcur, mtr); } } return(node->found_clust);
考虑如下序列:
解决:
在函数row_purge_reposition_pcur中,若是restore cursor失败,需要重置node->found_clust为false (Bug #19138298, Bug #70214, Bug #21126772, Bug #21065746)补丁:
问题描述:
尝试为表上rebuild 全文索引,但表上已经有损坏的索引时,会触发assert。(Bug #20637494)解决:
抛出错误,提示用户先删掉损坏的索引。返回错误码为ER_INNODB_INDEX_CORRUPT补丁:
问题描述:
构建full-text的表上存在隐藏的FTS_DOC_ID和唯一索引FTS_DOC_ID_INDEX(FTS_DOC_ID),当删除全文索引时,对应的隐藏列并没有删除,但在当前的逻辑中,如果存在FTS_DOC_ID,则不允许ONLINE DDL.(Bug #20590013, Bug #76012)解决:
当表上只有FTS_DOC_ID_INDEX和FTS_DOC_ID 但没有定义全文索引时,允许ONLINE DDL。这些隐藏列直到全表rebuild时才被删除。补丁:
问题描述:
ib_cursor_moveto 函数没有判断构建的tuple的列个数是否小于索引列个数,而是直接用索引列的个数来做遍历,可能导致段错误(Bug #21121197, Bug #77083)解决:
加上对应的判断补丁:
问题描述:
ib_table_truncate函数中,当truncate失败时,没有正确的释放事务对象,可能导致shutdown hang住解决:
总是释放事务对象补丁:
问题描述:
ib_open_table_by_id函数中,已经加了dict_sys->mutex锁,但该函数中调用dict_table_open_on_id传递的第二个参数为FALSE,认为没有持有mutex,属于基本的逻辑错误。(Bug #21121084, Bug #77100)解决:
调整传参补丁:
上面几个bug看起来都是非常“低级”的代码缺陷,这也侧面证明了InnoDB API接口在推出后社区用的人实在太少了,这三个Bug都是facebook的工程师提出的,很好奇他们会利用InnoDB API做些什么
问题描述:
InnoDB memcached plugin在处理unsigned NOT NULL类型时没有正确处理,导致返回的数据错误。但是代码里很多地方都使用类似m_col->attr == IB_COL_UNSIGNED,导致大量的逻辑错误。(Bug #20535517, Bug #75864)
解决:
修改成m_col->attr & IB_COL_UNSIGNED补丁:问题描述:
当使用多线程复制时,执行STOP SLAVE需要等待所有的worker线程完成其各自的工作队列中的事务。如果Pending的事务很多,可能要等待很长时间才能完成STOP SLAVE,另外在STOP SLAVE的过程中,是无法SHOW SLAVE STATUS的,一种比较常见的场景就是大量的监控程序SQL堵塞堆积(Bug #75525, Bug #20369401)解决:
解决方案是先找到任意worker线程中最新的commit的事务,确定一个上限位点,所有的worker线程执行到这个位置停止,剩下的事务暂时不执行。具体的:
但是上述方案并不能解决正在执行的大事务过慢的问题。
补丁:
问题描述:
MySQL使用InnoDB + binlog做XA的方式来进行crash recovery,但在之前的版本中如果写Binlog到磁盘发生了错误,group commit的逻辑并没有感知到这个错误,而是继续在引擎层提交事务,备库没有接收到对应的Binlog,导致主备数据不一致。 (Bug #76795, Bug #20938915)
解决:
从MySQL 5.6.22版本开始,引入了一个新参数binlog_error_action(5.6.20及21版本叫做binlogging_impossible_mode),若设置为ABORT_SERVER,则在发生binlog写入错误时直接让实例退出,避免引发更大的错误;若设置为IGNORE_ERROR,则忽略本次写入失败,同时禁止Binlog记录,需要重启才能让binlog再次开启。
为了主备数据的强一致性,通常应该将binlog_error_action设置为ABORT_SERVER,这样在打开文件、rotate新文件、从IO Cache写binlog到文件出现磁盘错误时,都会退出实例。补丁:
问题描述:
relay_log_recovery参数打开时,备库在重启时就可以根据SQL线程执行到的位置重新拉binlog,这可以有效处理备库发生机器宕机导致relay log文件损坏的情况,无需人工去change master,在之前版本中,如果使用了多线程复制,是无法开启该特性的,在启动实例时会报如下错误:
实际上,如果开启了GTID,就无需关心各个worker线程之间的Gap,通过备库的GTID集合充拉relay log即可。(Bug #73397, Bug #19316063)
解决:
在重启recovery时检查是否开启了GTID补丁:
问题描述:
当两台备库错误的配置了相同的server_uuid,并指向同一个主库时,备库的IO线程会被频繁的断开并尝试重连。而在备库来看,并没有足够的信息提示产生重连的原因。
解决:
这种场景下,主库会生产一个错误信息传递到备库,当备库接受到这样的错误信息时不再尝试重连。(Bug #72581, Bug #18731252)补丁:
问题描述:
alter table rebuild partition 处理逻辑错误导致的crash, bug被隐藏,无test case。对应release note:解决:
先搁置,后面在看。补丁:
对优化器模块了解不深,先Mark,后面有时间再细看。
问题描述:
While calculating the cost for doing semjoin_dupsweedout strategy inner_fnout is calculated wrongly when max_outer_fanout becomes 0. This causes mysql server to exit later (Bug #21184091)解决:
Calculate the inner_fanout only when max_outer_fanout is > 0. Else there is no need to recalculate inner_fanout w.r.t max_outer_fanout.补丁:
问题描述:
GROUP BY or ORDER BY on a CHAR(0) NOT NULL column could lead to a server exit. (Bug #19660891) ASSERTION `PARAM.SORT_LENGTH != 0′ FAILED IN SQL/FILESORT.CC:361解决:
Mark,后面分析补丁:
问题描述:
When choosing join order, the optimizer could incorrectly calculate the cost of a table scan and choose a table scan over a more efficient eq_ref join. (Bug #71584, Bug #18194196) While choosing the join order, the cost for doing table_scan is wrongly calculated. As a result table_scan is preferred over eq_ref, thereby choosing a bad plan.解决:
Mark,后面分析补丁:
问题描述:
MySQL String库下的字符串处理问题,在cs_values函数中,对字符串长度的处理存在缺陷,可能导致内存损坏。 (Bug #20359808)解决: 调整长度判断补丁:
问题描述:
当会话断开或者执行类似change user时,session status会merge到全局status中(add_to_status(&global_status_var, &status_var)),但没有立刻对thd的status_var做reset,这时候另外一个session去查询global status时,会重复把这些session的status值加到全局。解决:
在THD::change_user、THD::release_resources函数中累加到全局status后,重置session的status。补丁:
转载地址:http://abpsl.baihongyu.com/