1

当我使用 TiDB 时,我发现当我同时运行两个事务时很奇怪。我期望得到与 MySQL 一样的值 2,但我得到的只是 0、2、0、2、0、2 ......

对于这两个数据库,tx_isolation 都设置为“read-committed”。所以 select 语句返回 2 是合理的,因为它已经提交了。

这是测试代码:

for i in range(10):
    conn1 = mysql.connector.connect(host='',
                                port=4000,
                                user='',
                                password='',
                                database='',
                                charset='utf8')
    conn2 = mysql.connector.connect(host='',
                                port=4000,
                                user='',
                                password='',
                                database='',
                                charset='utf8')

    cur1 = conn1.cursor()
    cur2 = conn2.cursor()

    conn1.start_transaction()
    conn2.start_transaction()

    cur2.execute("update t set b=%d where a=1" % 2)
    conn2.commit()

    cur1.execute("select b from t where a=1")
    a = cur1.fetchone()
    print(a)

    cur1.execute("update t set b=%d where a=1" % 0)
    conn1.commit()

    cur1.close()
    cur2.close()
    conn1.close()
    conn2.close()

表 t 是这样创建的:

CREATE TABLE `t` (
  `a` int(11) NOT NULL AUTO_INCREMENT,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`a`)
) 

和 (1,0) 最初被插入。

4

1 回答 1

0

首先:

TiDB支持SNAPSHOT (最新版本) Transactions Isolation Level。但它只能在事务开始之前看到提交的数据。

并且TiDB也不会在事务中更新相同的值,如MySQLSQL Server等。

对于MySQL,当使用READ COMMITTED隔离级别时,它会读取已提交的数据,因此它会读取其他事务已提交的数据。

所以作为你的代码片段:

TiDB 第一轮工作流程:

               T1                                   T2

     +--------------------+
     | transaction start  |
     |      (b = 0)       |
     +---------+----------+
               |
               |
               |                        +------------------------------+
               | <----------------------+  update `b` to 2, and commit |
               |                        +------------------------------+
               |
               |
   +-----------+-----------+
   | select b should be 0, |
   | since tidb will only  |
   | get the data before   |
   | transaction committed |
   +-----------+-----------+
               |
               v

+------------------------------+
|      update value to 0       |
| (since 0 is equal to the     |
| transaction started value,   |
| tidb will ignore this update)|
+------------------------------+
                                  +
                                  |
                                  |
                                  |
                                  v

                      +-------------------------+
                      |so finally `b` will be 2 |
                      +-------------------------+

TiDB 第 2 轮工作流程:

               T1                                   T2

     +--------------------+
     | transaction start  |
     |      (b = 2)       |
     +---------+----------+
               |
               |
               |                        +------------------------------+
               | <----------------------+  update `b` to 2, and commit |
               |                        +------------------------------+
               |
               |
   +-----------+-----------+
   | select b should be 2, |
   | since tidb will only  |
   | get the data before   |
   | transaction committed |
   +-----------+-----------+
               |
               v

+------------------------------+
|      update value to 0       |
| (since 0 is not equal to 2   |
+------------------------------+
                                  +
                                  |
                                  |
                                  |
                                  v

                      +-------------------------+
                      |so finally `b` will be 0 |
                      +-------------------------+

所以TiDB会输出如下:

0, 2, 0, 2, 0, 2...

MySQL工作流程:

                      T1                                   T2


          +----------------------+
          |  transaction start   |
          |       ( b = 0 )      |
          +-----------+----------+
                      |
                      |
                      |
                      |                         +---------------------------+
                      |  <----------------------+update `b` to 2, and commit|
                      |                         +---------------------------+
                      |
                      |
                      v

+--------------------------------------------+
| select b should be 2,                      |
| since use READ COMMITTED isolation level,  |
| it will read committed data.               |
+---------------------+----------------------+
                      |
                      |
                      v

           +--------------------+
           | update value to 0  |
           +--------------------+
                                        +
                                        |
                                        |
                                        |
                                        v

                             +--------------------------+
                             | so finally `b` will be 0 |
                             +--------------------------+

所以MySQL可以连续输出:

2, 2, 2, 2...


最后一句话

我认为TiDBTransaction跳过更新相同的值是很奇怪的,但是当使用不同的值时,它也可以成功更新,就像我们可以在循环中更新到不同的值一样,我们总是可以得到最新的变化b。b

所以也许最好在相同值不同值之间保持相同的行为。

我为此创建了一个问题:

https://github.com/pingcap/tidb/issues/7644

参考:

https://github.com/pingcap/docs/blob/master/sql/transaction-isolation.md

于 2018-09-07T14:52:10.753 回答