mysql锁与事务

2023-11-03 09:27:32 阅读:1 编辑

使用事务的表,必须要用InnoDB类型

没有任何事务

$user = \App\Models\ImUser::find(1);
        $money = $user->money;
        print_r("old money:" . $money . "\n");
        $new_money = $money + 100;
        $user->money = $new_money;
        sleep(10);
        print_r("new money:" . $new_money . "\n");
        $user->save();

https://learnku.com/articles/44383 排他锁或写锁(一般用于并发对同一个字段进行修改)

DB::transaction(function () {
    $user = \App\Models\ImUser::lockForUpdate()->find(1);
    $money = $user->money;
    print_r("old money:" . $money . "\n");
    $new_money = $money + 100;
    $user->money = $new_money;
    sleep(10);
    print_r("new money:" . $new_money . "\n");
    $user->save();
});     
使用INNODB_TRX查看事务锁的情况
select * from INFORMATION_SCHEMA.INNODB_TRX;
下面对 innodb_trx 表的每个字段进行解释:
trx_id:事务ID。只读事务和非锁事务是不会创建id的。
trx_state:事务状态,有以下几种状态:RUNNING、LOCK WAIT、ROLLING BACK 和 COMMITTING。
trx_started:事务开始时间。
trx_requested_lock_id:事务当前正在等待锁的标识,可以和 INNODB_LOCKS 表 JOIN 以得到更多详细信息。
trx_wait_started:事务开始等待的时间。
trx_weight:事务的权重。代表修改的行数和被事务锁住的行数。为了解决死锁,innodb会选择一个高度最小的事务来当做牺牲品进行回滚。已经被更改的非交易型表的事务权重比其他事务高,即使改变的行和锁住的行比其他事务低。
trx_mysql_thread_id:事务线程 ID,可以和 PROCESSLIST 表 JOIN。
trx_query:事务正在执行的 SQL 语句。
trx_operation_state:事务当前操作状态。
trx_tables_in_use:当前事务执行的 SQL 中使用的表的个数。
trx_tables_locked:当前执行 SQL 的行锁数量。因为只是行锁,不是表锁,表仍然可以被多个事务读和写
trx_lock_structs:事务保留的锁数量。
trx_lock_memory_bytes:事务锁住的内存大小,单位为 BYTES。
trx_rows_locked:事务锁住的记录数。包含标记为 DELETED,并且已经保存到磁盘但对事务不可见的行。
trx_rows_modified:事务更改的行数。
trx_concurrency_tickets:该值代表当前事务在被清掉之前可以多少工作,由 innodb_concurrency_tickets系统变量值指定。
trx_isolation_level:当前事务的隔离级别。
trx_unique_checks:是否打开唯一性检查的标识。
trx_foreign_key_checks:是否打开外键检查的标识。
trx_last_foreign_key_error:最后一次的外键错误信息。
trx_adaptive_hash_latched:自适应哈希索引是否被当前事务阻塞。当自适应哈希索引查找系统分区,一个单独的事务不会阻塞全部的自适应hash索引。自适应hash索引分区通过 innodb_adaptive_hash_index_parts参数控制,默认值为8。
trx_adaptive_hash_timeout:是否为了自适应hash索引立即放弃查询锁,或者通过调用mysql函数保留它。当没有自适应hash索引冲突,该值为0并且语句保持锁直到结束。在冲突过程中,该值被计数为0,每句查询完之后立即释放门闩。当自适应hash索引查询系统被分区(由 innodb_adaptive_hash_index_parts参数控制),值保持为0。

共享锁(当你加sharedLock共享锁时读取数据,在你写入数据之前别人若也先写入的话,会报异常)

Artisan::command('test_sql', function () {

    DB::transaction(function () {
        $user = \App\Models\ImUser::sharedLock()->find(1);
        //$user = \App\Models\ImUser::find(1);
        $money = $user->money;
        print_r("old money:" . $money . "\n");
        $new_money = $money + 100;
        $user->money = $new_money;
        sleep(10);
        print_r("new money:" . $new_money . "\n");
        $user->save();
    });

});

异常信息:

   Illuminate\Database\QueryException  : SQLSTATE[40001]: Serialization failur
QL: update `im_users` set `parent_userID` = 100, `im_users`.`updated_at` = 202

  at E:\edison\im-system\vendor\laravel\framework\src\Illuminate\Database\Conn
    665|         // If an exception occurs when attempting to run a query, we'
    666|         // message to include the bindings with SQL, which will make
    667|         // lot more helpful to the developer instead of just the data
    668|         catch (Exception $e) {
  > 669|             throw new QueryException(
    670|                 $query, $this->prepareBindings($bindings), $e
    671|             );
    672|         }
    673|

  Exception trace:

  1   Doctrine\DBAL\Driver\PDO\Exception::("SQLSTATE[40001]: Serialization fai
")
      E:\edison\im-system\vendor\doctrine\dbal\lib\Doctrine\DBAL\Driver\PDO\Ex

  2   Doctrine\DBAL\Driver\PDO\Exception::new(Object(PDOException))
      E:\edison\im-system\vendor\doctrine\dbal\lib\Doctrine\DBAL\Driver\PDOSta

  Please use the argument -v to see more details.