-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Query in transaction may return rows with same unique index column value #24195
Comments
The different behaviours bettween int handle and non-int handle is caused that the |
Scene 2:Pessimistic transaction /* s1 */ drop table if exists t;
/* s1 */ create table t (c1 varchar(10), c2 int, c3 char(20), primary key (c1, c2));
/* s1 */ insert into t values ('tag', 10, 't'), ('cat', 20, 'c');
/* s2 */ begin;
/* s2 */ update t set c1=reverse(c1) where c1='tag';
/* s4 */ begin;
/* s4 */ insert into t values('dress',40,'d'),('tag', 10, 't');
/* s2 */ commit;
/* s4 */ select * from t use index(primary) order by c1,c2;
when primary key is clustered index, query return
mysql> /* s4 */ select * from t use index(primary) order by c1,c2;
+-------+----+------+
| c1 | c2 | c3 |
+-------+----+------+
| cat | 20 | c |
| dress | 40 | d |
| tag | 10 | t |
+-------+----+------+
3 rows in set (0.00 sec)
when primary key is nonclustered index, query return
mysql> /* s4 */ select * from t use index(primary) order by c1,c2;
+-------+----+------+
| c1 | c2 | c3 |
+-------+----+------+
| cat | 20 | c |
| dress | 40 | d |
| tag | 10 | t |
| tag | 10 | t |
+-------+----+------+
4 rows in set (0.00 sec)
mysql return
mysql> /* s4 */ select * from t use index(primary) order by c1,c2;
+-------+----+------+
| c1 | c2 | c3 |
+-------+----+------+
| cat | 20 | c |
| dress | 40 | d |
| gat | 10 | t |
| tag | 10 | t |
+-------+----+------+
4 rows in set (0.00 sec) |
As described above, the different behaviours bettwen int handle and non-int handle sometimes may confuse the use that two rows with same unique key values are returned doing snapshot read in a transaction. |
It's needed to fix this issue, the way to to so is to change the |
Scene 3:insert some record into the table,union scan can not merge when update clustered index set the value of the first record bigger than the second record; /* s1 */ drop table if exists test1;
/* s1 */ create table test1(a int primary key clustered, b int);
/* s1 */ insert into test1 values(1,1),(5,5),(10,10);
/* s1 */ begin;
/* s1 */ update test1 set a=8 where a=1;
/* s2 */ begin;
/* s2 */ update test1 set a=a+1;
/* s1 */ commit;
/* s2 */ select * from test1;
actual return:
mysql> /* s2 */ select * from test1;
+----+------+
| a | b |
+----+------+
| 1 | 1 |
| 6 | 5 |
| 9 | 1 |
| 11 | 10 |
+----+------+
4 rows in set (0.00 sec)
expect return:
mysql> /* s2 */ select * from test1;
+----+------+
| a | b |
+----+------+
| 6 | 5 |
| 9 | 1 |
| 11 | 10 |
+----+------+
3 rows in set (0.01 sec) |
@vivid392845427 But it's still incompatbible or different for cluster and non cluster index for this case, as the non-cluster index case the It's still the case cluster/non-cluster index table will have incompatible behaviour problem. |
There is one thing that confused me, the optimistic transactions will not do unique key check for insert statement before commit. This behavior is consistent across all versions of TiDB. However, it brought the problem of dealing with union membuffer data and snapshot data together. Further, which data to be choose is hard to answer. Lines 223 to 228 in 5516d7d
From the comment, we can notice what the developer thought is the conflict transaction fails when committing anyway, we do not handle the read issue for simplicity (Anomalies in aborted transaction can be dismissed in some theories). IMO, there are 2 ways to solve this issue.
/* s1 */ drop table if exists t;
/* s1 */ create table t (a int, b int, primary key (a));
/* s1 */ insert into t values (1, 10);
/* s1 */ begin optimistic;
/* s1 */ insert into t values (1, 11); -- Success in optimistic transaction
/* s1 */ select * from t; -- What result set do we expect? If we take membuffer data prior, we'll got (1, 11)
/* s1 */ commit; -- Fail in optimistic transaction
I prefer the second solution, @cfzjywxk, what's your idea? |
@you06 |
If I understood correctly, we may fix it by the first solution, handle it in union scan. The second solution may be a long term improvement in which we should care about if our users will suffer unexpected failures introduced by this change. @cfzjywxk is it what you mean? |
@you06 |
The weird behavior of MySQL should blame to its lazy snapshot strategy, if you select after s4's begin statement(this will take a snapshot for MySQL's transaction). Then we'll get the following result, this is also the expected result for me. /* s1 */ drop table if exists t;
/* s1 */ create table t (c1 varchar(10), c2 int, c3 char(20), primary key (c1, c2));
/* s1 */ insert into t values ('tag', 10, 't'), ('cat', 20, 'c');
/* s2 */ begin;
/* s2 */ update t set c1=reverse(c1) where c1='tag';
/* s4 */ begin;
/* s4 */ select * from t use index(primary) order by c1,c2;
/* s4 */ insert into t values('dress',40,'d'),('tag', 10, 't');
/* s2 */ commit;
/* s4 */ select * from t use index(primary) order by c1,c2;
-- s4 >> +-------+----+----+
-- s4 | C1 | C2 | C3 |
-- s4 +-------+----+----+
-- s4 | cat | 20 | c |
-- s4 | dress | 40 | d |
-- s4 | tag | 10 | t |
-- s4 +-------+----+----+
/* s4 */ commit;
/* s2 */ select * from t use index(primary) order by c1,c2;
-- s2 >> +-------+----+----+
-- s2 | C1 | C2 | C3 |
-- s2 +-------+----+----+
-- s2 | cat | 20 | c |
-- s2 | dress | 40 | d |
-- s2 | gat | 10 | t |
-- s2 | tag | 10 | t |
-- s2 +-------+----+----+ |
@you06 @vivid392845427 @zyguan |
Maybe we can document it as a known issue currently. IMO, it's actually not a conventional usage, since a optimistic transaction is not guaranteed to be committed, one should handle the intermediate results within the transaction carefully. |
There are similarities to the optimization of delete-your-writes, since #28806 is blocked by some complex problems, I think it's better to hold this issue by now.
I agree with this point, maybe we can downgrade the severity of this issue. |
Bug Report
Please answer these questions before submitting your issue. Thanks!
1. Minimal reproduce step (Required)
2. What did you expect to see? (Required)
Only one row is returned by the select statement.
3. What did you see instead (Required)
4. What is your TiDB version? (Required)
Release Version: v5.0.0-43-g41871e0c8
Edition: Community
Git Commit Hash: 41871e0
Git Branch: release-5.0
UTC Build Time: 2021-04-20 09:24:52
GoVersion: go1.13
Race Enabled: false
TiKV Min Version: v3.0.0-60965b006877ca7234adaced7890d7b029ed1306
Check Table Before Drop: false
The text was updated successfully, but these errors were encountered: