mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
c68007d958
A new configuration parameter innodb_deadlock_report is introduced: * innodb_deadlock_report=off: Do not report any details of deadlocks. * innodb_deadlock_report=basic: Report transactions and waiting locks. * innodb_deadlock_report=full (default): Report also the blocking locks. The improved deadlock checker will consider all involved transactions in one loop, even if the deadlock loop includes several transactions. The theoretical maximum number of transactions that can be involved in a deadlock is `innodb_page_size` * 8, limited by the persistent data structures. Note: Similar to mysql/mysql-server@3859219875 our deadlock checker will consider at most one blocking transaction for each waiting transaction. The new field trx->lock.wait_trx be nullptr if and only if trx->lock.wait_lock is nullptr. Note that trx->lock.wait_lock->trx == trx (the waiting transaction), while trx->lock.wait_trx points to one of the transactions whose lock is conflicting with trx->lock.wait_lock. Considering only one blocking transaction will greatly simplify our deadlock checker, but it may also make the deadlock checker blind to some deadlocks where the deadlock cycle is 'hidden' by the fact that the registered trx->lock.wait_trx is not actually waiting for any InnoDB lock, but something else. So, instead of deadlocks, sometimes lock wait timeout may be reported. To improve on this, whenever trx->lock.wait_trx is changed, we will register further 'candidate' transactions in Deadlock::to_check(), and check for 'revealed' deadlocks as soon as possible, in lock_release() and innobase_kill_query(). The old DeadlockChecker was holding lock_sys.latch, even though using lock_sys.wait_mutex should be less contended (and thus preferred) in the likely case that no deadlock is present. lock_wait(): Defer the deadlock check to this function, instead of executing it in lock_rec_enqueue_waiting(), lock_table_enqueue_waiting(). DeadlockChecker: Complete rewrite: (1) Explicitly keep track of transactions that are being waited for, in trx->lock.wait_trx, protected by lock_sys.wait_mutex. Previously, we were painstakingly traversing the lock heaps while blocking concurrent registration or removal of any locks (even uncontended ones). (2) Use Brent's cycle-detection algorithm for deadlock detection, traversing each trx->lock.wait_trx edge at most 2 times. (3) If a deadlock is detected, release lock_sys.wait_mutex, acquire LockMutexGuard, re-acquire lock_sys.wait_mutex and re-invoke find_cycle() to find out whether the deadlock is still present. (4) Display information on all transactions that are involved in the deadlock, and choose a victim to be rolled back. lock_sys.deadlocks: Replaces lock_deadlock_found. Protected by wait_mutex. Deadlock::find_cycle(): Quickly find a cycle of trx->lock.wait_trx... using Brent's cycle detection algorithm. Deadlock::report(): Report a deadlock cycle that was found by Deadlock::find_cycle(), and choose a victim with the least weight. Altogether, we may traverse each trx->lock.wait_trx edge up to 5 times (2*find_cycle()+1 time for reporting and choosing the victim). Deadlock::check_and_resolve(): Find and resolve a deadlock. lock_wait_rpl_report(): Report the waits-for information to replication. This used to be executed as part of DeadlockChecker. Replication must know the waits-for relations even if no deadlocks are present in InnoDB. Reviewed by: Vladislav Vaintroub
165 lines
5.3 KiB
Text
165 lines
5.3 KiB
Text
--source include/have_partition.inc
|
|
--source include/have_innodb.inc
|
|
|
|
let $MYSQLD_DATADIR= `SELECT @@datadir`;
|
|
|
|
call mtr.add_suppression("InnoDB: Table .* does not exist in the InnoDB internal data dictionary .*");
|
|
call mtr.add_suppression("Deadlock found when trying to get lock; try restarting transaction");
|
|
|
|
--echo #
|
|
--echo # Bug#11766879/Bug#60106: DIFF BETWEEN # OF INDEXES IN MYSQL VS INNODB,
|
|
--echo # PARTITONING, ON INDEX CREATE
|
|
--echo # Bug#12696518: MEMORY LEAKS IN HA_PARTITION (VALGRIND TESTS ON TRUNK)
|
|
--echo #
|
|
CREATE TABLE t1 (
|
|
id bigint NOT NULL AUTO_INCREMENT,
|
|
time date,
|
|
id2 bigint not null,
|
|
PRIMARY KEY (id,time)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
|
/*!50100 PARTITION BY RANGE(TO_DAYS(time))
|
|
(PARTITION p10 VALUES LESS THAN (734708) ENGINE = InnoDB,
|
|
PARTITION p20 VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */;
|
|
|
|
INSERT INTO t1 (time,id2) VALUES ('2011-07-24',1);
|
|
INSERT INTO t1 (time,id2) VALUES ('2011-07-25',1);
|
|
INSERT INTO t1 (time,id2) VALUES ('2011-07-25',1);
|
|
|
|
--error ER_DUP_ENTRY
|
|
CREATE UNIQUE INDEX uk_time_id2 on t1(time,id2);
|
|
|
|
SELECT COUNT(*) FROM t1;
|
|
SHOW CREATE TABLE t1;
|
|
|
|
DROP TABLE t1;
|
|
|
|
call mtr.add_suppression("InnoDB: Error: table `test`.`t1` .* InnoDB internal");
|
|
--echo #
|
|
--echo # Bug#55091: Server crashes on ADD PARTITION after a failed attempt
|
|
--echo #
|
|
SET @old_innodb_file_per_table = @@global.innodb_file_per_table;
|
|
SET @old_innodb_strict_mode = @@global.innodb_strict_mode;
|
|
SET @@global.innodb_file_per_table = ON,
|
|
@@global.innodb_strict_mode = ON;
|
|
--disable_query_log
|
|
SET @save_innodb_read_only_compressed=@@GLOBAL.innodb_read_only_compressed;
|
|
SET GLOBAL innodb_read_only_compressed=OFF;
|
|
--enable_query_log
|
|
|
|
--connect(con1,localhost,root,,)
|
|
CREATE TABLE t1 (id INT NOT NULL
|
|
PRIMARY KEY,
|
|
user_num CHAR(10)
|
|
) ENGINE = InnoDB
|
|
KEY_BLOCK_SIZE=4
|
|
PARTITION BY HASH(id) PARTITIONS 1;
|
|
|
|
--replace_result #p# #P#
|
|
--list_files $MYSQLD_DATADIR/test t1*
|
|
SHOW CREATE TABLE t1;
|
|
|
|
SET GLOBAL innodb_file_per_table = OFF;
|
|
|
|
--disconnect con1
|
|
--connect(con2,localhost,root,,)
|
|
|
|
LOCK TABLE t1 WRITE;
|
|
|
|
--echo # ALTER fails because COMPRESSED/KEY_BLOCK_SIZE
|
|
--echo # are incompatible with innodb_file_per_table = OFF;
|
|
|
|
--replace_regex / - .*//
|
|
--error ER_GET_ERRNO
|
|
ALTER TABLE t1 ADD PARTITION PARTITIONS 1;
|
|
|
|
--replace_result #p# #P#
|
|
--list_files $MYSQLD_DATADIR/test t1*
|
|
--echo # This SET is not needed to reproduce the bug,
|
|
--echo # it is here just to make the test case more realistic
|
|
SET innodb_strict_mode = OFF;
|
|
|
|
ALTER TABLE t1 ADD PARTITION PARTITIONS 2;
|
|
--replace_result #p# #P#
|
|
--list_files $MYSQLD_DATADIR/test t1*
|
|
|
|
# really bug#56172
|
|
ALTER TABLE t1 REBUILD PARTITION p0;
|
|
|
|
UNLOCK TABLES;
|
|
SHOW CREATE TABLE t1;
|
|
DROP TABLE t1;
|
|
|
|
--disconnect con2
|
|
--connection default
|
|
SET @@global.innodb_strict_mode = @old_innodb_strict_mode;
|
|
SET @@global.innodb_file_per_table = @old_innodb_file_per_table;
|
|
--disable_query_log
|
|
SET GLOBAL innodb_read_only_compressed=@save_innodb_read_only_compressed;
|
|
--enable_query_log
|
|
|
|
#
|
|
# Bug#32430 - show engine innodb status causes errors
|
|
#
|
|
SET @save_detect= @@GLOBAL.innodb_deadlock_detect;
|
|
SET @save_report= @@GLOBAL.innodb_deadlock_report;
|
|
SET GLOBAL innodb_deadlock_detect=ON;
|
|
SET GLOBAL innodb_deadlock_report=BASIC;
|
|
SET NAMES utf8;
|
|
CREATE TABLE `t``\""e` (a INT, PRIMARY KEY (a))
|
|
ENGINE=InnoDB
|
|
PARTITION BY RANGE (a)
|
|
SUBPARTITION BY HASH (a)
|
|
(PARTITION `p0``\""e` VALUES LESS THAN (100)
|
|
(SUBPARTITION `sp0``\""e`,
|
|
SUBPARTITION `sp1``\""e`),
|
|
PARTITION `p1``\""e` VALUES LESS THAN (MAXVALUE)
|
|
(SUBPARTITION `sp2``\""e`,
|
|
SUBPARTITION `sp3``\""e`));
|
|
INSERT INTO `t``\""e` VALUES (0), (2), (6), (10), (14), (18), (22);
|
|
START TRANSACTION;
|
|
connect(con1,localhost,root,,);
|
|
SET NAMES utf8;
|
|
START TRANSACTION;
|
|
connection default;
|
|
UPDATE `t``\""e` SET a = 16 WHERE a = 0;
|
|
connection con1;
|
|
UPDATE `t``\""e` SET a = 8 WHERE a = 22;
|
|
let $id_1= `SELECT CONNECTION_ID()`;
|
|
SEND;
|
|
UPDATE `t``\""e` SET a = 12 WHERE a = 0;
|
|
connection default;
|
|
let $wait_condition= SELECT COUNT(*)=2 FROM INFORMATION_SCHEMA.INNODB_LOCKS;
|
|
--source include/wait_condition.inc
|
|
--sorted_result
|
|
SELECT lock_table, COUNT(*) FROM INFORMATION_SCHEMA.INNODB_LOCKS
|
|
GROUP BY lock_table;
|
|
set @old_sql_mode = @@sql_mode;
|
|
set sql_mode = 'ANSI_QUOTES';
|
|
--sorted_result
|
|
SELECT lock_table, COUNT(*) FROM INFORMATION_SCHEMA.INNODB_LOCKS
|
|
GROUP BY lock_table;
|
|
set @@sql_mode = @old_sql_mode;
|
|
--error ER_LOCK_DEADLOCK
|
|
UPDATE `t``\""e` SET a = 4 WHERE a = 22;
|
|
--echo # First table reported in 'SHOW ENGINE InnoDB STATUS'
|
|
# RECORD LOCKS space id 0 page no 50 n bits 80 index `PRIMARY` in \
|
|
# Database `test`, Table `t1`, Partition `p0`, Subpartition `sp0` \
|
|
# trx id 0 775
|
|
# NOTE: replace_regex is very slow on match copy/past '(.*)' regex's
|
|
# on big texts, removing a lot of text before + after makes it much faster.
|
|
#/.*in (.*) trx.*/\1/
|
|
--replace_regex /.*RECORD LOCKS space id [0-9]* page no [0-9]* n bits [0-9]* // / trx id .*// /.*index .* in // /trx table locks [0-9]* // /total table locks [0-9]* //
|
|
SHOW ENGINE InnoDB STATUS;
|
|
set @old_sql_mode = @@sql_mode;
|
|
set sql_mode = 'ANSI_QUOTES';
|
|
--replace_regex /.*RECORD LOCKS space id [0-9]* page no [0-9]* n bits [0-9]* // / trx id .*// /.*index .* in // /trx table locks [0-9]* // /total table locks [0-9]* //
|
|
SHOW ENGINE InnoDB STATUS;
|
|
SET GLOBAL innodb_deadlock_detect= @save_detect;
|
|
SET GLOBAL innodb_deadlock_report= @save_report;
|
|
set @@sql_mode = @old_sql_mode;
|
|
connection con1;
|
|
REAP;
|
|
ROLLBACK;
|
|
disconnect con1;
|
|
connection default;
|
|
DROP TABLE `t``\""e`;
|