Geotto
2021-08-19 11:19
采纳率: 100%
浏览 53
已结题

关于MySQL中SELECT语句的原子性问题

在MySQL数据库中存在一张settings表:

snamesvalue
no0
no20

同时存在以下两个函数:

CREATE FUNCTION `fn_inc_no`() RETURNS int(11)
BEGIN
    DECLARE val INT DEFAULT 0;
    
    UPDATE settings
    SET svalue = svalue + 1
    WHERE sname = 'no';
    
    SELECT svalue INTO val FROM settings WHERE sname = 'no';
    
    RETURN val;
END

CREATE FUNCTION `fn_inc_no2`() RETURNS int(11)
BEGIN
    DECLARE val INT DEFAULT 0;
    
    UPDATE settings
    SET svalue = svalue + 1
    WHERE sname = 'no2';
    
    SELECT svalue INTO val FROM settings WHERE sname = 'no2';
    
    RETURN val;
END

如果此时在Java程序中运行如下代码:

Statement query = conn.createStatement();
ResultSet rs = query.executeQuery("SELECT fn_inc_no() AS no, fn_inc_no2() AS no2");

在多个线程(测试时使用了10个线程)中多次(测试时重复执行10000次)执行以上代码,最后得到的 no 和 no2 总是相等的。这是否说明了这条SELECT语句当中的fn_inc_no和fn_inc_no2总是在同一个事务中执行的,是否说明此条SELECT语句在MySQL中本身是一个原子操作?

  • 收藏

4条回答 默认 最新

  • 两个猕猴桃 2021-08-19 12:44
    已采纳

    不同的函数操作的两个不同行,说明不了任何问题。
    前条语句更新,后条语句查询,肯定返回的是更新后的结果。

    也不用多个线程循环,你让两个函数操作同一行记录, 第一赋值为3,第二个不赋值,直接查询,看看能不能查询出3即可。

    已采纳该答案
    打赏 评论
  • 不同的函数调用是在不同的事务中。

    1 打赏 评论
  • 老紫竹 2021-08-19 14:15

    本来就是2行,你分别更新,分别查询相同的次数, 结果集肯定一样啊?凭啥不一样。

    1 打赏 评论
  • Geotto 2021-08-19 16:47

    在mysql数据库中创建了settings和settings两张表:
    settings:

    snamesvalue
    no0

    settings2:

    snamesvalue
    no0

    同时有两个函数分别对这两张表进行修改:

    DELIMITER $$
    CREATE FUNCTION `fn_inc_no`() RETURNS int
        DETERMINISTIC
    BEGIN
        DECLARE val INT(11) DEFAULT 0;
        
        UPDATE settings
        SET svalue = svalue + 1
        WHERE sname = 'no';
    
        SELECT svalue INTO val
        FROM settings
        WHERE sname='no';
        
        RETURN val;
    END$$
    DELIMITER ;
    
    DELIMITER $$
    CREATE FUNCTION `fn_inc_no2`() RETURNS int
        DETERMINISTIC
    BEGIN
        DECLARE val INT(11) DEFAULT 0;
        
        UPDATE settings2
        SET svalue = svalue + 1
        WHERE sname = 'no';
    
        SELECT svalue INTO val
        FROM settings2
        WHERE sname='no';
        
        RETURN val;
    END$$
    DELIMITER ;
    

    执行以下语句:

    SELECT fn_inc_no() AS no1, fn_inc_no2() AS no2;
    

    然后执行

    -- 需要开启binlog
    -- <binlog>替换为binlog文件名,<pos>替换为binlog文件中的起始位置
    SHOW BINLOG EVENTS IN '<binlog>' FROM <pos>
    

    可以发现fn_inc_no和fn_inc_no2是在相同的事务中执行的:

    | Log_name          | Pos       | Event_type           | Server_id | End_log_pos | Info                                                              |
    | ----------------- | --------- | -------------------- | --------- | ----------- | ----------------------------------------------------------------- |
    | mysql-bin.000055  | 916835895 | Anonymous_Gtid       | 133026001 | 916835974   | SET @@SESSION.GTID_NEXT= 'ANONYMOUS'                              |
    | mysql-bin.000055    | 916835974 | Query                | 133026001 | 916836058   | BEGIN                                                             |
    | mysql-bin.000055    | 916836058 | Table_map            | 133026001 | 916836119   | table_id: 3885 (test.settings)                                    |
    | mysql-bin.000055    | 916836119 | Table_map            | 133026001 | 916836181   | table_id: 3887 (test.settings2)                                   |
    | mysql-bin.000055    | 916836181 | Update_rows          | 133026001 | 916836231   | table_id: 3885                                                    |
    | mysql-bin.000055    | 916836231 | Update_rows          | 133026001 | 916836279   | table_id: 3887 flags: STMT_END_F                                  |
    | mysql-bin.000055    | 916836279 | Xid                  | 133026001 | 916836310   | COMMIT /* xid=92321411 */                                         |
    
    打赏 评论

相关推荐 更多相似问题