第15章_锁: (表级锁、页级锁、行锁、悲观锁、乐观锁、全局锁、死锁)

news2024/11/24 14:39:15

3.2 从数据操作的粒度划分:表级锁、页级锁、行锁

为了提高数据库并发度,每次锁定的数据范围越小越好,理论上每次只锁定当前操作的数据的方案会得到最大的并发度,但管理锁是很耗资源(涉及获取、检查、释放锁等动作)。因此数据库系统需要在高并发响应系统性能两方面进行平衡,这样就产生了“锁粒度(Lock granularity)”的概念。
对一条记录加锁影响的也只是这条记录而已,我们就说这个锁的粒度比较细;其实一个事务也可以在表级别进行加锁,自然就被称之为表级锁或者表锁,对一个表加锁影响整个表中的记录,我们就说这个锁的粒度比较粗。锁的粒度主要分为表级锁、页级锁和行锁。

1. 表锁(Table Lock

该锁会锁定整张表,它是MysQL中最基本的锁策略,并不依赖于存储引擎(不管你是MysQL的什么存储引擎对于表锁的策略都是一样的),并且表锁是开销最小的策略(因为粒度比较大)。由于表级锁一次会将整个表锁定,所以可以很好的避免死锁问题。当然,锁的粒度大所带来最大的负面影响就是出现锁资源争用的概率也会最高,导致并发率大打折扣

  ① 表级别的S锁、X锁 

在对某个表执行 SELECT INSERT DELETE UPDATE 语句时, InnoDB 存储引擎是不会为这个表添加表级别的 S 或者 X 的。在对某个表执行一些诸如 alter table drop table  这类的 DDL 语句时,其他事务对这个表并发执行诸如select insert delete update的语句会发生阻塞。同理,某个事务中对某个表执行select insert delete update 语句时,在其他会话中对这个表执行 DDL 语句也会发生阻塞。这个过程其实是通过在 server 使用一种称之为 元数据锁 (英文名: Metadata Locks ,简称 MDL )结构来实现的(总之就是宁可用元数据锁也不用表级别的S 锁、X锁)。
innodb只会在一些特殊情况下,比方说 崩溃恢复 过程中用到。在系统变量 autocommit=0 innodb_table_locks = 1 时, 手动 获取InnoDB存储引擎提供的表 t S 或者 X 可以这么写:
lock tables t read  InnoDB 会对表 t 加表级别的 S 锁 
lock tables t write  InnoDB 会对表 t 加表级别的 X 锁 
不过尽量避免在使用 InnoDB 存储引擎的表上使用 LOCK TABLES 这样的手动锁表语句,它们并不会提供什么额外的保护,只是会降低并发能力而已。InnoDB 的厉害之处还是实现了更细粒度的 行锁 ,关于 InnoDB表级别的 S X 大家了解一下就可以了。
总结 : 
MyISAM 在执行查询语句(select)前, 会给涉及的所有表加读锁, 在执行增删改操作前, 会给涉及的表加写锁, InnoDB 存储引擎是不会为这个表添加表级锁的 读锁 写锁 的(因为InnoDB实现了行锁)

  ② 意向锁 (intention lock

InnoDB 支持 多粒度锁( multiple granularity locking ,它允许 行级锁 表级锁 共存,而 意向
就是其中的一种 表锁
1. 意向锁的存在是为了协调行锁和表锁的关系, 支持多粒度(表锁与行锁)的锁并存
2. 意向锁是一种不与行级锁冲突的表级锁, 这点非常重要
3. 表明某个事务正在某些行持有了锁或该事务准备去持有锁 
意向锁分为两种:
     ① 意向共享锁 intention shared lock, IS ):事务有意向对表中的某些行加 共享锁 S 锁)
-- 事务要获取某些行的 S 锁,必须先获得表的 IS 锁。
SELECT column FROM table ... LOCK IN SHARE MODE;

     ②意向排他锁intention exclusive lock, IX):事务有意向对表中的某些行加排他锁X锁)

-- 事务要获取某些行的 X 锁,必须先获得表的 IX 锁。
SELECT column FROM table ... FOR UPDATE;

即:意向锁是由存储引擎 自己维护的 ,用户无法手动操作意向锁,在为数据行加共享 / 排他锁之前, InooDB 会先获取该数据行 所在数据表的对应意向锁

1. 意向锁要解决的问题
现在有两个事务,分别是T1和T2,其中T2试图在该表级别上应用共享或排它锁,如果没有意向锁存在,那么T2就需要去检查各个页或行是否存在锁;如果存在意向锁,那么此时就会受到由T1控制的 表级别意向锁的阻塞 。T2在锁定该表前不必检查各个页或行锁,而只需检查表上的意向锁。简单来说就是给更大一级别的空间示意里面是否已经上过锁。

在数据表的场景中, 如果我们给某一行数据加上了排它锁,数据库会自动给更大一级的空间,比如数据页或数据表加上意向锁,告诉其他人这个数据页或数据表已经有人上过排它锁了 ,这样当其他人想要获取数据表排它锁的时候,只需要了解是否有人已经获取了这个数据表的意向排他锁即可。
 
如果事务想要获得数据表中某些记录的共享锁,就需要在数据表上添加 意向共享锁
如果事务想要获得数据表中某些记录的排他锁,就需要在数据表上添加 意向排他锁
这时,意向锁会告诉其他事务已经有人锁定了表中的某些记录。
举例:
 
因为共享锁与排他锁互斥,所以事务B在试图对teacher表加共享锁的时候,必须保证两个条件。
(1)当前没有其他事务持有teacher表的排他锁
(2)当前没有其他事务持有teacher表中任意一行的排他锁。

为了检测是否满足第二个条件,事务B必须在确保teacher表不存在任何排他锁的前提下,去检测表中的每一行是否存在排他锁。 很明显这是一个效率很差的做法,但是有了意向锁之后,情况就不一样了。
意向锁是怎么解决这个问题的呢?首先,我们需要知道意向锁之间的兼容互斥性,如下所示。

意向锁之间是互相兼容的(虽然是表级的,但描述的是行级上锁情况),虽然意向锁和自家兄弟互相兼容,但是它会与普通的排他/共享锁互斥。
注意这里的排他/共享锁指的都是表锁, 意向锁不会与行级的共享/排他锁互斥。回到刚才teacher 表的例子。
 
意向锁的并发性

意向锁不会与行级的共享 / 排他锁互斥!正因为如此,意向锁并不会影响到多个事务对不同数据行加排 他锁时的并发性。(不然我们直接用普通的表锁就行了)

我们扩展一下上面 teacher 表的例子来概括一下意向锁的作用(一条数据从被锁定到被释放的过程中,可 能存在多种不同锁,但是这里我们只着重表现意向锁)。
从上面的案例可以得到如下结论:
1. InnoDB 支持 多粒度锁 ,特定场景下,行级锁可以与表级锁共存
2. 意向锁之间互不排斥,但除了 IS S 兼容外, 意向锁会与 共享锁 / 排他锁 互斥
3. IX IS 是表级锁,不会和行级的 X S 锁发生冲突。只会和表级的 X S 发生冲突。
4. 意向锁在保证并发性的前提下,实现了 行锁和表锁共存 满足事务隔离性 的要求。

③ 元数据锁(MDL锁) 

MySQL5.5 引入了 meta data lock ,简称 MDL 锁,属于表锁范畴。 MDL 的作用是,保证读写的正确性。比 如,如果一个查询正在遍历一个表中的数据,而执行期间另一个线程对这个 表结构做变更 ,增加了一列,那么查询线程拿到的结果跟表结构对不上,肯定是不行的。
因此, 当对一个表做增删改查操作的时候,加 MDL 读锁;当要对表做结构变更操作的时候,加 MDL 锁。
读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性,解决了DML和DDL操作之间的一致性问题。 不需要显式使用 ,在访问一个表的时候会被自动加上。

2. InnoDB中的行锁 

行锁(Row Lock)也称为记录锁,顾名思义,就是锁住某一行(某条记录row)。需要的注意的是,MySQL服务器层并没有实现行锁机制,行级锁只在存储引擎层实现。
优点:锁定力度小,发生锁冲突概率低,可以实现的并发度高。

缺点:对于锁的开销比较大,加锁会比较慢,容易出现死锁情况。


InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION);二是采用了行级锁。

先建立student表

① 记录锁(Record Locks

记录锁也就是仅仅把一条记录锁上,官方的类型名称为: LOCK_REC_NOT_GAP 。比如我们把 id 值为 8 的那条记录加一个记录锁的示意图如图所示。仅仅是锁住了id 值为8的记录,对周围的数据没有影响。
举例如下:
记录锁是有 S 锁和 X 锁之分的,称之为 S 型记录锁 X 型记录锁
  • 当一个事务获取了一条记录的S型记录锁后,其他事务也可以继续获取该记录的S型记录锁,但不可 以继续获取X型记录锁;
  • 当一个事务获取了一条记录的X型记录锁后,其他事务既不可以继续获取该记录的S型记录锁,也不可以继续获取X型记录锁。

② 间隙锁(Gap Locks

MySQL REPEATABLE READ 隔离级别下是可以解决幻读问题的,解决方案有两种,可以使用 MVCC 方案解决,也可以采用 加锁 方案解决。但是在使用加锁方案解决时有个大问题,就是事务在第一次执行读取操作时,那些幻影记录尚不存在,我们无法给这些 幻影记录 加上 记录锁 InnoDB 提出了一种称之为 Gap Locks 的锁,官方的类型名称为: LOCK_GAP ,我们可以简称为 gap 。比如,把 id 值为 8 的那条记录加一个gap 锁的示意图如下。

 

图中 id 值为 8 的记录加了 gap 锁,意味着 不允许别的事务在 id 值为 8 的记录前边的间隙插入新记录 ,其实就是id列的值 (3, 8) 这个区间的新记录是不允许立即插入的。比如,有另外一个事务再想插入一条 id 值为 4 的新记录,它定位到该条新记录的下一条记录的id 值为 8 ,而这条记录上又有一个 gap 锁,所以就会阻塞插入操作,直到拥有这个gap 锁的事务提交了之后, id 列的值在区间 (3, 8) 中的新记录才可以被插入。
gap 锁的提出仅仅是为了防止插入幻影记录而提出的
间隙锁的引入,可能会导致同样的语句锁住更大的范围, 这其实是影响了并发度的, 下面的例子会产生死锁
  1. session 1 执行select ... for update 语句, 由于id = 5 这一行并不存在, 因此会加上间隙锁(3,8) 
  2. session 2 执行select ... for update 语句, 同样加上间隙锁(3,8), 间隙锁之间不会冲突, 因此这个语句可以执行成功
  3. session 2 试图插入一行, 被session 1 的间隙锁挡住,进入等待
  4. session 1 视图插入一行, 被session 2 的间隙锁挡住, 两个session进入死锁

③ 临键锁(Next-Key Locks

记录锁 + 间隙锁

④ 插入意向锁(Insert Intention Locks

我们说一个事务在 插入 一条记录时需要判断一下插入位置是不是被别的事务加了 gap next - key 也包含 gap ),如果有的话,插入操作需要等待,直到拥有 gap 的那个事务提交。但是 InnoDB 定事务在等待的时候也需要在内存中生成一个锁结构 ,表明有事务想在某个 间隙 插入 新记录,但是 现在在等待。InnoDB 就把这种类型的锁命名为 Insert Intention Locks ,官方的类型名称为: LOCK_INSERT_INTENTION ,我们称为 插入意向锁 。插入意向锁是一种 Gap ,不是意向锁,在 insert操作时产生。

插入意向锁是在插入一条记录行前,由INSERT操作产生的一种间隙锁。该锁用以表示插入意向,
当多个事务在同一区间(gap)插入位置不同的多条数据时,事务之间不需要互相等待。假设存在两条值分别为4和7的记录,两个不同的事务分别试图插入值为5和6的两条记录,每个事务在获取插入行上独占的(排他)锁前,都会获取(4,7)之间的间隙锁,但是因为数据行之间并
不冲突,所以两个事务之间并不会产生冲突(阻塞等待)。
总结来说,插入意向锁的特性可以分成两部分:

  1. 插入意向锁是一种特殊的间隙锁—―间隙锁可以锁定开区间内的部分记录。
  2. 插入意向锁之间互不排斥,所以即使多个事务在同一区间插入多条记录,只要记录本身(主键、唯一索引)不冲突,那么事务之间就不会出现冲突等待。

注意,虽然插入意向锁中含有意向锁三个字,但是它并不属于意向锁而属于间隙锁,因为意向锁是表锁而插入意向锁是行锁
比如,把id值为8的那条记录加一个插入意向锁的示意图如下:
比如, 现在T1为id值为8 的记录加了一个gap锁, 然后T2 和 T3 分别想向student表中插入id值分别为4,5的两条记录, 所以现在为id值为8的记录加的锁的示意图就如下所示:

从图中可以看到,由于T1持有gap锁,所以T2和T3需要生成一个插入意向锁的锁结构并且处于等待状态。当T1提交后会把它获取到的锁都释放掉,这样T2和T3就能获取到对应的插入意向锁了(本质上就是把插入意向锁对应锁结构的is_waiting属性改为false),T2和T3之间也并不会相互阻塞,它们可以同时获取到id值为8的插入意向锁,然后执行插入操作。事实上插入意向锁并不会阻止别的事务继续获取该记录上任何类型的锁。

3.3 从对待锁的态度划分:乐观锁、悲观锁

从对待锁的态度来看锁的话,可以将锁分成乐观锁和悲观锁,从名字中也可以看出这两种锁是两种看待
数据并发的思维方式 。需要注意的是,乐观锁和悲观锁并不是锁,而是锁的 设计思想

1. 悲观锁(Pessimistic Locking

悲观锁是一种思想,顾名思义,就是很悲观,对数据被其他事务的修改持保守态度,会通过数据库自身的锁机制来实现,从而保证数据操作的排它性。
悲观锁总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会 阻塞 直到它拿到锁( 共享资源每次只给一个线程使用,其它线程阻塞, 用完后再把资源转让给其它线程 )。比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁,当其他线程想要访问数据时,都需要阻塞挂起。Java synchronized ReentrantLock 等独占锁就是悲观锁思想的实现。

2. 乐观锁(Optimistic Locking

乐观锁认为对同一数据的并发操作不会总发生,属于小概率事件,不用每次都对数据上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,也就是 不采用数据库自身的锁机制,而是通过 程序来实现 。在程序上,我们可以采用 版本号机制 或者 CAS 机制 实现。 乐观锁适用于多读的应用类型, 这样可以提高吞吐量 。在 Java java.util.concurrent.atomic 包下的原子变量类就是使用了乐观锁 的一种实现方式: CAS 实现的。

1. 乐观锁的版本号机制

在表中设计一个 版本字段 version ,第一次读的时候,会获取 version 字段的取值。然后对数据进行更新或删除操作时,会执行 UPDATE ... SET version=version+1 WHERE version=version 。此时如果已经有事务对这条数据进行了更改,修改就不会成功。

2. 乐观锁的时间戳机制

时间戳和版本号机制一样,也是在更新提交的时候,将当前数据的时间戳和更新之前取得的时间戳进行比较,如果两者一致则更新成功,否则就是版本冲突。
你能看到乐观锁就是程序员自己控制数据并发操作的权限,基本是通过给数据行增加一个戳(版本号或者时间戳),从而证明当前拿到的数据是否最新。

3. 两种锁的适用场景

从这两种锁的设计思想中,我们总结一下乐观锁和悲观锁的适用场景:
  1. 乐观锁 适合 读操作多 的场景,相对来说写的操作比较少。它的优点在于 程序实现 不存在死锁 问题,不过适用场景也会相对乐观,因为它阻止不了除了程序以外的数据库操作。
  2. 悲观锁 适合 写操作多 的场景,因为写的操作具有 排它性 。采用悲观锁的方式,可以在数据库层面阻止其他事务对该数据的操作权限,防止 - - 的冲突。

3.4 其它锁之:全局锁

全局锁就是对 整个数据库实例 加锁。当你需要让整个库处于 只读状态 的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。全局锁的典型使用 场景 是:做 全库逻辑备份
全局锁的命令:

Flush tables with read lock

3.5 其它锁之:死锁

1. 概念

死锁是指两个或多个事务都持有对方需要的锁, 并且在等待对方释放, 并且双方都不会释放自己的锁。
死锁示例:

  

这时候,事务1在等待事务2释放id=2的行锁,而事务2在等待事务1释放id=1的行锁。 事务1和事务2在互相等待对方的资源释放,就是进入了死锁状态。当出现死锁以后,有 两种策略

  • 一种策略是,直接进入等待,直到超时。这个超时时间可以通过参数 innodb_lock_wait_timeout 来设置。
  • 另一种策略是,发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务(将持有最少行级排他锁的事务进行回滚),让其他事务得以继续执行。将参数 innodb_deadlock_detect 设置为 on ,表示开启这个逻辑。

2. 产生死锁的必要条件

  1. 两个或者两个以上事务
  2. 每个事务都已经持有锁并且申请新的锁
  3. 锁资源同时只能被同一个事务持有或者不兼容
  4. 事务之间因为持有锁和申请锁导致彼此循环等待

死锁的关键在于:两个(或以上)的Session加锁的顺序不一致。

 3.如何处理死锁

  • 方式1:等待,直到超时(innodb_lock_wait_timeout=50s)。

即当两个事务互相等待时,当一个事务等待时间超过设置的阈值时,就将其回滚,另外事务继续进行。这种方法简单有效,在innodb中,参数innodb_lock_wait_timeout用来设置超时时间。
缺点:对于在线服务来说,这个等待时间往往是无法接受的。
那将此值修改短一些,比如1s,0.1s是否合适?不合适,容易误伤到普通的锁等待。

  • 方式2:使用死锁检测进行死锁处理

方式1检测死锁太过被动,innodb还提供了wait-for graph算法来主动进行死锁检测,每当加锁请求无法立即满足需要并进入等待时,wait-for graph算法都会被触发。

      

基于这两个信息, 可以绘制wait-for graph(等待图)

                                 

死锁检测的原理是构建一个以事务为顶点, 锁为边的有向图,判断有向图是否存在环, 存在即有锁

一旦检测到回路、有死锁,这时候InnoDB存储引擎会选择回滚undo量最小的事务(将持有最少行级排他锁的事务进行回滚),让其他事务继续执行(innodb_deadlock_detect=on `表示开启这个逻辑)。
缺点:每个新的被阻塞的线程,都要判断是不是由于自己的加入导致了死锁,这个操作时间复杂度是o(n)。如果100个并发线程同时更新同一行,意味着要检测100*100 = 1万次,1万个线程就会有1千万次检测。


如何解决?

  • 方式1:关闭死锁检测,但意味着可能会出现大量的超时,会导致业务有损。
  • 方式2:控制并发访问的数量。比如在中间件中实现对于相同行的更新,在进入引擎之前排队,这样在InnoD内部就不会有大量的死锁检测工作。
     

进一步的思路:

可以考虑通过将一行改成逻辑上的多行来减少锁冲突. 比如, 连锁超市账户总额的记录, 可以考虑放到多条记录上, 账户总额等于这多个记录的值的总和.

4.如何避免死锁

  • 合理设计索引,使业务sQL尽可能通过索引定位更少的行,减少锁竞争。

  • 调整业务逻辑sQL执行顺序,避免update/delete长时间持有锁的sQL在事务前面。

  • 避免大事务,尽量将大事务拆成多个小事务来处理,小事务缩短锁定资源的时间,发生锁冲突的几率也更小。

  • 在并发比较高的系统中,不要显式加锁,特别是是在事务里显式加锁。如select ... for update语句,如果是在事务里运行了start transaction或设置了autocommit等于o,那么就会锁定所查找到的记录。

  • 降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为Rc,可以避免掉很多因为gap锁造成的死锁。
     

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/988616.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

我总结的《149个Python面试题.pdf》,都是干货!

大家好,我是涛哥。 很多小伙伴找Python面试资料,所以为了方便大家,涛哥我整理了《149个Python面试干货》,方便大家进行学习,尤其是要面试学习的同学可以重点学起来。 第一个部分就是讲Python基础相关内容 第二个部分…

JAVA毕业设计097—基于Java+Springboot+Vue+uniapp的医院挂号小程序系统(源码+数据库)

基于JavaSpringbootVueuniapp的医院挂号小程序系统(源码数据库)097 一、系统介绍 本系统前后端分离(网页端和小程序端都有) 本系统分为管理员、医院、用户三种角色(角色菜单可自行分配) 用户功能: 注册、登录、医院搜索、最新资讯、医生搜索、挂号预约、挂号记…

由于找不到msvcp120.dll无法继续执行代码,重新安装相关软件

在我们的生活中,计算机已经成为不可或缺的工具,我们依赖它来进行工作、学习和娱乐。然而,当我们在使用计算机时,有时会遭遇一些令人烦恼的问题,例如“找不到 msvcp120.dll 无法继续执行代码”的错误提示。这究竟是什么…

TGA格式文件转材质

今天淘宝上买了一个美女的模型,是blender的源文件,上面说有fbx格式的。我用unity,所以觉得应该可以用。文件内容如下图: FBX文件夹打开后,内容如下图所示,当时就预感到可能没有色彩。 unity打开后果然发现只…

Go 报错 Package libzmq was not found in the pkg-config search path.

make编译程序时,报错提示如下: 因为 zmq.h 是包含在开发包 libczmq-dev 中的,libzmq.pc 也是在 dev 包安装时才被导入,故需安装如下两个包: sudo apt install libzmq5 libczmq-dev

9月7日上课内容 redis群集

redis高可用重点回顾 redis的两种持久化方式 rdb 优缺点 缺点 ① 数据完整性不如AOF ② RDB类似于快照(完备) ③ 在进行备份时会阻塞进程 优点 ① 持久化的速度比较快(因为保存的是数据结果),在写入到*.rdb持久化文…

C++内存泄露

目录 1.什么是内存泄露 2.内存泄露的危害 3.如何解决内存泄露等相关的问题 1.什么是内存泄露 在C/C中 ,我们申请了资源,因为一些原因忘记对申请的资源进行释放,或者因为异常安全等问题没有进行释放就会造成内存泄露的。 2.内存泄露的危害…

火山引擎边缘云助力智能科技赋予生活更多新意

当下,先进的科学技术使得我们的日常生活变得快捷、舒适。大到上百层智能大厦、高端公共场所、社会智能基础设施,小到智能家居监控、指纹密码锁等,在这个充满想象力的时代,科技以更加智能化的方式改变和守护我们的生活。 引入智能…

【数据结构】树的基础入门

文章目录 什么是树树的常见术语树的表示树的应用 什么是树 相信大家刚学数据结构的时候最先接触的就是顺序表,栈,队列等线性结构. 而树则是一种非线性存储结构,存储的是具有“一对多”关系的数据元素的集合 非线性 体现在它是由n个有限结点(可以是零个结点)组成一个具有层次关…

Google云数据库的“Enterprise“和“Enterprise Plus“版怎么选

最近,Google Cloud SQL(Google云上的RDS)做了一次大的产品调整与发布:将原来的Cloud SQL分为了两个版本,分别为"Enterprise"和"Enterprise Plus"版本。本文概述了两个版本的异同,以帮助…

全解私域流量搭建细节剖析

一、找准吸粉引流的渠道和方式 引流思路: 引流方式:

微信协议开发

人微信号的二次开发可以包括但不限于以下方面: 自定义菜单:根据个人需求设置自定义,方便快速访问常用功能或链接。 消息管理:通过开发接口,实现消息的自动回复、关键词匹配等功能,提供更好的用户体验。 …

基础算法--理解递归

理解递归 递归的两个特点 调用自身结束条件 举个从小就听过的例子: 1. 从前有座山,山中有座庙,庙里有个老和尚,老和尚在给小和尚讲故事:2. 从前有座山,山中有座庙,庙里有个老和尚,…

unity scene场景调整好后让game窗口的视角与scene相同

调整scene中场景视角 选中相机 然后 如果要实现相反的功能 即scene的视角与game的一样则 选中相机

目标检测笔记(十四): 使用YOLOv8完成对图像的目标检测任务(从数据准备到训练测试部署的完整流程)

文章目录 一、目标检测介绍二、YOLOv8介绍三、源码获取四、环境搭建4.1 环境检测 五、数据集准备六、 模型训练6.1 方式一6.2 方式二6.3 针对其他任务 七、模型验证八、模型测试九、模型转换9.1 转onnx9.1.1 方式一 9.2 转tensorRT9.2.1 trtexec9.2.2 代码转换9.2.3 推理代码 一…

问道管理:炸裂上涨,“神奇力量”!Mate 60 Pro+来了

今天上午,同花顺软件刚增添的光刻机板块大爆发,光刻胶板块也大涨。早年两年的光伏,到本年上半年的光模块,再到最近的光刻胶、光刻机,股民评论:“光”,充溢奇特的力气。 上午收盘,上…

SQLite加密解密

Android 微信备份 微信聊天记录导出(2020新版) Android数据库加解密逆向分析(三)——微信数据库密码破解 微备份 论坛讨论 解密sqlite db数据库文件 转自windwos 安装 pysqlcipher3 下载 pysqlcipher 去https://pypi.org/search/?qpysqlcipher&…

Python并发编程实战,用多线程、多进程、多协程加速程序运行

文章目录 1. 并发 & 并行 、同步 & 异步1.1 并发 & 并行并发 Concurrency并行 Parallelism 1.2 同步 & 异步同步 Synchronous异步 Asynchronous 2. CPU密集型计算 & IO密集型计算2.1 CPU密集型(CPU-bound)2.2 IO密集型(I…

智慧工地可视化解决方案-智慧工地源码

智慧工地是指运用信息化手段,围绕施工过程管理,建立互联协同、智能生产、科学管理的施工项目信息化生态圈,并将此数据在虚拟现实环境下与物联网采集到的工程信息进行数据挖掘分析,提供过程趋势预测及专家预案,实现工程…

Redis 7 第九讲 微服务集成Redis 应用篇

Jedis 理论 Jedis是redis的java版本的客户端实现,使用Jedis提供的Java API对Redis进行操作,是Redis官方推崇的方式;并且,使用Jedis提供的对Redis的支持也最为灵活、全面;不足之处,就是编码复杂度较高。 …