MySQL Bug

MySQL5.6.27 Release Note解读(innodb及复制模块)

新功能 问题描述(Bug #18871046, Bug #72811): 主要为了解决一个比较“古老”的MySQL在NUMA架构下的“swap insanity”问题,其表现为尽管为InnoDB buffer pool分配了足够多的内存,但依然会产生swap。而swap对数据库系统性能而言是比较致命的。 当我们配置的buffer pool超过单个node的内存时,例如总共64GB内存,每个节点32GB,分配buffer pool为40GB,默认情况下,会先用满node 0,再在node1上分配8GB内存。如果绑定到node 0上的线程需要申请新的内存时,不是从node1上申请(还有24GB空余),而是swap node0的内存,再做申请。 这个问题在社区讨论了很久,大神Jeremy Cole 对该问题有写博客做过详细的介绍(http://blog.jcole.us/2010/09/28/mysql-swap-insanity-and-the-numa-architecture/) 解决: 增加了一个只读的新参数innodb_numa_interleave选项,当开启该选项,可以允许在NUMA架构下采取交叉分配内存的方式。 在启动MySQL时,采用MPOL_INTERLEAVE方式分配内存,如上例,在启动时每个node上分配20GB内存,保证每个node依然有空闲内存可用;在完成InnoDB buffer pool内存分配后,再将内存分配策略设置为MPOL_DEFAULT,之后的线程申请内存,都在各自的节点完成。 补丁: https://github.com/mysql/mysql-server/commit/242fa2c92de304637e794e531df1f1b86b8d1dee https://github.com/mysql/mysql-server/commit/d2578b57ba7d90a00281ae124a1cd6c83193f62a InnoDB bugfix 问题描述: 当一个表被驱逐并重新导入时,如果表的内容是空的(例如先插入大量数据,再一次全删除时,auto_increment不为0),就会导致AUTO_INCREMENT的值被重置。(Bug #21454472, Bug #77743) 这是因为每次打开表时,总是通过计算表上记录最大自增列值的方式来重新设置,当表被打开时,如果没有数据,AUTO_INCREMENT就会被重置成最小值。 解决: 在驱逐表时,将这个表的AUTO_INCREMENT值存储在内存中(dict_sys->autoinc_map),当表重新读入时,再恢复其AUTO_INCREMENT值。 这个Fix并不算一个完整的修复,当实例重启时,AUTO_INCREMENT依然会被重置(参阅bug#199), RDS MYSQ已经Fix了这个bug,能够持久化自增列到物理文件中,在重启后不会丢失。 补丁: https://github.com/mysql/mysql-server/commit/d404923aad4693dc152d02461f858d7ef218c336 问题描述: Memcached的一段开启事务的代码在assert中调用,而根据assert的文档定义( http://man7.org/linux/man-pages/man3/assert.3.html ),他的行为是未知的,受NDEBUG控制,可能assert会被定义成空函数,导致assert中的函数调用被忽略掉。这个bug在某些平台下可能极易触发。使用memcached的同学需要注意下。 (Bug #21239299, Bug #75199) 解决: 将函数调用从assert中移出来,只assert函数返回值。 补丁: https://github.com/mysql/mysql-server/commit/db5dc6fd3abe855685a554bc3c555b1b63914b60 问题描述: 在ARM64平台上, GCC的内建的TAS操作函数__sync_lock_test_and_set 可能不准确,这和平台内存模型有关,锁的行为错误,导致进一步的数据损坏。(Bug […]


[MySQL bug] unique key corruption again…..

最近Percona的研发人员report了一个uk corruption的bug,这个Bug不同于之前发现的bug(见我的另外一篇博客http://mysqllover.com/?p=1041),而是影响从5.1 到5.8全系列MySQL版本,应该算是设计上的缺陷吧。 原创文章,转载请注明: 转载自Simple Life 本文链接地址: [MySQL bug] unique key corruption again….. Post Footer automatically generated by wp-posturl plugin for wordpress.


InnoDB: Failing assertion: page_get_n_recs(page) > 1

我们知道,在MySQL5.5里,insert buffer换了个说法,叫change buffer,能够缓存对二级索引的操作,直到将实际的page读入bp时才进行合并,这在IO-bound并且表上有很多二级索引时,可以有效提升性能。 但存在一个蛋疼的bug,在5.5.31版本才被彻底fix掉.如果你不幸碰到如下断言错误crash,那么恭喜你中招了:                 InnoDB: Failing assertion: page_get_n_recs(page) > 1 这个问题最初可以追溯到2012年1月份的中旬,春节前三四天,当时一个线上库不幸触发该bug,导致crash,并且无法重启。 当时的处理方式是用innodb froce recovery起来,同时关掉innodb purge thread(另外一个bug,设置一个较大的innodb force recovery无法启动mysqld),然后dump数据,重建库.. 比较早的关于该bug的讨论见:http://bugs.mysql.com/bug.php?id=61104, 但bug#61104并没用完全修复该bug,只是将断言移除了而已,这样用户可以把实例起来,执行一次DDL来重建表的二级索引 后来Percona的Alexey Kopytov 在buglist上提出了该bug的导致的根本原因(http://bugs.mysql.com/bug.php?id=66819)在于删除ibuf记录和应用Ibuf记录并不是原子的,也就是不在一个mtr中,那么在不恰当的时间点挂掉,就可能导致无法crash recovery,实际上,即使我们将innodb_change_buffer设置inserts也不是安全的。。。。 再后来,这个bug被fix掉了,但我看了diff后,发现fix的不完整,DELETE的场景依然存在问题。于是俺在Facebook上吐槽了下,Percona的Valeriy Kravchuk很热心的帮忙确认了.. 主要涉及两个函数 >ibuf_merge_or_delete_for_page,是对一个block上记录进行change buffer合并的主要函数; 对于Purge操作,即IBUF_OP_DELETE类型: 先执行ibuf_delete后,会直接进行ibuf_btr_pcur_commit_specify_mtr,提交redo,然后再去删除ibuf记录(ibuf_delete_rec) >ibuf_delete_rec,每完成一次change buffer记录的合并,都会调用该函数去从Ibuf tree中将其删除 在ibuf_delete_rec函数中,当进行btr_cur_optimistic_delete失败后,会先去commit mitr(调用ibuf_btr_pcur_commit_specify_mtr),再去开启新的mtr做btr_cur_pessimistic_delete 我们知道,Innodb是通过mtr写入的redo日志来做crash recovery的,如果我们在merge数据成功和删除ibuf记录之间crash掉,那么就可能数据记录被更新了,但ibuf记录还没被删除,从而触发前面提到的断言失败(在做ibuf_delete时,想删除记录,却发现page上的记录已经删光了…) 也就是说,实际上对于所有的change buffer操作类型都可能存在问题,只是对于purge操作,概率更高点,因为它有两个风险点 官方第一次fix,在MySQL5.5.29里,修复了ibuf_delete_rec中存在的问题,方式是先标记删除ibuf entry,再做悲观删除 http://bazaar.launchpad.net/~mysql/mysql-server/5.5/revision/3979 官方第二次fix,在MySQL5.5.31里,修复ibuf_merge_or_delete_for_page中存在的问题 http://bazaar.launchpad.net/~mysql/mysql-server/5.5/revision/4177 原创文章,转载请注明: 转载自Simple Life 本文链接地址: […]


InnoDB: Failing assertion: trx->isolation_level == TRX_ISO_READ_UNCOMMITTED

最近再次碰到之前遇到的断言失败的bug,错误信息如下: InnoDB: Failing assertion: trx->isolation_level == TRX_ISO_READ_UNCOMMITTED   这是一个已知的bug(bug#62037),在MySQL5.5.22版本中被fix掉,在lauchpad上可以看到具体是如何修复的   如何重现 重现case,使用gdb的non-stop模式很容易重现: 启动gdb,使用non-stop set target-async 1 set pagination off set non-stop on 断点: row0upd.c:2033   (Percona Server5.5.18) case: session 1:  CREATE TABLE `t1` (   `a` int(11) DEFAULT NULL,   `b` text,   `c` text ) ENGINE=InnoDB DEFAULT CHARSET=gbk; insert into t1 values (1,repeat(‘b’, 7000), repeat(‘c’, 100)); update t1 set c = concat(c, repeat(‘c’, 2000)); 这时候会因为更新列c而导致b列的数据被外部存储(b的列长最大),停在断点 session 2:执行查询 select * from t1 where a = 1; 该bug主要包含两个问题: 问题一:运行时断言失败 主要原因是: 1.更新记录时,undo中只记录了那些被更新的列,而由于其他列更新,导致某个列需要外部存储时,这个列的值不会写入undo。 也就是说,在从函数btr_cur_pessimistic_update中返回后,记录上某个未被更新的列,可能存储的是无效的指针 例如上述场景b列被选出来外部存储,因为它的长度最大,因此b列的数据被修改成一个尚未生效的值全为0的指针; 在悲观更新返回后,由于先mtr commit,再更新外部存储数据,这导致block及索引上的排他锁都被释放掉;这时候其他连接的查询是可以看到这条记录的。 2.虽然MVCC保证查询可以看到修改过的列,但1提到的未修改但被选作外部存储的列没有做undo,因此查询看到的是指针,导致触发断言失败,实例crash 解决: 官方的解决办法是在悲观更新和更新完外部存储列后,才进行mtr commit,这可以保证中间不会有其他查询看到未完成的更新(被阻塞住) 问题二:断言失败crash后,无法crash recovery 原因: 如果在完成悲观更新和写入外部存储列的过程中crash,记录更新可能完成了,但外部存储列是失败的,在crash recovery后,记录依然维持在不一致的状态。这样只要一访问该记录,就会报和问题一一样的断言crash错误 解决: 将上述逻辑修改为: 1.对原记录进行修改,mtr不提交(用btr_mtr代表该mtr) 2.扩展新的外部存储页(不能使用btr_mtr之前释放掉的page,暂时没搞明白为什么…),对新的外部存储页的写入(包括初始化)使用另外一个mtr(称为blob_mtr),对记录上指针的更新使用btr_mtr 3.commit blob_mtr 4.commit btr_mtr 这样在crash recovery的时候,就会先恢复blob页,再恢复记录上的操作;最差的情况就是丢失更新,ibd中存在一个有部分数据的无效blob页,但记录本身仍然能保持一致的状态   另外在插入记录时也可能触发该bug,例如,当一条记录被标记删除,但未被purge掉时,如果再次插入该聚集索引记录,被标记删除的记录就会被更新成现在的记录,这时候触发bug的逻辑就和上述Update的逻辑类似了。 […]


bug#66718 Assert from DROP TABLE concurrent with ibuf merges

http://bugs.mysql.com/bug.php?id=66718 Assert from DROP TABLE concurrent with ibuf merges A.原因: master线程发起的ibuf merge和用户线程的drop table操作可能存在竞争。 1.master线程请求文件page IO,读入内存之前,会调用buf_page_init_for_read->fil_tablespace_deleted_or_being_deleted_in_mem 如果这时候该表尚未标记为删除,那么则认为可以继续读文件Page。 2.用户线程调用fil_delete_tablespace >设置space->stop_ibuf_merges = TRUE >确认space->n_pending_ibuf_merges == 0   设置space->is_being_deleted = TRUE; >确认space->chain->n_pending为0  并且 space->n_pending_flushes = 0 继续下面的逻辑 3.master线程继续调用函数fil_io -> fil_node_prepare_for_io会递增node->n_pending++ 4.用户线程继续往下走,调用 fil_delete_tablespace->fil_space_free->fil_node_free 在这里会做断言: ut_a(node->n_pending == 0); 不过在Percona版本中,断言处就不太一样,而是: ut_a(node->n_pending == 0 || space->is_being_deleted); 因为在free node之前,我们已经设置了space->is_being_deleted为TRUE,因此不会触发断言错误。 B.总结: 根据bug描述及分析,这个bug不影响我们的线上MySQL版本。 C.其他: 我们已经确保space->n_pending_ibuf_merges为0了,为什么还会有对该表的ibuf Merge呢? […]


[MySQL Bug]Bug#13986705 CRASH IN GET_INTERVAL_VALUE() WITH DATE CALCULATION WITH UTF32 INTERVALS

upstream link: http://bazaar.launchpad.net/~mysql/mysql-server/5.5/revision/3840 test case: select space(date_add(101, INTERVAL (CAST(CHAR(‘1’ USING utf16) AS CHAR(1)))hour_second)) 只影响5.5 根据crash的backtrace Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7ffff4643700 (LWP 5874)] 0x0000000000706602 in get_interval_value (args=0x7fff84004e98, int_type=INTERVAL_HOUR_SECOND, str_value=<value optimized out>, interval=0x7ffff4640c40)     at /u01/project/PS5518/trunk/Percona-Server-5.5.18/sql/item_timefunc.cc:1460 1460        while (str != end && my_isspace(cs,*str)) 在my_isspace(cs,*str)这里crash,my_isspace是一个宏: #define my_isspace(s, c)  (((s)->ctype+1)[(uchar) (c)] […]


[MySQL Bug]bug#66301(Percona Bug#1035225)简析

转载请署名:印风 ——————————— 先跑test case 1.创建测试表 CREATE TABLE t(        id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,        k INT,        c CHAR(1),        UNIQUE KEY(k)) ENGINE=InnoDB; 2.当顺序执行如下SQL时 SQL1–INSERT INTO t(k) VALUES (1), (2), (3) ON DUPLICATE KEY UPDATE c=’1′; SQL2–INSERT INTO t(k) VALUES (2), (4), (5) ON […]


[MySQL Bug]bug#61209简析

转载请署名:印风 ————————————————— MySQL bug#61209,changelog如下,在5.5.23被fix InnoDB: Running concurrent bulk inserts on a server with auto_increment_offset=1,auto_increment_increment greater than 1, and innodb_autoinc_lock_mode=1 could result in intermittent errors like the following, even with the primary key set to auto_increment and omitted from the INSERT statement:   Duplicate entry ‘value’ for key ‘PRIMARY’ The workaround was to set auto_increment_offset=1 or innodb_autoinc_lock_mode=0 (“traditional”). (Bug #13817703, Bug #61209)   之前已经介绍过了innodb 如何处理auto inc的代码流程,现在来看看在innobase_next_autoinc里是如何来计算的。 […]


[MySQL Bug] bug#61579碎碎念

转载请署名:印风 ———————————————- MySQL bug#61579 changelog: InnoDB: For an InnoDB table with a trigger, under the setting innodb_autoinc_lock_mode=1, sometimes auto-increment values could be interleaved when inserting into the table from two sessions concurrently. The sequence of auto-increment values could vary depending on timing, leading to data inconsistency in systems using replication. (Bug #12752572, Bug #61579) 根据changelog的描述,当某个表上存在触发器时,并且innodb_autoinc_lock_mode=1,当两个session并发插入表时,自增列的值可能会产生交错。当使用statement记录时,会导致duplicate key错误,导致复制停止。 把触发器移除掉,则一切正常。 简单的gdb一把可以看出来,对于一条简单的插入语句,在函数handler::update_auto_increment中,当有trigger时,预估值 estimation_rows_to_insert值为0, […]


[MySQL Bug] bug#65111碎碎念

changelog里的描述: InnoDB: In a transaction using the REPEATABLE READ isolation level, an UPDATE or DELETE statement for anInnoDB table could sometimes overlook rows recently committed by other transactions. As explained inSection 14.3.9.2, “Consistent Nonlocking Reads”, DML statements within a REPEATABLE READ transaction apply to rows committed by other transactions, even if a query could not see those rows. (Bug #14007649, Bug #65111) http://bugs.mysql.com/bug.php?id=65111 从bug#65111的描述来看,根据文档http://dev.mysql.com/doc/refman/5.5/en/innodb-consistent-read.html中的描述: 有两个以以START […]