当前位置: 首页 > news >正文

[MySQL数据库] 事务与锁

🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:
🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482
🍕 Collection与数据结构 (93平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀线程与网络(97平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(95平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482
🎃Redis(97平均质量分)https://blog.csdn.net/2301_80050796/category_12777129.html?spm=1001.2014.3001.5482
🐰RabbitMQ(97平均质量分) https://blog.csdn.net/2301_80050796/category_12792900.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
在这里插入图片描述

目录

  • 1. 什么是事务
  • 2. 为什么使用事务
  • 3. 怎么使用事务
  • 4. 如何实现原子性
  • 5. 如何实现持久性
  • 6. 隔离性的实现原理
    • 6.1 事务的隔离性
    • 6.2 事务的隔离级别
    • 6.3 锁
      • 6.3.1 锁类型与锁模式
      • 6.3.2 共享锁和独占锁-Shared and Exclusive Locks
      • 6.3.3 意向锁-Intention Locks
      • 6.3.4 索引记录锁-record locks
      • 6.3.5 间隙锁-gap locks
      • 6.3.6 临键锁-next key locks
      • 6.3.7 插入意向锁-Insert Intention Locks
      • 6.3.8 AUTO-INC Locks
      • 6.3.9 死锁
        • 6.3.9.1 什么是死锁
        • 6.3.9.2 死锁产生的条件
        • 6.3.9.3 InnoDB对死锁的检测
        • 6.3.9.4 如何避免死锁
    • 6.4 查看并设置隔离级别
    • 6.5 READ UNCOMMITED 读未提交与脏读
      • 6.5.1 实现方式
      • 6.5.2 存在问题
      • 6.5.3 问题重现
    • 6.6 READ COMMITTED 读已提交与不可重复读
      • 6.6.1 实现方式
      • 6.6.2 存在问题
      • 6.6.3 问题重现
    • 6.7 REPEATABLE READ 可重复读与幻读
      • 6.7.1 实现方式
      • 6.7.2 存在问题
      • 6.7.3 问题重现
    • 6.8 serializable 串行化
      • 6.8.1 实现方式
      • 6.8.2 存在问题
    • 6.9 不提供隔离级别的性能安全
    • 6.10 多版本控制(MVCC)
      • 6.10.1 实现原理
        • 6.10.1.1 版本链
        • 6.10.1.2 ReadView
      • 6.10.2 MVCC如何解决脏读与不可重复读问题

1. 什么是事务

事务指逻辑上的一组操作,就是把多个操作打包为一个操作,组成这组操作的各个单元,要么全部成功,要么全部失败。在不同的环境中,都可以有事务。对应在数据库中,就是数据库事务,数据库事务可以有效避免部分执行,部分未执行的中间状态.如果在执行的过程中,我们不使用事务的话,当服务崩溃的时候,整体的数据就会不正确.
在这里插入图片描述
以下的四点在事务的整个执行过程中必须要得到保证,这也就是事务的ACID特性:

  1. Atomicity(原子性): 一个事务中的所有操作,要么全部成功,要么全部失败,不会出现之执行了一半的情况,如果事务在执行的过程中发生了错误,那么会触发回滚机制(rollback),会回滚到事务开始前的状态,就像这个事务从来没有执行过一样.在回滚的时候,会使用undolog进行回滚,undolog中记录了数据修改之前的记录.
  2. Consistency(一致性): 在事务开始之前和事务结束之后,数据的完整性不会破坏,这表示写入的数据必须完全符合所有的预设规则,包括数据的精度,关联性以及关于事务执行的过程中服务器崩溃后如何恢复.
  3. Isolation(隔离性): 数据库允许多个并发事务同时对数据进行读写和修改,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据不一致,事务可以指定不同的隔离级别,以权衡在不同的应用场景之下数据库的性能和安全,后面的小节会详细介绍.
  4. Durability(持久性): 事务处理之后,对数据的修改将永久的写入存储(磁盘)介质,即便系统故障也不会丢失.

需要重点说明的是,事务最终要保证数据的可靠和⼀致,也就是说 ACID 中的Consistency(⼀致
性)是最终的目的,那么当事务同时满⾜了Atomicity(原⼦性),Isolation(隔离性)和Durability(持久性)时,也就实现了⼀致性.

2. 为什么使用事务

事务具备的ACID特性,也是我们使用事务的原因,在我们日常的业务场景中有大量的需求要用事务来保证.支持事务的数据库能够简化编程的模型,不需要我们去考虑各种各样的潜在的错误和并发的问题,在使用事务的过程中,要么提交,要么回滚,不用去考虑网络异常,服务器宕机等其他的因素,因此我们经常接触到事务本质上是数据库对ACID模型的一个实现,是为应用层服务的.

3. 怎么使用事务

  • 首先需要存储引擎支持事务,我们默认的存储引擎InnoDB引擎就是支持事务的,可以通过show engines语句查看.
    在这里插入图片描述
  • 通过以下的语句即可完成对事务的控制
    • start Transaction或者begin开启一个新事务.
    • commit提交当前事务,并对其进程持久化保存.
    • rollback回滚事务,取消其更改.
    • set autocommit禁用或者启用当前会话的默认自动提交模式,autocommit是一个系统变量可以通过指定选项指定也可以通过命令行设置--autocommit[={OFF|ON}].

下面进行演示,执行之后进行回滚:

# 开启事务
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
# 在修改之前查看表中的数据
mysql> select * from account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 1000.00 |
| 2 | 李四 | 1000.00 |
+----+------+---------+
2 rows in set (0.00 sec)
# 张三余额减少100
mysql> UPDATE account set balance = balance - 100 where name = '张三';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
# 李四余额增加100
mysql> UPDATE account set balance = balance + 100 where name = '李四';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
# 在修改之后,提交之前查看表中的数据,余额已经被修改
mysql> select * from account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 900.00 |
| 2 | 李四 | 1100.00 |
+----+------+---------+
2 rows in set (0.00 sec)
# 回滚事务
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
# 再查询发现修改没有⽣效
mysql> select * from account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 1000.00 |
| 2 | 李四 | 1000.00 |
+----+------+---------+
2 rows in set (0.00 sec)

当然也可以对事务进行提交

# 开启事务
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
# 在修改之前查看表中的数据
mysql> select * from account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 1000.00 |
| 2 | 李四 | 1000.00 |
+----+------+---------+
2 rows in set (0.00 sec)
# 张三余额减少100
mysql> UPDATE account set balance = balance - 100 where name = '张三';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
# 李四余额增加100
mysql> UPDATE account set balance = balance + 100 where name = '李四';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
# 在修改之后,提交之前查看表中的数据,余额已经被修改mysql> select * from account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1  | 张三  | 900.00  |
| 2  | 李四  | 1100.00 |
+----+------+---------+
2 rows in set (0.00 sec)
# 提交事务
mysql> commit;
Query OK, 0 rows affected (0.01 sec)
# 再查询发现数据已被修改,说明数据已经持久化到磁盘
mysql> select * from account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1  | 张三  | 900.00  |
| 2  | 李四  | 1100.00 |
+----+------+---------+
2 rows in set (0.00 sec)
  • 默认情况下MySQL启用自动提交,也就是每个语句都是一个事务,不能使用rollback来撤销执行结果,但是如果在语句执行期间发生错误,则自动回滚.
  • 通过set autocommit设置自动提交与手动提交
# 查看当前的事务提交模式
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    | # ON表⽰⾃动提交模式
+---------------+-------+
1 row in set, 1 warning (0.02 sec)
# 设置为⼿动提交(禁⽤⾃动提交)
mysql> SET AUTOCOMMIT=0; # 方式一
mysql> SET AUTOCOMMIT=OFF; # 方式二
  • 手动提交模式之下,提交或回滚事务时直接使用commit或者rollback
mysql> UPDATE account set balance = balance - 100 where name = '张三';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
# 在修改之后查看表中的数据,余额已经被修改
mysql> select * from account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 800.00 | # ⽐原来的减少了100
| 2 | 李四 | 1100.00 |
+----+------+---------+
2 rows in set (0.00 sec)
# 提交事务
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
# 再查询是被修改之后的值,说明数据已经持久化到磁盘
mysql> select * from account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 800.00 |
| 2 | 李四 | 1100.00 |
+----+------+---------+
2 rows in set (0.00 sec)
  • 通过set autocommit设置自动提交
mysql> SET AUTOCOMMIT=1; # ⽅式⼀
mysql> SET AUTOCOMMIT=ON; # ⽅式⼆
Query OK, 0 rows affected (0.00 sec)
# 再次查看事务提交模式
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    | # ON表⽰⾃动提交模式
+---------------+-------+
1 row in set, 1 warning (0.00 sec)
  • 注意: 只要使用start Transaction或者begin开启事务之后,必须要通过commit提交才会支持持久化,与是否设置set autocommit无关.

4. 如何实现原子性

在一个事务的执行过程中,如果多条DML语句顺利执行,那么结果会最终写入数据库,如果在事务的执行过程中,其中一条DML语句出现异常,导致后面的语句无法继续执行或即使继续执行也会导致数据不完整,不一致,这时前面执行的语句已经对数据做出了修改,如果要保证一致性,就需要对之前的修改做撤销操作,这个撤销操作称为回滚rollback,如下图所示:

在这里插入图片描述

  • 事务的回滚原理是什么呢?回滚过程中依据的是什么呢?依据的是InnoDB中的Undolog,在事务执行每个DML之前会把原始的数据放在一个日志中,作为回滚的依据,这个日志称为undolog,在不考虑缓存和刷盘的条件下,执行过程如下:
    在这里插入图片描述
  • 当需要回滚的时候,MySQL根据操作的类型,Insert undo链或update undo链中读取响应的日志记录,反向执行修改,使数据还原,完成回滚.
  • 通过undo log实现了数据回滚操作,这时就可以保证事务成功的时候全部的sql语句都执行成功,在事务失败的时候全部sql语句都执行失败.

5. 如何实现持久性

提交的事务要把数据写入到存储介质中,比如磁盘.如果在服务器突然断电的情况下,一个事务中的多个修改操作,只有一部分写入了数据文件,一部分没有写入,如果不做处理的话,就会造成数据的丢失,从而导致数据不完整,也就不能保证一致性.
在真正写入数据文件之前,MySQL会把事务中的所有DML操作以日志的形式记录下来,以便在服务器下次启动的时候进行恢复操作,恢复操作的过程就是把日志中没有写到数据文件的记录重新执行一遍,保证所有的需要保存的数据都持久化到存储介质中,我们把这个日志称为Redo Log(重做日志),生成重做日志是保证数据一致性的重要环节.在持久化过程中,还包括缓冲池,doublewrite Buffer(双写缓冲区),Binary Log(二进制日志)等.InnoDB日志生成机制以及崩溃恢复机制如下:

在这里插入图片描述
在数据真正落盘之前,会把日志写入Redo Log和双写缓冲区中,如果在此期间MySQL发生崩溃,会根据之前生成的RedoLog和双写缓冲区中的数据把数据恢复过来,保证数据正确落盘.

6. 隔离性的实现原理

6.1 事务的隔离性

MySQL服务可以通过多个客户端访问,每个客户端执行的DML语句以事务为单位,那么不同的客户端在对同一张表中的同一条数据进行修改的时候就可能出现相互影响的情况,为了保证不同的事务之间在执行的过程中不受影响,那么事务之间就需要相互隔离,这种就是事务的隔离性.

6.2 事务的隔离级别

事务具有隔离性,那么如何实现事务之间的隔离,隔离到什么程度,如何保证数据安全的同时也要兼顾性能,这都是要思考的问题.
在并发执行的过程中,多个线程对同一个共享变量修改的时候,在不加限制的情况下会出现线程安全问题,我们解决线程安全问题时,一般的做法是通过对修改操作进行加锁,同理,多个事务在对同一个表中的同一条数据进行了修改时,如果要实现事务之间的隔离也可以通过锁来完成,在MySQL中常见的锁包括: 读锁,写锁,行锁,间隙锁,next-key锁等,不同的锁策略联合多版本并发控制可以实现事务间不同程度的隔离,称为事务的隔离级别.不同的隔离级别能在性能和安全方面做了取舍,有的隔离级别注重并发性,有的注重安全性,有的则是并发和安全适中,在MySQL的InnoDB引擎中事务的隔离级别是四种,分别是:

  • Read uncommited,读未提交
  • Read committed,读已提交
  • repeatable Read,可重复读(默认)
  • serializable,串行化

6.3 锁

实现事务隔离级别的过程中用到了锁,所谓的锁就是在事务A修改某些数据时,对这些数据加一把锁,防止其他的事务同时对这些数据执行修改操作,当事务A完成修改操作之后,释放当前持有的锁,以便其他的事务再次上锁执行对应的操作,不同的存储引擎中的锁功能并不相同,这里我们重点介绍InnoDB存储引擎中的锁.

6.3.1 锁类型与锁模式

  • 锁类型
    锁类型依赖于存储引擎,在InnoDB存储引擎中按照锁的粒度分为,行级锁recode和表级锁table:
    • 行级锁也叫行锁,是对表中的某些具体的数据进行加锁.
    • 表级锁也叫表锁,是对整个数据表加锁.
    • 在之前的版本中也有页级锁,也叫页锁,锁定的是一个数据页,MySQL8中没有页级锁.
  • 锁模式
    锁模式,用来描述如何请求(申请)锁,分为共享锁(S),独占锁(X),意向共享锁(IS),意向独占锁(IX),记录锁,间隙锁,next-key锁,auto-inc锁,空间索引的谓词锁等.

6.3.2 共享锁和独占锁-Shared and Exclusive Locks

InnoDB实现了标准的行级锁,分为两种分别是共享锁(S锁)和独占锁(X锁),独占锁也称排它锁.

  • 共享锁(S锁): 允许持有该锁的事务读取表中的一行记录,同时允许其他事务在锁定行上加另一个共享锁并读取被锁定的对象,但是不能对其进行写操作.
  • 独占锁(X锁): 允许持有该锁的事务对数据进行更新或者删除,同时不论其他事务对锁定进行读取或者修改都不允许对锁定行进行加锁.
  • 如果事务T1持有R行上的共享锁(s),那么事务T2请求R行上的锁时会有如下处理:
    • T2请求S锁会立即被授予,此时T1和T2都对R行持有S锁.
    • T2请求X锁不能立即被授予,阻塞到T1释放持有的锁.
  • 如果事务T1持有R行上的独占锁(X),那么T2请求R行上的任意类型锁都不能立即被授予,事务T2必须等待事务T1释放R行上的锁.

6.3.3 意向锁-Intention Locks

  • InnoDB支持多粒度锁,允许行锁和表锁共存.
  • InnoDB使用意向锁实现多粒度级别的锁,意向锁是表级别的锁,它并不是真正意义上的加锁,而是在data_locks中记录事务以后要对表中的哪一行加哪种类型的锁(共享锁或者排它锁),意向锁分为两种:
    • 意向共享锁(IS): 表示事务打算对表中的单个行设置共享锁.
    • 意向排它锁(IX): 表示事务打算对表中的单个行设置排它锁.
  • 在获取意向锁时又如下的协议:
    • 在事务获得表中某一行的共享锁(S)之前,它必须首先获得该表上的IS锁或者更强的锁.
    • 在事务获得表中某一行的排它锁(X)之前,它必须首先获得该表上的IX锁.
  • 意向锁可以提高加锁的性能,在真正加锁之前不需要遍历表中的行是否加锁,只需要查看一下表中的意向锁即可.
  • 在请求锁过程中,如果将要请求的锁与现有锁兼容,则将锁授予请求的事务,如果与现有的锁冲突,则不会授予,事务将阻塞等待,直到冲突的锁别释放,意向锁与行级锁的兼容性如下表:

在这里插入图片描述

  • 除了全表锁定请求之外,意向锁不会阻止任何锁请求,意向锁的主要目的是表示事务正在锁定某行或者正在意图锁定某一行.

6.3.4 索引记录锁-record locks

  • 索引记录锁或者称为精准行锁,顾名思义是指索引记录上的锁,如下sql锁住的指定的一行:
# 防⽌任何其他事务插⼊、更新或删除值为1的⾏,id为索引列
SELECT * FROM account WHERE id = 1 For UPDATE;
  • 索引记录锁总是锁定索引行,在没有定义索引的情况之下,InnoDB创建一个隐藏的聚集索引,并使用该索引进行记录锁定,当使用索引进行查找时,锁定的只是满足条件的行,如图所示:

在这里插入图片描述

6.3.5 间隙锁-gap locks

  • 间隙锁锁定的时索引记录之间的间隙,或者第一个索引记录之前,再或者最后一个索引记录之后的间隙,如图所示的位置,根据不同的查询条件都可能会加间隙锁:
    在这里插入图片描述
  • 例如有如下的sql,锁定的时ID(10,20)之间的间隙,注意不包括10和20的行,目的是防止其他的事务将ID值为15的列插入account表中(无论是否已经存在要插入的数据列),因为指定的范围之间的间隙被锁定了.
SELECT * FROM account WHERE id BETWEEN 10 and 20 For UPDATE;
  • 间隙可以跨越单个或者是多个索引值
    在这里插入图片描述
  • 对于使用唯一索引查询到的唯一行,不使用间隙锁,如下语句,id列有唯一的索引,只对id值为100的行使用索引记录锁:
# 只使⽤Record Locks
SELECT * FROM account WHERE id = 100;
  • 如果查询的时候使用普通索引或者是非唯一的索引,以上的语句将锁定对应记录前面的间隙.
  • 不同事务的间隙锁可以共存,一个事务的间隙锁不会阻止另一个事务在相同的间隙上使用间隙锁,共享间隙锁和独占间隙锁之间没有区别.
  • 当事务隔离级别设置为Read commited时间间隙锁会被禁用,对于搜索和索引扫描不再使用间隙锁定.

6.3.6 临键锁-next key locks

  • next-key锁是索引记录锁和索引记录之前间隙上间隙锁的组合,如图所示:
    在这里插入图片描述
  • InnoDB搜索或扫描一个表的索引时,执行行级锁策略,具体方式是: 在扫描过程中遇到的索引记录上设置共享锁或排它锁,因此,行级锁策略实际上应用的时索引记录锁,索引记录上的next-key锁也会影响该索引记录之间的"间隙",也就是说,next-key锁是索引记录锁加上索引记录前面的间隙锁.
  • 假设索引包含值10,11,13和20,这些索引可能next-key锁覆盖的以下区间,其中圆括号表示不包含区间的端点,方括号表示包含端点(左开右闭):
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
  • 默认情况下,repeatable Read事务隔离级别开启next-key锁并进行搜索和索引扫描,可以防止幻象行,从而解决幻读问题,后面我们再分析.

6.3.7 插入意向锁-Insert Intention Locks

  • 插入意向锁是一个特殊的间隙锁,在向索引记录之前的间隙进行Insert操作插入数据时使用,如果多个事务向相同索引间隙中不同的位置插入记录,则不要彼此等待,假设已经存在值10和20的索引记录,两个事务分别尝试插入索引值为15和16的行,在获得插入行上的排它锁之前,每个事务都用插入意向锁锁住10到20之间的间隙,但不会相互阻塞,因为他们所操作的行并不冲突.

6.3.8 AUTO-INC Locks

AUTO-INC锁也叫自增锁是一个表级锁,服务于配置了AUTO_INCREMENT自增列的表,在插入数据时会在表上加上自增锁,并生成自增值,同时阻塞其他的事务操作,以保证值的唯一性,需要注意的是,当一个事务执行新的操作以生成自增值,但是事务回滚了,申请到的主键值不会回退,这意味着在表中会出现自增值不连续的情况.

6.3.9 死锁

6.3.9.1 什么是死锁
  • 由于每个事务都持有另一个事务所需的锁,导致事务无法继续进行的情况称为死锁,以下图为例,两个事务都不会主动释放自己持有的锁,并且都在等待对方持有的资源变得可用.
    在这里插入图片描述
6.3.9.2 死锁产生的条件
  • 互斥访问: 如果线程1获取到锁A,那么线程2就不能同时得到锁A.
  • 不可抢占:获取到锁的线程,只能自己主动释放锁,别的线程不能从他的手中抢占锁
  • 保持与请求:线程1已经获得了锁A,还要在这个基础上再去获了锁B
  • 循环等待:线程1等待线程2释放锁,线程2也等待线程1释放锁,死锁发⽣时系统中⼀定有由两个或两个以上的线程组成的⼀条环路,该环路中的每个线程都在等待着下⼀个进程释放锁

以上四条是造成死锁的必要条件,必须同时满⾜,所以如果想要打破死锁,可以破坏以上四个条件之⼀,最常⻅的⽅式就是打破循环等待

6.3.9.3 InnoDB对死锁的检测
  • InnoDB在运行时会对死锁进行检测,当死锁检测启用时(默认),InnoDB自动检测事务死锁,并回滚一个或者多个事务来打破锁,InnoDB尝试选择小事务来回滚,其中事务的大小由插入,更新或删除的行数决定.
  • 当超过200个事务等待锁资源或等待锁的个数超过1000000个时也会被视为死锁,并尝试将等待列表的事务回滚.
  • 在高并发系统中,多个线程等待相同的锁时,死锁检测的可能会导致性能变慢,此时禁用死锁检测依赖系统变量innodb_lock_wait_timeout设置进行事务回滚可能性能会更高,可以通过设置系统变量innodb_deadlock_detect[={OFF|ON}]禁用死锁检测.
6.3.9.4 如何避免死锁
  • MySQL是一个多线程程序,死锁的情况大概率会发生,但是他并不可怕,除非平凡出现,导致无法运行某些事务.
  • InnoDB使用自动行级锁,即使在值插入或删除单行的事务中,也可能出现死锁,这是因为插入或删除行并不是真正的"原子"操作,同时会对索引记录进行修改并设置锁.
  • 使用以下的技术来处理死锁并降低死锁发生的概率:
    • 使用事务而不是使用LOCK TABLES语句手动加锁,并使用innodb_lock_wait_timeout变量设置锁的超时时间,保证任何情况之下都可以自动释放.
    • 经常使用show engine innodb status命令来确定最近一次死锁的原因,这可以帮助我们修改程序以避免死锁.
    • 如果出现频繁的死锁警告,可以启用innodb_print_all_deadlocks变量来手机调试信息,对于死锁的信息,都记录在MySQL的错误日志中,调试完成之后记得禁用此选项.
    • 如果事务由于死锁而失败,记得重新发起事务,再执行一次
    • 尽量避免大事务,保持事务粒度小而且持续时间短,这样事务之间就不容易发生冲突,从而降低发生死锁的概率.
    • 修改完成之后立即提交事务,特别注意的是,不要在一个交互式会话中长时间打开一个未提交的事务.
    • 当事务中要修改多个表或者是同一个表中的不同行时,每次都要以一致的顺序执行这些操作,使事务中的修改操作形成定义良好的队列,可以避免死锁.而不是在不同的位置编写多个类似的Insert,update和delete语句.我们写的程序其实就是把一系列操作组织成一个方法或者函数.
    • 使用表级锁防止对表进行更新,可以避免死锁,但是代价是系统的并发性降低
    • 如果在查询是时加锁,比如select...for updateselect...for share,尝试使用较低的隔离级别,比如Read commited.

6.4 查看并设置隔离级别

  • 事务的隔离级别分为全局作用域和回话作用域,查看不同的隔离级别,可以使用以下的方式:
# 全局作⽤域
SELECT @@GLOBAL.transaction_isolation;
# 会话作⽤域
SELECT @@SESSION.transaction_isolation;
# 可以看到默认的事务隔离级别是REPEATABLE-READ(可重复读)
+---------------------------------+
| @@SESSION.transaction_isolation |
+---------------------------------+
| REPEATABLE-READ 				  | # 默认是可重复读
+---------------------------------+
1 row in set (0.00 sec)
  • 设置事务的隔离级别和访问模式,可以使用以下的语法:
# 通过GLOBAL|SESSION分别指定不同作⽤域的事务隔离级别
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level|access_mode;# 隔离级别
level: {REPEATABLE READ # 可重复读| READ COMMITTED # 读已提交| READ UNCOMMITTED # 读未提交| SERIALIZABLE # 串⾏化
}
# 访问模式
access_mode: {READ WRITE # 表⽰事务可以对数据进⾏读写| READ ONLY # 表⽰事务是只读,不能对数据进⾏读写
}
# ⽰例
# 设置全局事务隔离级别为串⾏化
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
# 设置会话事务隔离级别为串⾏化
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
# 如果不指定任何作⽤域,设置将在下⼀个事务开始⽣效
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
  • 通过选项文件指定事务的隔离级别,以便MySQL启动的时候读取并设置
[mysqld]
transaction-isolation = REPEATABLE-READ # 隔离级别为可重复读
transaction-read-only = OFF # 关闭只读意味着访问模式为读写
  • 也可以通过以set语法设置系统变量的方式来设置事务的隔离级别
# ⽅式⼀
SET GLOBAL transaction_isolation = 'SERIALIZABLE';
# 注意使⽤SET语法时有空格要⽤"-"代替
SET SESSION transaction_isolation = 'REPEATABLE-READ'; 
# ⽅式⼆
SET @@GLOBAL.transaction_isolation='SERIALIZABLE';
# 注意使⽤SET语法时有空格要⽤"-"代替
SET @@SESSION.transaction_isolation='REPEATABLE-READ';

注意: 设置事务的隔离级别的时候,不能在已经开启的事务中执行,否则会报错.

6.5 READ UNCOMMITED 读未提交与脏读

6.5.1 实现方式

  • 读取时: 不加任何锁,直接读取版本链中的最新版本,也就是当前读,可能会出现脏读,不可重复读,幻读问题.
  • 更新时: 加共享锁(S锁),事务结束的时候释放,在数据修改完成之前,其他事务不能修改当前数据,但是可以被其他事务读取.

6.5.2 存在问题

事务的READ UNCOMMITTED隔离级别的不使用独占锁,所以并发性能很高,但是会出现大量的数据安全问题,比如在事务A中执行了一条Insert语句,在没有执行commit的情况下,会在事务B中被读取到,此时如果事务A执行回滚操作,那么事务B中读取到的事务A写入的数据将没有意义,我们把这个现象叫做"脏读".由于READ UNCOMMITTED读未提交会出现"脏读"现象,在正常的业务出现这种问题会产生非常严重的后果,所以正常情况下应该避免使用READ UNCOMMITTED读未提交这种隔离级别.

6.5.3 问题重现

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

6.6 READ COMMITTED 读已提交与不可重复读

6.6.1 实现方式

  • 读取时: 不加锁,但使用快照读,即按照MVCC机制读取符合ReadView要求的版本数据,每次查询都会构造一个新的ReadView,可以解决脏读,但无法解决不可重复读和幻读的问题.
  • 更新时: 加独占行锁(X锁),事务结束时释放,数据在修改完毕之前,其他事务不能修改也不能读取.

6.6.2 存在问题

为了解决脏读问题,可以把事务的隔离级别设置为READ COMMITTED,这时事务只能读到了其他事务提交之后的数据,但会出现不可重复读问题,比如事务A先对某条数据进行了查询,之后事务B对这条数据进行了修改,并且提交事务,事务A再对这条数据进行查询的时候,得到了事务B修改之后的结果,这就导致了事务A再同一个事中以相同的条件查询得到了不同的值,这个现象叫"不可重复读".

6.6.3 问题重现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.7 REPEATABLE READ 可重复读与幻读

6.7.1 实现方式

  • 读取时: 不加锁,页使用快照读,按照MVCC机制读取符合ReadView要求的版本数据,但无论事务中有几次查询,只会在首次查询生成一个ReadView,可以解决脏读,不可重复读,配合next-key行锁可以解决一部分幻读问题.
  • 更新时: 加next-key行锁,事务结束的时候释放,在一个范围内的数据修改完成之前,其他的事务不能对这个范围内的数据进行修改,插入和删除操作,同时页不能被查询.

6.7.2 存在问题

在SQL的标准定义中,事务的REPEATABLE READ隔离级别是会出现幻读问题的,但是在InnoDB中使用了Next-key行锁来解决大部分场景下的幻读问题,那么在不加next-key行锁的情况下会出现什么问题吗?
我们知道next-key锁,锁住的时当前索引记录以及索引记录前面的间隙,那么在不加next-key锁的情况下,也就是只对当前修改行加了独占行锁(X),这时记录前面的间隙没有被锁定,其他的事务就可以向这个间隙中插入记录,就会导致一个问题: 事务A查询了一个区间的记录得到结果集A,事务B向这个区间的间隙中写入了一条记录,事务A再查询这个区间的结果集时会查询事务B新吸入的记录得到结果集B,两次查询的结果集不一致,这个现象就是"幻读".

6.7.3 问题重现

由于REPEATABLE READ隔离级别默认使用了next-key锁,为了重现幻读问题,我们把隔离级别退回到更新时只加排它锁的READ COMMITTED
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.8 serializable 串行化

6.8.1 实现方式

  • 读取时: 加共享表锁,读取版本链中最新版本,事务结束时释放.
  • 更新时: 加独占表锁,事务结束时释放,完全串行操作,可以解决所有的事务问题.
  • 与前面不同,这里加的都是表锁,而前面加的都是行锁.

6.8.2 存在问题

所有的更新都是串行执行操作,效率极低.

6.9 不提供隔离级别的性能安全

在这里插入图片描述

6.10 多版本控制(MVCC)

上一个小节介绍了事务隔离的锁机制,但是频繁加锁与释放锁会对性能产生较大的影响,为了提高性能,InnoDB与锁结合,同时采用一种事务实现机制MVCC,即MultiVersioned Concurrency Control多版本并发控制,用来解决脏读,不可重复读等事务之间的读写问题(注意不能处理幻读问题),MVCC在某些场景中替代了低效的锁,在保证了隔离性的基础上,提升了读取效率和并发性.

6.10.1 实现原理

6.10.1.1 版本链
  • MVCC的实现是基于undo Log版本链和ReadView来完成的,版本链在所有事务中时共享的,所有的事务只要是访问数据的id相同,访问的都是同一条版本链,undo log作为回滚的基础,在执行update或者delete,会将每次操作的上一个版本记录在undo log中,每条undo log中都记录一个叫做roll_pointer的引用信息,通过roll_pointer就可以将某条数据对应的undo Log组织成一个undo链,在数据行的头部通过数据行中的roll_pointer与undo Log中的第一条日志进行关联,这样就构成了一条完整的数据版本链,如下如所示:
    在这里插入图片描述
  • 每一条被修改的记录都会有一条版本链,体现了这条记录的有所变更,当有事务对这条记录进行修改时,将修改后的数据链连接到版本的头部,如下图中的undo3:
    在这里插入图片描述
6.10.1.2 ReadView
  • 每条数据的版本链都构造好之后,在查询时具体选择哪个版本呢?这里就需要ReadView结构来实现了,所谓ReadView是一个内存结构,顾名思义是一个视图,在事务使用select查询数据时就会构造一个ReadView,里面记录了版本链的一些统计值,这样后续查询处理时就不用遍历所有版本链了,这些统计包括:

    • m_ids: 当前所有活跃事务的集合,即没有提交或者没有回滚的事务
    • m_low_limit_id: 活跃事务中最小的事务id,如果undo版本链中的事务id小于此值,说明该事物已经提交
    • m_up_limit_id: 下一个将被分配的事务id,也就是最大的事务id+1,即还没有创建的事务,对于当前事务来说是不可见的.
    • m_creator_trx_id: 创建当前的ReadView的事务id.
  • 构造好的ReadView之后需要根据一定的查询规则找到唯一可用的版本,这个查找规则比较简单,以下图的版本链为例,在m_creator_trx_id=201的事务执行select时,会构造一个ReadView同时对应的变量赋值.

    • m_ids: 活跃事务集合为[90,100,200]
    • m_low_limit_id: 活跃事务最小事务id=90
    • m_up_limit_id: 预分配事务id=202,最大事务id=预分配事务id-1=201
    • m_creator_trx_id: 当前创建ReadView的事务id=201
      在这里插入图片描述
  • 接下来找到版本链头,从链头开始遍历所有版本,根据四部查找规则,判断每个版本:

    • 第一步: 判断该版本是否为当前事务创建,若m_creator_trx_id等于该版本事务id,意味着读取自己修改的数据,可以直接访问,如果不等则到第二步.
    • 第二步: 若该版本事务id< m_up_limit_id(最小事务id),意味着该版本事务在ReadView生成之前就已经提交,可以直接访问,如果不是则到第三部
    • 第三步: 若该版本事务id>=m_low_limit_id(最大事务id),意味着该版本事务在ReadView生成之后才创建,所以坑定不能被当前事务访问,所以无需第四部判断,直接遍历下一个版本,如果不是则到第四部.
    • 第四步: 若该版本事务id在m_up_limit_id(最小事务id)和m_low_limit_id(最大事务id)之间,同时该版本不在活跃事务列表中,意味着创建ReadView时该版本已经提交,可以直接访问,如果不是则遍历并判断下一个版本.
      在这里插入图片描述

6.10.2 MVCC如何解决脏读与不可重复读问题

  • 首先幻读无法通过MVCC单独解决
  • ReadView解决脏读问题: 从版本链头遍历到版本链尾,找到首个符合要求的版本即可,就可以实现查询到的结果都是已经提交的事务数据,解决了脏读问题
    在这里插入图片描述
  • 对于不可重复读问题,在事务中的第一个查询时创建的一个ReadView,后续查询都是用这个ReadView进行判断,所以每次的查询结果都是一样的,从而解决不可重复读问题,在REPEATABLE READ可重复读,隔离级别下就是采用的这种方式.
  • 如果每次查询都创建一个新的ReadView,这样就会出现不可重复读问题,因为每次创建的ReadView的活跃事务id都不一样,所以导致查询版本链的时候每个ReadView查询到的第一个符合条件的数据版本也不一样,从而导致了不可重复读问题,在读已提交中就使用的是这种实现方式.在一个事务中可能会创建多个ReadView.

相关文章:

[MySQL数据库] 事务与锁

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…...

Rule.issuer(通过父路径配置loader处理器)

说明 正常在设置loader配置规则时&#xff0c;都是通过文件后缀来配置的 issuer的作用是可以通过父级的路径&#xff0c;设置生效的匹配规则 与rule的差别 test: 匹配当前模块的路径&#xff08;如 .css 文件&#xff09; issuer: 匹配引入当前模块的父模块的路径&#xff0…...

MyBatis 插件开发的完整详细例子

MyBatis 插件开发的完整详细例子 MyBatis 插件&#xff08;Interceptor&#xff09;允许开发者在已映射语句执行过程中的某一点进行拦截调用&#xff0c;从而实现自定义逻辑。以下是一个完整的 MyBatis 插件开发示例&#xff0c;涵盖所有使用场景&#xff0c;并附有详细注释和总…...

树状数组底层逻辑探讨 / 模版代码-P3374-P3368

目录 功能 实现 Q:但是&#xff0c;c[x]左端点怎么确定呢&#xff1f; Q:那么为什么要以二进制为基础呢&#xff1f; Q:为什么是补码 - &#xff1f; 区间查询 树形态 性质1.对于x<y,要么c[x]和c[y]不交&#xff0c;要么c[x]包含于c[y] 性质2.c[x] 真包含 于c[x l…...

Eigen库入门

Eigen是一个C模板库&#xff0c;用于线性代数运算&#xff0c;包括矩阵、向量、数值求解和相关算法。它以其高性能、易用性和丰富的功能而闻名。 安装与配置 Eigen是一个纯头文件库&#xff0c;无需编译&#xff0c;只需包含头文件即可使用。 下载Eigen&#xff1a;从官方网站…...

力扣HOT100——102.二叉树层序遍历

给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]] /*** Definition for a bi…...

客户案例 | 光热+数智双驱动!恒基能脉的数字化协同与技术创新实践

光热先锋 智领未来 恒基能脉新能源科技有限公司: 创新驱动&#xff0c;智造光热未来行业领航者 恒基能脉新能源科技有限公司是一家立足于光热发电核心技术产品&#xff0c;专注于“光热” 多能互补项目的国家高新技术企业&#xff0c;其核心产品定日镜广泛应用于光热发电、储…...

第十六周蓝桥杯2025网络安全赛道

因为只会web&#xff0c;其他方向都没碰过&#xff0c;所以只出了4道 做出来的&#xff1a; ezEvtx 找到一个被移动的文件&#xff0c;疑似被入侵 提交flag{confidential.docx}成功解出 flag{confidential.docx} Flowzip 过滤器搜索flag找到flag flag{c6db63e6-6459-4e75-…...

构造函数有哪些种类?

构造函数用于对象的初始化。 1.默认构造函数&#xff1a;没有参数&#xff0c;执行默认的初始化操作&#xff1b; 2.参数化构造函数&#xff1a;传入参数的构造函数&#xff0c;允许构造函数初始化成员变量&#xff1b; 3.拷贝构造函数&#xff1a;将同一类型的实例化对象作…...

第十六届蓝桥杯大赛软件赛省赛 C/C++ 大学B组 [京津冀]

由于官方没有公布题目的数据, 所以代码仅供参考 1. 密密摆放 题目链接&#xff1a;P12337 [蓝桥杯 2025 省 AB/Python B 第二场] 密密摆放 - 洛谷 题目描述 小蓝有一个大箱子&#xff0c;内部的长宽高分别是 200、250、240&#xff08;单位&#xff1a;毫米&#xff09;&…...

关于调度策略的系统性解析与物流机器人应用实践

关于调度策略的系统性解析与物流机器人应用实践 一、调度策略的定义与核心目标 调度策略是用于在复杂环境中协调资源分配、任务排序及路径规划的决策框架,旨在通过优化资源利用率和任务执行效率,实现系统整体性能的最优解。其核心目标包括: 动态适应性:应对实时变化(如订…...

探索具身智能协作机器人:技术、应用与未来

具身智能协作机器人&#xff1a;概念与特点 具身智能协作机器人&#xff0c;简单来说&#xff0c;就是将人工智能技术与机器人实体相结合&#xff0c;使其能够在与人类共享的空间中进行安全、高效协作的智能设备。它打破了传统机器人只能在预设环境中执行固定任务的局限&#…...

毕业项目-Web入侵检测系统

1. 项目简介 系统主要分为两大板块&#xff1a;靶标站点和入侵检测系统。靶标站点是系统的被监测对象&#xff0c;而入侵检测系统则是用于检测靶标站点的流量是否存在异常&#xff0c;以及在检测到异常时进行告警。 入侵检测系统的实现过程简述如下&#xff1a; 数据获取与分…...

【分布式系统中的“瑞士军刀”_ Zookeeper】二、Zookeeper 核心功能深度剖析与技术实现细节

在分布式系统的复杂生态中&#xff0c;Zookeeper 凭借其强大的核心功能&#xff0c;成为保障系统稳定运行的关键组件。上篇文章我们了解了 Zookeeper 的基础概念与安装配置&#xff0c;本文将继续深入剖析 Zookeeper 的核心功能&#xff0c;包括分布式锁、配置管理、命名服务和…...

前端学习笔记(四)自定义组件控制自己的css

1、前言及背景 自己写的一个组件有至少3个页面在使用&#xff0c;组件中的部分文字颜色需要统一修改需要根据一个状态字段来显示不同颜色且不希望受父组件影响 注意&#xff1a;博主学习vue截止目前也就半年&#xff0c;如有知识错误之处还请指出不胜感激&#xff0c;祝学习开…...

从描述语言,非功能性需求,需求和架构的一致性三个方面,说明软件需求到架构的映射存在哪些难点

软件需求到架构的映射是软件工程中的关键环节&#xff0c;其难点主要体现在描述语言差异、非功能性需求的复杂性以及需求与架构的一致性维护三个方面。以下是具体分析&#xff1a; 1. 描述语言的差异 难点&#xff1a;需求与架构使用不同的抽象语言描述&#xff0c;导致语义鸿…...

linux blueZ 第五篇:高阶优化与性能调优——蓝牙吞吐、延迟与功耗全攻略

本篇面向已有实战经验的读者,深入探讨 Classic Bluetooth 与 BLE 在 BlueZ 平台上的性能优化和调优方法,包括连接参数、MTU 调整、PHY 选择、缓存管理、并发策略,以及 HCI 抓包、功耗测量与自动化基准测试,助你打造高吞吐、低延迟、超低功耗的蓝牙应用。 目录 为何要做性能…...

linux的例行性工作(at)

使用场景&#xff1a; 生活中&#xff0c;我们有太多场景需要使用到闹钟&#xff0c;比如早上 7 点起床&#xff0c;下午 4 点开会&#xff0c;晚上 8 购物&#xff0c;等等 在 Linux 系统里&#xff0c;我们同样也有类似的需求。比如我们想在凌晨 1 点将文件上传服务器&#…...

JVM考古现场(二十六):执剑人·降维打击的终极审判

楔子&#xff1a;二向箔的颤动——当修真文明遭遇降维打击 "警告&#xff01;老年代发生维度坍缩&#xff01;"我腰间悬挂的昆仑镜突然迸发幽蓝光芒&#xff0c;终南山巅的河图洛书大阵中&#xff0c;GC日志正以《奇门遁甲》的格局疯狂演化&#xff1a; // 降维打击…...

腾讯云物联网平台

文档&#xff1a;物联网开发平台 MQTT.fx 快速接入物联网开发平台_腾讯云...

Unity之基于MVC的UI框架-含案例

Unity之基于MVC的UI框架-含案例 使用案例&#xff1a;类《双人成行》3D动作益智冒险类双人控制游戏开发教程 资源地址&#xff1a;https://learn.u3d.cn/tutorial/3d-adventure-william-anna 一、MVC框架概览 本框架以MVC的方式搭建&#xff0c;以View视口的方式展现数据&am…...

【Token系列】01 | Token不是词:GPT如何切分语言的最小单元

文章目录 01 | Token不是词&#xff1a;GPT如何切分语言的最小单元&#xff1f;一、什么是 Token&#xff1f;二、Token 是怎么来的&#xff1f;——BPE算法原理BPE核心步骤&#xff1a; 三、为什么不直接用词或字符&#xff1f;四、Token切分的实际影响五、中文Token的特殊性六…...

C++学习之路,从0到精通的征途:List类的模拟实现

目录 一.list的介绍 二.list的接口实现 1.结点 2.list结构 3.迭代器 &#xff08;1&#xff09;begin &#xff08;2&#xff09;end 4.修改 &#xff08;1&#xff09;insert &#xff08;2&#xff09;push_back &#xff08;3&#xff09;push_front &#xff0…...

Java大师成长计划之第4天:Java中的泛型

&#x1f4e2; 友情提示&#xff1a; 本文由银河易创AI&#xff08;https://ai.eaigx.com&#xff09;平台gpt-4o-mini模型辅助创作完成&#xff0c;旨在提供灵感参考与技术分享&#xff0c;文中关键数据、代码与结论建议通过官方渠道验证。 在现代软件开发中&#xff0c;类型安…...

计算机学报 2024年 区块链论文 录用汇总 附pdf下载

计算机学报 Year&#xff1a;2024 1 Title: 区块链中的公钥密码&#xff1a;设计、分析、密评与展望 Authors: Key words: 区块链&#xff1b;公钥密码算法&#xff1b;算法设计&#xff1b;复杂性分析&#xff1b;密评 Abstract: 比特币的成功&#xff0c;吸引了人们研…...

【Castle-X机器人】三、紫外消杀模块安装与调试

持续更新。。。。。。。。。。。。。。。 【Castle-X机器人】紫外消杀模块安装与调试 三、紫外消杀模块安装与调试2.1 安装2.2 调试2.2.1 紫外消杀模块话题2.2.2 测试 三、紫外消杀模块安装与调试 2.1 安装 使用相应工具将紫外消杀模块固定在Castle-X机器人底盘 2.2 调试 2.2…...

精益数据分析(29/126):深入剖析电子商务商业模式

精益数据分析&#xff08;29/126&#xff09;&#xff1a;深入剖析电子商务商业模式 在创业和数据分析的学习道路上&#xff0c;我们始终在探索如何更精准地把握商业规律&#xff0c;提升业务的竞争力。今天&#xff0c;我们依旧怀揣着共同进步的愿望&#xff0c;深入解读《精…...

AI图像编辑器 Luminar Neo 便携版 Win1.24.0.14794

如果你对图像编辑有兴趣&#xff0c;但又不想花费太多时间学习复杂的软件操作&#xff0c;那么 Luminar Neo 可能就是你要找的完美工具。作为一款基于AI技术的创意图像编辑器&#xff0c;Luminar Neo简化了复杂的编辑流程&#xff0c;即使是没有任何图像处理经验的新手&#xf…...

在Mybatis中为什么要同时指定扫描mapper接口和 mapper.xml 文件,理论单独扫描 xml 文件就可以啊

设计考虑因素 历史兼容性&#xff1a; MyBatis早期版本主要依赖XML配置&#xff0c;后来才引入接口绑定方式同时支持两种方式可以保证向后兼容 明确性&#xff1a; 显式指定两种路径可以使映射关系更加明确减少因命名不一致导致的潜在问题 性能考虑&#xff1a; 同时扫描可…...

MyBatis XML 配置完整示例(含所有核心配置项)

MyBatis XML 配置完整示例&#xff08;含所有核心配置项&#xff09; 1. 完整 mybatis-config.xml 配置文件 <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""htt…...

【数据结构入门训练DAY-24】美国大选

文章目录 前言一、题目二、解题思路结语 前言 本次训练内容 训练STL中sort的操作方法。训练解题思维。 一、题目 美国大选是按各州的投票结果来确定最终的结果的&#xff0c;如果得到超过一半的州的支 持就可以当选&#xff0c;而每个州的投票结果又是由该州选民投票产生…...

stm32L4R5ZI Nucleo-144 GPIO点灯及按键中断

文章目录 前言一、CubeMx环境配置二、GPIO点灯操作三、按键中断点灯总结 前言 STM32L4R5ZI Nucleo-144是STMicroelectronics推出的一款基于ARM Cortex-M4内核的微控制器芯片。它是STM32L4系列中的一员&#xff0c;针对低功耗和高性能的应用而设计。以下是一些关键特点和技术规格…...

访问Redis时遇到 unknown command ‘FT.INFO‘, with args beginning with 错误的解决方案

在B站学习 图灵程序员-诸葛 的LangChain入门课程《基于Redis保存历史聊天信息》。在实践代码时遇到了一些问题&#xff0c;在这里记录一下&#xff0c;希望能帮助到也在学习的同学。话不多说&#xff0c;上代码&#xff0c;这段代码使用langchain_redis来持久化大模型对话的聊天…...

Swift与iOS内存管理机制深度剖析

前言 内存管理是每一位 iOS 开发者都绕不开的话题。虽然 Swift 的 ARC&#xff08;自动引用计数&#xff09;极大简化了开发者的工作&#xff0c;但只有深入理解其底层实现&#xff0c;才能写出高效、健壮的代码&#xff0c;避免各种隐蔽的内存问题。本文将从底层原理出发&…...

数据库系统概论(五)关系模型的数据结构及形式化

数据库系统概论&#xff08;五&#xff09;关系模型的数据结构及形式化 前言一、关系&#xff1a;从“表格”说起1.1 关系数据模型中的“关系”是什么&#xff1f;1.2 域&#xff08;Domain&#xff09;&#xff1a;数据的“类型限定”1.3 笛卡尔积&#xff08;Cartesian Produ…...

Python类和对象四(十三)

魔法方法&#xff1a; 按位运算 按位于运算 只要相同才是1 或运算&#xff1a; 只要某个位是1结果就是1 、 按位非 将结果取反 按位异或&#xff1a; 左移和右移运算符&#xff1a; 右移两位 右移动n位&#xff0c;就是除以2的n次方 左移两位&#xff1a; 左移n位就是乘…...

Go 1.24 中的弱指针包 weak 使用介绍

在 Go 语言中&#xff0c;“弱指针”指的是不会阻止垃圾回收器&#xff08;GC&#xff09;回收目标对象的引用。 当一个对象只剩弱指针指向它&#xff0c;而没有任何强引用时&#xff0c;GC 仍会把该对象当作不可达对象并回收&#xff1b;随后&#xff0c;所有指向它的弱指针会…...

毕业项目-基于深度学习的入侵检测系统

选题背景与意义 随着互联网技术的飞速发展&#xff0c;网络在各个领域中的作用日益重要。然而&#xff0c;伴随着技术的进步&#xff0c;网络安全问题也愈加严峻&#xff0c;网络攻击事件频繁发生&#xff0c;给个人、企业乃至国家带来了巨大的经济损失与安全威胁。入侵检测系…...

Github 2025-04-26 Rust开源项目日报Top10

根据Github Trendings的统计,今日(2025-04-26统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10Dart项目1RustDesk: 用Rust编写的开源远程桌面软件 创建周期:1218 天开发语言:Rust, Dart协议类型:GNU Affero General Public Li…...

【Hive入门】Hive分桶表深度解析:从哈希分桶到Join优化的完整指南

目录 引言 1 分桶表基础概念 1.1 什么是分桶表 1.2 分桶与分区的区别 2 分桶表设计与创建 2.1 创建分桶表语法 2.2 分桶键选择原则 2.3 桶数确定策略 3 分桶表数据加载 3.1 标准数据加载流程 3.2 分桶表数据验证 4 分桶Join优化原理 4.1 Map端Join优化 4.2 Sort-…...

django之优化分页功能(利用参数共存及封装来实现)

优化分页功能 目录 1.封装分页代码 2.解决分页时覆盖搜索参数的bug 3.优化分页功能 上一篇文章我们讲到了搜索功能和分页展示数据功能。那这篇文章, 在上篇文章的基础上, 会去优化这些功能并解决搜索功能和分页功能不能一起使用的bug。 一、封装分页代码 原本我们的asse…...

linux blueZ 第四篇:BLE GATT 编程与自动化——Python 与 C/C++ 实战

本篇聚焦 BLE(Bluetooth Low Energy)GATT 协议层的编程与自动化实践,涵盖 GATT 基础、DBus API 原理、Python(dbus-next/bleak)示例、C/C++ (BlueZ GATT API)示例,以及自动发现、读写特征、订阅通知、安全配对与脚本化测试。 目录 BLE GATT 基础概念 BlueZ DBus GATT 模…...

前端面试每日三题 - Day 16

这是我为准备前端/全栈开发工程师面试整理的第16天每日三题练习&#xff0c;涵盖事件循环深入解析 、Vue3 响应式系统原理 &#xff0c;以及 多租户系统设计实践。每道题附带 详细解析、示例代码与脑图建议&#xff0c;助你全面掌握底层原理与架构设计思维。 ✅ 题目 1&#xf…...

使用PyTorch实现简单图像识别(基于MNIST手写数字数据集)的完整代码示例,包含数据加载、模型定义、训练和预测全流程

以下是一个使用PyTorch实现简单图像识别&#xff08;基于MNIST手写数字数据集&#xff09;的完整代码示例&#xff0c;包含数据加载、模型定义、训练和预测全流程&#xff1a; import torch import torch.nn as nn import torch.optim as optim import torchvision import torc…...

【Android】四大组件之Activity

目录 一、什么是Activity 二、如何创建和配置Activity 三、Activity 跳转与数据传递 四、数据保存与恢复 五、Activity 启动模式 六、自定义返回行为 七、复杂界面布局 你可以把Activity想象成手机屏幕上的一个“页面”。比如&#xff0c;当你打开一个App时&#xff0c;…...

数据库原理(1)

第一章 概论 一、基本概念 数据&#xff08;Data&#xff09;是数据库中存储的基本对象&#xff0c;描述事物的符号记录。例如学生的学号、姓名等信息都是数据。 数据库&#xff08;Database&#xff0c;DB&#xff09;长期存储在计算机内、有组织的、可共享的大量数据的集合。…...

SQL盲注问题深度解析与防范策略

引言 在当今互联网时代,Web应用程序的安全性是重中之重。SQL注入作为一种常见且极具威胁性的攻击手段,而其中的SQL盲注更是因其隐蔽性强、难以察觉而备受关注。攻击者借助SQL盲注,在无法直接获取数据库返回结果的情况下,通过精心构造特殊的SQL语句,利用页面的不同响应来逐…...

Android JIT( ART即时编译器),Just In Time Compiler,即时编译技术

Android JIT&#xff08; ART即时编译器&#xff09;,Just In Time Compiler&#xff0c;即时编译技术 Android Runtime (ART) 包含一个具备代码分析功能的即时 (JIT) 编译器&#xff0c;该编译器可以在 Android 应用运行时持续提高其性能。JIT是Just In Time Compiler&#xf…...

当自动驾驶遇上“安全驾校”:NVIDIA如何用技术给无人驾驶赋能?

自动驾驶技术的商业化落地&#xff0c;核心在于能否通过严苛的安全验证。国内的汽车企业其实也在做自动驾驶&#xff0c;但是吧&#xff0c;基本都在L2级别。换句话说就是在应急时刻内&#xff0c;还是需要人来辅助驾驶&#xff0c;AI驾驶只是决策层&#xff0c;并不能完全掌握…...

Unity中数据储存

在Unity项目开发中,会有很多数据,有需要保存到本地的数据,也有直接保存在缓存中的临时数据,一般为了方便整个项目框架中各个地方能调用需要的数据,因此都会实现一个数据工具或者叫数据管理类,用来管理项目中所有的数据。 首先保存在缓存中的数据,比如用户信息,我们只需…...