Bug #82592 | MySQL InnoDB update locking | ||
---|---|---|---|
Submitted: | 16 Aug 2016 3:55 | Modified: | 23 Aug 2016 14:27 |
Reporter: | Ivan Ma | Email Updates: | |
Status: | Not a Bug | Impact on me: | |
Category: | MySQL Server: InnoDB storage engine | Severity: | S3 (Non-critical) |
Version: | 5.7.14 | OS: | Any |
Assigned to: | CPU Architecture: | Any |
[16 Aug 2016 3:55]
Ivan Ma
[16 Aug 2016 4:29]
Ivan Ma
The lock timeout value is changed to 8 seconds. The default is too long. innodb_lock_wait_timeout =8
[16 Aug 2016 4:38]
Ivan Ma
Configuration File attached : [mysqld] explicit_defaults_for_timestamp server-id=1 datadir=/u02/mysql/data/test1 basedir=/home/mysql/mysql port=3316 socket=/u02/mysql/data/test1/mysqld.sock character-set-server=utf8 log-error=/u02/mysql/data/test1/mysqld.error sql-mode=NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY log-bin=mysqllog.bin gtid-mode=on enforce-gtid-consistency master_info_repository=TABLE relay_log_info_repository=TABLE innodb_lock_wait_timeout =8 max_connections = 3500 sync_binlog=0 innodb_flush_log_at_trx_commit = 2
[16 Aug 2016 14:13]
MySQL Verification Team
Hi, First of all, InnoDB is a transactional storage engine (SE) and as such, lock wait timeouts and deadlocks are expected to happen and are the application programmer has to accommodate for those. Second, in order to be able to repeat the behavior, we require a full test case, including tables definitions, their content and commands that were run. Third, and last, as ACID SE's like InnoDB, have lock escalation, this could be that case. In order to find out whether this is so, we require the output from : SHOW ENGINE INNODB STATUS;
[17 Aug 2016 3:05]
Ivan Ma
Understood the lock timeout. But the impact is significant. With concurrency - same column with same length update, the system runs perfectly fine. With concurrency - same column with different length varchar update, the system performance is significantly impacted. The video is attached.
[23 Aug 2016 14:27]
MySQL Verification Team
Hi! Updating column with a value of the same length should be MUCH faster then the update of the columns with different lengths. This is due to the organization of the rows within the InnoDB page. The organization depends both, on the row format and file format that you have chosen for your InnoDB storage engine, or for your table, as this is part of the table definition within InnoDB SE. Hence, this is the expected behavior. As I wrote before, if a difference in wait time is too large, send us InnoDB statuses for both operations.
[1 Sep 2016 1:16]
Libing Song
Perf top showed below functions takes many cpu: 26.58% mysqld [.] lock_rec_has_to_wait_in_queue 12.13% mysqld [.] lock_rec_dequeue_from_page 11.02% mysqld [.] RecLock::lock_add 9.59% mysqld [.] lock_rec_add_to_queue 0.86% libc-2.23.so [.] _int_malloc 0.82% mysqld [.] ut_delay A simple test case to repeat it. CREATE TABLE t1(c1 INT PRIMARY KEY, c2 VARCHAR(40)) ENGINE=INNODB; INSERT INTO t1 VALUES(1, 'abcd'); SET GLOBAL innodb_deadlock_detect=OFF; --exec $MYSQL_SLAP -S var/tmp/mysqld.1.sock -u root --concurrency=100 --iterations=100000000 --query="UPDATE t1 SET c2=repeat('b', rand()*10) WHERE c1=1;" --create-schema=test
[1 Sep 2016 13:35]
MySQL Verification Team
For the operations in question, the perf ranking is approximately what is expected.
[1 Sep 2016 13:53]
MySQL Verification Team
Some further explanations on why this is not a bug. There is no secondary index, and the PRIMARY KEY is not being updated. The clustered index record can be updated within the same page. A variable-length record cannot be updated in-place if the length is changing. So, btr_cur_optimistic_update() cannot invoke the shortcut implemented in btr_cur_update_in_place(). However, the latching should be the same. The table should always consist of the single clustered index root page. However, the slowness should not propagate to user lock waits, but it could be due to the starvation problem. The 8-second user lock wait timeout should not be exceeded.
[1 Sep 2016 14:08]
Marko Mäkelä
I think that "Not a Bug" might be wrong. "Won't fix" could be more appropriate. Or we could try to do something about the problem. There is no secondary index, and the PRIMARY KEY is not being updated. The clustered index record can be updated within the same page, no matter if it is fixed-length or variable-length. A variable-length record cannot be updated in-place if the length is changing. So, btr_cur_optimistic_update() cannot invoke the shortcut implemented in btr_cur_update_in_place(). In either case, the table should always consist of the single clustered index root page. I do not see why the increased slowness of the operation should propagate to user lock timeouts. A page latch should not be held for longer than some milliseconds. Adding a millisecond to the latch hold time should not explain why the 8-second user lock wait timeout should be exceeded. Everything is in main memory, too. When it comes to InnoDB record locking, there is a small difference between in-place updates and optimistic updates: When we are updating a record in-place, the heap number will not change, and there is no need to update the heap numbers in the explicit locks that point to the page. A delete+insert would change the heap number and have to update the lock structures accordingly. An even more expensive class of updates would be a pessimistic update. In a pessimistic operation, pages have to be allocated or freed. Normally, they are caused by page splits or merges. Also BLOB operations will be handled as pessimistic operations. I would suggest a test where we have a column that is at least half the innodb_page_size. Then, UPDATE the BLOB column in the tests. Any update will do; InnoDB will do copy-on-write, keeping the index tree X-latched so that no other thread can access the data, not even for the SELECT part of the UPDATE. Something like this: create table test.test(col1 int primary key, col2 bigint,col3 blob) engine=innodb charset=latin1; begin; insert into test.test(col1,col2,col3) values(1,1,repeat('1',12345)); commit; Then, --query="update test.test set col2=col2+1,col3=repeat('a',12345) where col1=1 limit 1;" and so on. How would it perform? Does it make a difference if the table is defined as ROW_FORMAT=DYNAMIC instead of ROW_FORMAT=COMPACT or ROW_FORMAT=REDUNDANT? In the above BLOB case, the heap number should remain unchanged, because everything in the record itself would be fixed-length.
[2 Sep 2016 13:11]
Marko Mäkelä
If you want to test the impact of page splits and merges, you could try SQL like this: BEGIN;INSERT INTO test.test VALUES (2,2,REPEAT('b',7000)),(3,3,REPEAT('c',7000)),(4,4,REPEAT('d',7000)); UPDATE test.test set col2=col2+1,col3='bb' where col1=1; ROLLBACK; The INSERTs should force page splits, and the ROLLBACK should invoke btr_compress() to merge pages. For the INSERTs, you would have to take gap locks into account. The easiest way would be to INSERT with negative col1 values (-2,-3,-4) in one thread, and positive (2,3,4) in the other. In this way, you should still get transactional locking conflicts on the UPDATE only. Yet another case would be to use BLOBs for col3 in the UPDATE part of this test. Note that the REPEAT('b',7000) is on purpose chosen to be less than the InnoDB BLOB storage threshold when using innodb_page_size=16 (forcing in-page storage). And in my previous comment, the REPEAT('b',12345) should be above the BLOB storage threshold (forcing off-page storage).