当我进行乘法运算时,同一笔交易会返回不同的结果

时间:2018-08-30 13:09:33

标签: tidb

当我使用TiDB时,当我使两个事务同时运行时,我感到很奇怪。我原本希望得到与MySQL相同的值2,但是我得到的只是0、2、0、2、0、2 ...

对于两个数据库,tx_isolation都设置为“已读”。因此,合理的是,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)。

1 个答案:

答案 0 :(得分:0)

首先:

  

对于 TiDB ,仅支持 SNAPSHOT (最新版本)   交易隔离级别。但只能在开始交易之前看到提交的数据

     

TiDB 也不会更新交易中的相同值,   例如 MySQL SQL 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...


措辞

我认为对于 TiDB Transaction 跳过更新相同的值是很奇怪的,但是当不同的值时也可以成功更新,就像我们可以在循环中将b更新为其他值一样,我们总是可以获取最新更改 b。

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

我为此创建了一个问题:

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

参考文献:

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