Bug #102913 | REPEATABLE-READ: UPDATE EVENTS NOT MEET 'CURRENT-READ' RULE | ||
---|---|---|---|
Submitted: | 11 Mar 2021 3:06 | Modified: | 12 Mar 2021 4:29 |
Reporter: | Mips Lee | Email Updates: | |
Status: | Duplicate | Impact on me: | |
Category: | MySQL Server: InnoDB storage engine | Severity: | S3 (Non-critical) |
Version: | 8.0.18 | OS: | Any |
Assigned to: | CPU Architecture: | Any | |
Tags: | REPEATABLE-READ TRANSACTION |
[11 Mar 2021 3:06]
Mips Lee
[11 Mar 2021 13:27]
MySQL Verification Team
Hi Mr. Lee, Thank you for your bug report. Your report is a duplicate of the following one: https://bugs.mysql.com/bug.php?id=102752 If you disagree with this, then first, test with our latest 8.0 release and then explain your problem in much more clear wording and full test case. Duplicate.
[11 Mar 2021 14:23]
Mips Lee
I don't think it's similar to "https://bugs.mysql.com/bug.php?id=102752". My core opinion is : The update statement updates a row of data during the update process, but since the updated value is equal to the current value, the number of affected rows is 0. But row locks still exist, so I think this is a semantic error.
[11 Mar 2021 14:38]
MySQL Verification Team
Hi, This is expected behaviour. Row lock MUST exist, since it is taken before reading the row. Not a bug.
[11 Mar 2021 15:11]
Mips Lee
Hi friends, I understand what you mean. I didn't express clearly what I meant just now. At the repeatable-read level, each unlocked select statement determines the data rows visible in the current view based on the transaction id array at startup. What I want to express is: when update sql is updating met condition rows, it will add row locks, read the newest row-transaction-id version data, and update them. But in my example above, sessionA starts first, sessionB After starting, and updating the field "name" by id=1 result : affected rows 1 , and finally submitting the transaction, then sessionA updated the age field of the row data according to the condition of id=1, but because the updated value is the same as the old value, SessionA The update result shows affected 0 rows. At this time, sessionA has the row lock of row id=1, so I think in the next select statement of sessionA, query the row with id=1 again, it should be able to see the modified name filed value of sessionB submitted, but in fact it is not. If sessionA updates the age field, the updated value is different from the old value. When affected rows =1, the select statement can query the updated name value of sessionB. This is in line with our expectations, but when affected 0 rows, it does not meet my expectations~
[11 Mar 2021 17:57]
Yang Sk
Tested with mysql-8.0.23 How to reproduce: create table t( pk int primary key, col0 int, col1 int ); insert into t values (1,1,1); -- T1 -- -- T2 -- begin; select * from t where pk=1; begin; select * from t where pk=1; -- 1 1 1 update t set col0=2 where pk=1; update t set col1=1 where pk=1; commit; select * from t where pk=1; -- 1 1 1 commit; select * from t where pk=1; -- 1 2 1 After T1 commits, the update statement made by T2 holds the lock on pk=1 and update(0 rows affected), but can't see the LATEST VERSION of the row(col0 = 2) Mentioned by Justin Swanhart in https://bugs.mysql.com/bug.php?id=102752 "In a repeatable-read transaction, DML statements that lock rows always see the LATEST VERSION of a row. And any rows modified by DML statements become visible to the transaction that modified them.". If what he said is accurate, T2 should see the LATEST VERSION of the row where col0 = 2, right?
[12 Mar 2021 2:02]
Sunny Bains
REPEATABLE-READ is one of those things where the exact semantics are mostly implementation dependent. IInnoDB itself has historically claimed its REPEATABLE-READ as "OracleDB like". That's about as precise as it gets at the high level. What Justin describes is correct but there is an exception. The exception is due to the mechanics under the hood. 1. Server first does a read of the row from the table 2. Server then updates the columns in the row and sends an update ro request to the SE (InnoDB) 3. InnoDB then calculates the difference in the columns and creates an update delta, in the case for T2 it's empty. 4. InnoDB then locks the table i IX mode etc. etc. 5. There is nothing to apply in the delta and so it returns (it doesn't re-read the latest contents of the row). In the following select before the commit you will see the same data because InnoDB hasn't actually read the latest data in step #5.
[12 Mar 2021 4:29]
Mips Lee
What Sunny Bains said made me understand : There is a mechanism of "creates an update delta" when InnoDB is updating. But because of this mechanism, it does not meet our expectations when the number of updated rows is 0. And due to past experience, we thought we could read the latest submitted row.
[15 Mar 2021 6:04]
Debarun Banerjee
Looks like the confusion is about "locking the row" Vs "updating the row" [1] doesn't necessarily imply [2] and in the bug example it only acquires lock and that is not enough for the row to be visible in read-only statements in RR isolation currently in Innodb. It is quite inline with the comments from other bug ... "Mentioned by Justin Swanhart in https://bugs.mysql.com/bug.php?id=102752 ..." In a repeatable-read transaction, 1. DML statements that lock rows always see the LATEST VERSION of a row. 2. Any rows modified by DML statements become visible to the transaction that modified them. [1] is not satisfied in this case because the statement in question is "read-only" and doesn't lock the row. [2] is also not satisfied because the row is not modified. The root cause is the base anomaly of our RR isolation that the "read-only" and "read-write" statements could read different data. e.g. "select for update" Vs "select". -------------------------------------------------------------- mysql> create table t2(col1 int primary key, col2 int); mysql> insert into t2 values(1, 10); Trx-1: mysql>begin; mysql> select * from t2; // Creates the snapshot +------+------+ | col1 | col2 | +------+------+ | 1 | 10 | +------+------+ 1 row in set (0.00 sec) Trx-2 ------ mysql> update t2 set col2 = 100 where col1 = 1; // 1,100 Trx-1 ------ mysql> select * from t2 for update; // Locks the row and see latest +------+------+ | col1 | col2 | +------+------+ | 1 | 100 | +------+------+ 1 row in set (0.00 sec) mysql> select * from t2; // Still reads old row version although // the row is locked. +------+------+ | col1 | col2 | +------+------+ | 1 | 10 | +------+------+ 1 row in set (0.00 sec) mysql> update t2 set col2 = 200 where col1 = 1; // 1,200 mysql> select * from t2; // Sees the latest change as the row // is modified by current transaction. +------+------+ | col1 | col2 | +------+------+ | 1 | 200 | +------+------+ 1 row in set (0.00 sec)