精华内容
下载资源
问答
  • 数据库锁分类和总结

    万次阅读 多人点赞 2018-04-18 15:11:28
    是网络数据库中的一个非常重要的概念,当多个用户同时对数据库并发操作时,会带来数据不一致的问题,所以,主要用于多用户环境下保证数据库完整性和一致性。 帮助理解:以商场的试衣间为例,每个试衣间都可供...

    是网络数据库中的一个非常重要的概念,当多个用户同时对数据库并发操作时,会带来数据不一致的问题,所以,锁主要用于多用户环境下保证数据库完整性和一致性。

    帮助理解:以商场的试衣间为例,每个试衣间都可供多个消费者使用,因此,可能出现多个消费者同时需要使用试衣间试衣服。为了避免冲突,试衣间装了锁,某一个试衣服的人在试衣间里把锁锁住了,其他顾客就不能从外面打开了,只能等待里面的顾客试完衣服,从里面把锁打开,外面的人才能进去。

    数据库锁出现的目的:处理并发问题

    并发控制的主要采用的技术手段:乐观锁、悲观锁和时间戳。

    锁分类

    数据库系统角度分为三种:排他锁、共享锁、更新锁。
    程序员角度分为两种:一种是悲观锁,一种乐观锁。

    悲观锁(Pessimistic Lock)

    顾名思义,很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人拿这个数据就会block(阻塞),直到它拿锁。

    悲观锁(Pessimistic Lock):正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

    传统的关系数据库里用到了很多这种锁机制,比如行锁、表锁、读锁、写锁等,都是在操作之前先上锁。

    悲观锁按使用性质划分

    共享锁(Share Lock)

    S锁,也叫读锁,用于所有的只读数据操作。共享锁是非独占的,允许多个并发事务读取其锁定的资源。
    性质
    1. 多个事务可封锁同一个共享页;
    2. 任何事务都不能修改该页;
    3. 通常是该页被读取完毕,S锁立即被释放。

    在SQL Server中,默认情况下,数据被读取后,立即释放共享锁。
    例如,执行查询语句“SELECT * FROM my_table”时,首先锁定第一页,读取之后,释放对第一页的锁定,然后锁定第二页。这样,就允许在读操作过程中,修改未被锁定的第一页。
    例如,语句“SELECT * FROM my_table HOLDLOCK”就要求在整个查询过程中,保持对表的锁定,直到查询完成才释放锁定。

    排他锁(Exclusive Lock)

    X锁,也叫写锁,表示对数据进行写操作。如果一个事务对对象加了排他锁,其他事务就不能再给它加任何锁了。(某个顾客把试衣间从里面反锁了,其他顾客想要使用这个试衣间,就只有等待锁从里面打开了。)
    性质
    1. 仅允许一个事务封锁此页;
    2. 其他任何事务必须等到X锁被释放才能对该页进行访问;
    3. X锁一直到事务结束才能被释放。

    产生排他锁的SQL语句如下:select * from ad_plan for update;

    更新锁

    U锁,在修改操作的初始化阶段用来锁定可能要被修改的资源,这样可以避免使用共享锁造成的死锁现象。

    因为当使用共享锁时,修改数据的操作分为两步:
    1. 首先获得一个共享锁,读取数据,
    2. 然后将共享锁升级为排他锁,再执行修改操作。
    这样如果有两个或多个事务同时对一个事务申请了共享锁,在修改数据时,这些事务都要将共享锁升级为排他锁。这时,这些事务都不会释放共享锁,而是一直等待对方释放,这样就造成了死锁。
    如果一个数据在修改前直接申请更新锁,在数据修改时再升级为排他锁,就可以避免死锁。

    性质
    1. 用来预定要对此页施加X锁,它允许其他事务读,但不允许再施加U锁或X锁;
    2. 当被读取的页要被更新时,则升级为X锁;
    3. U锁一直到事务结束时才能被释放。

    悲观锁按作用范围划分为:行锁、表锁。(这部分摘自https://www.jianshu.com/p/eb41df600775

    行锁

    锁的作用范围是行级别。

    表锁

    锁的作用范围是整张表。

    数据库能够确定那些行需要锁的情况下使用行锁,如果不知道会影响哪些行的时候就会使用表锁。

    举个例子,一个用户表user,有主键id和用户生日birthday。
    当你使用update … where id=?这样的语句时,数据库明确知道会影响哪一行,它就会使用行锁;
    当你使用update … where birthday=?这样的的语句时,因为事先不知道会影响哪些行就可能会使用表锁。

    乐观锁(Optimistic Lock)

    顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以,不会上锁。但是在更新的时候会判断一下在此期间别人有没有更新这个数据,可以使用版本号等机制。

    乐观锁( Optimistic Locking ): 相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。
    悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。而乐观锁机制在一定程度上解决了这个问题。
    乐观锁,大多是基于数据版本( Version )记录机制实现。
    数据版本:为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

    乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

    乐观锁的实现方式(这部分摘自https://www.jianshu.com/p/eb41df600775

    • 版本号(version)

    版本号(记为version):就是给数据增加一个版本标识,在数据库上就是表中增加一个version字段,每次更新把这个字段加1,读取数据的时候把version读出来,更新的时候比较version,如果还是开始读取的version就可以更新了,如果现在的version比老的version大,说明有其他事务更新了该数据,并增加了版本号,这时候得到一个无法更新的通知,用户自行根据这个通知来决定怎么处理,比如重新开始一遍。这里的关键是判断version和更新两个动作需要作为一个原子单元执行,否则在你判断可以更新以后正式更新之前有别的事务修改了version,这个时候你再去更新就可能会覆盖前一个事务做的更新,造成第二类丢失更新,所以你可以使用update … where … and version=”old version”这样的语句,根据返回结果是0还是非0来得到通知,如果是0说明更新没有成功,因为version被改了,如果返回非0说明更新成功。

    • 时间戳(使用数据库服务器的时间戳)

      时间戳(timestamp):和版本号基本一样,只是通过时间戳来判断而已,注意时间戳要使用数据库服务器的时间戳不能是业务系统的时间。

    • 待更新字段

      待更新字段:和版本号方式相似,只是不增加额外字段,直接使用有效数据字段做版本控制信息,因为有时候我们可能无法改变旧系统的数据库表结构。假设有个待更新字段叫count,先去读取这个count,更新的时候去比较数据库中count的值是不是我期望的值(即开始读的值),如果是就把我修改的count的值更新到该字段,否则更新失败。java的基本类型的原子类型对象如AtomicInteger就是这种思想。

    • 所有字段
      所有字段:和待更新字段类似,只是使用所有字段做版本控制信息,只有所有字段都没变化才会执行更新。

    乐观锁几种方式的区别(这部分摘自https://www.jianshu.com/p/eb41df600775

    新系统设计可以使用version方式和timestamp方式,需要增加字段,应用范围是整条数据,不论那个字段修改都会更新version,也就是说两个事务更新同一条记录的两个不相关字段也是互斥的,不能同步进行。旧系统不能修改数据库表结构的时候使用数据字段作为版本控制信息,不需要新增字段,待更新字段方式只要其他事务修改的字段和当前事务修改的字段没有重叠就可以同步进行,并发性更高。

    并发控制会造成两种锁(这部分摘自https://www.cnblogs.com/ismallboy/p/5574006.html

    • 活锁
    • 死锁

    并发控制会造成活锁和死锁,就像操作系统那样,会因为互相等待而导致。

    活锁

    定义:指的是T1封锁了数据R,T2同时也请求封锁数据R,T3也请求封锁数据R,当T1释放了锁之后,T3会锁住R,T4也请求封锁R,则T2就会一直等待下去。
    解决方法:采用“先来先服务”策略可以避免。

    死锁

    定义:就是我等你,你又等我,双方就会一直等待下去。比如:T1封锁了数据R1,正请求对R2封锁,而T2封住了R2,正请求封锁R1,这样就会导致死锁,死锁这种没有完全解决的方法,只能尽量预防。
    预防方法
    1. 一次封锁法,指的是一次性把所需要的数据全部封锁住,但是这样会扩大了封锁的范围,降低系统的并发度;
    2. 顺序封锁法,指的是事先对数据对象指定一个封锁顺序,要对数据进行封锁,只能按照规定的顺序来封锁,但是这个一般不大可能的。

    系统判定死锁的方法

    • 超时法:如果某个事物的等待时间超过指定时限,则判定为出现死锁;
    • 等待图法:如果事务等待图中出现了回路,则判断出现了死锁。

    对于解决死锁的方法,只能是撤销一个处理死锁代价最小的事务,释放此事务持有的所有锁,同时对撤销的事务所执行的数据修改操作必须加以恢复。

    参考资料

    • 《Java程序员面试笔试真题与分析》 猿媛之家 编著
    • 百度百科
    展开全文
  • 详细介绍DB2数据库锁升级分析及处理步骤。
  • 又称读(S),共享不阻塞其他事务的读操作,但阻塞写操作,同一数据对象A可以共存多个共享,这被称为共享兼容。 当T1为数据对象A加上共享后,可以对A进行读操作,但不能进行写操作,并且T2可以再次对A加...

    一,锁的种类

    1.共享锁——Shared lock
    又称读锁(S锁),共享锁不阻塞其他事务的读操作,但阻塞写操作,同一数据对象A可以共存多个共享锁,这被称为共享锁兼容。

    当T1为数据对象A加上共享锁后,可以对A进行读操作,但不能进行写操作,并且T2可以再次对A加共享锁,大家都可以正常地读A,但是在A上的共享锁释放之前,任何事务不可以对A进行写操作。

    例1:

    T1:select * from table

    T2:update table set column1=‘hello’

    分析:假设T1先执行,则T2必须等待T1执行完才可以执行。因为T2为写操作,需要为table加一个排他锁,而数据库规定相同资源不可以同时存在共享锁和排他锁,所以T2必须等待T1执行完,释放掉共享锁,才可以加排他锁,然后执行update。

    例2:(死锁的发生)

    T1:

    begin Transaction t1

    select * from table with (holdlock) (holdlock的意思是加共享锁,直到事务结束(提交或回滚)才会释放)

    update table set column1=‘hello’

    T2:

    begin Transaction t2

    select * from table with (holdlock)

    update table set column1=‘world’

    分析:假设T1和T2同时到达select语句,都为table加上了共享锁,那么当T1、T2要执行update时,根据锁机制,共享锁需要升级为排他锁,但是排他锁与共享锁不能共存,要给table加排他锁,必须等待table上的共享锁全部释放才可以,可是holdlock的共享锁必须等待事务结束才能释放,因此T1和T2都在等待对方释放共享锁,形成循环等待,造成死锁。

    例3:

    T1:update table set column1=‘hello’ where id=‘001’

    T2:update table set column1=‘world’ where id=‘002’

    分析:此种情况有可能造成等待,分为id列有索引与无索引两种情况。

    (1)id列有索引,则T1直接定位到id='001’行,加排他锁,更新;T2直接定位到id='002’行,加排他锁,更新。互不影响。

    (2)id列无索引,T1扫描全表,找到id='001’行,加排他锁后,T2为了找到id='002’行,需要全表扫描,那么就会为table加共享锁或更新锁或排他锁,但不管加什么锁,都需要等待T1释放id='001’行的排他锁,不然无法为全表加锁。

    死锁可以通过直接对表加排他锁来解决,即将事务的隔离级别提高至最高级——串行读,各个事务串行执行,可是这样虽然避免了死锁,但是效率太低了,那我们干脆别发明并发这个词语好了。

    2.更新锁——Update lock
    更新锁(U锁)。当T1给资源A加上更新锁后,代表该资源将在稍后更新,更新锁与共享锁兼容,更新锁可以防止例2里那种一般情况的死锁发生,更新锁会阻塞其他的更新锁和排他锁。因此相同资源上不能存在多个更新锁。

    更新锁允许其他事务在更新之前读取资源。但不可以修改。因为其他事务想获取资源的排他锁时,发现该资源已存在U锁,则等待U锁释放。

    在T1找到需要更新的数据时,更新锁直接转为排他锁,开始更新数据,不需要等待其他事务释放共享锁啥的。

    那么就问了,共享锁为什么不可以直接升级为排他锁,而必须等待其他共享锁都释放掉才可以转为排他锁呢?

    这就是共享锁和更新锁的一个区别了,共享锁之间是兼容的,但是更新锁之间互不兼容,因此仅有一个更新锁直接转为排他锁是安全的,而多个共享锁问也不问直接转为排他锁,那怎么行呢,排他锁只能有一个的,这就是为什么共享锁需要等待其他共享锁释放才可以升级为排他锁的原因了。

    例4:

    T1:

    begin

    select * from table with (updlock) (加更新锁)

    update table set column1=‘hello’ (重点:这里T1做update时,不需要等T2释放什么,而是直接把更新锁升级为排他锁,然后执行update)

    T2:

    begin

    select * from table (T1的更新锁不影响T2的select)

    update table set column1=‘world’ (T2的update需要等待T1的update执行完)

    分析:(1)T1先到达,T1的select句对table加更新锁,此时T2紧接着到达,T2的select句对table加共享锁,假设T2的select先执行完,要开始T2的update,发现table已有更新锁,则T2等,T1此时执行完select,然后将更新锁升级为排他锁,开始更新数据,执行完成,事务结束,释放排他锁,此时T2才开始对table加排他锁并更新。

    (2)T2先到,T1紧接着,T2加共享锁 => T1加更新锁 => 假设T2先结束select => 试图将共享锁升级为排他锁 => 发现已有更新锁 => 之后的情况同

    3.排他锁——Exclusive Locks
    又叫独占锁,写锁,X锁,很容易理解,排他锁阻塞任何锁,假设T1为资源A加上排他锁,则其他事务不允许对资源A进行任何的读写操作。

    例5:(假设id都是自增长且连续的)

    T1: update table set column1=‘hello’ where id<1000

    T2: update table set column1=‘world’ where id>1000

    假设T1先达,T2随后至,这个过程中T1会对id<1000的记录施加排他锁.但不会阻塞T2的update。

    例6:

    T1: update table set column1=‘hello’ where id<1000

    T2: update table set column1=‘world’ where id>900

    假设T1先达,T2立刻也到,T1加的排他锁会阻塞T2的update。

    4.意向锁——Intent Locks
    意向锁,就是说当你给数据加锁时,必须先给他的上级加锁,用来向其他事务表明这段数据中的某些数据正在被加某某锁,你看着办吧。其实是一个节省开销的做法。

    例7:

    T1:

    begin tran

    select * from table with (xlock) where id=10 --意思是对id=10这一行强加排他锁

    T2:

    begin tran

    select * from table with (tablock) --意思是要加表级锁

    假设T1先执行,T2后执行,T2执行时,欲加表锁,为判断是否可以加表锁,数据库系统要逐条判断table表每行记录是否已有排他锁,

    如果发现其中一行已经有排他锁了,就不允许再加表锁了。只是这样逐条判断效率太低了。

    实际上,数据库系统不是这样工作的。当T1的select执行时,系统对表table的id=10的这一行加了排他锁,还同时悄悄的对整个表加了意向排他锁(IX),当T2执行表锁时,只需要看到这个表已经有意向排他锁存在,就直接等待,而不需要逐条检查资源了。

    常用的意向锁有三种:意向共享锁(Intent Share Lock,简称IS锁);意向排他锁(Intent Exclusive Lock,简称IX锁);共享意向排它锁(Share Intent Exclusive Lock,简称SIX锁),共享意向排它锁的意思是,某事务要读取整个表,并更新其中的某些数据。

    二、如何加锁

    1.数据库自动加锁
    其实锁在大多数情况下都是数据库自动加的,比如这么一条语句:

    update table set column1=‘hello’

    通过Profiler跟踪sql发现,他会逐行先获取U锁,然后转为X锁,更新完这一行,不释放X锁,继续获取下一行的U锁,转X…一直到全部更新结束,再逐行释放掉所有的X锁。

    而如果加上where条件,如:update table set column1=‘hello’ where column2=‘world’,并且column2无索引,则逐行获取U锁,如果符合条件,转X锁,更新,不释放X锁;如果不符合,释放U锁。

    但是如果有索引的话,则不需要逐行获取U锁 => 判断 => 转X锁或释放,而是直接获取到要更新行的X锁,更新,释放X锁即可。
    所有的U,X,S之前都会首先为表或者页加意向锁。

    2.手动加锁
    例8:

    T1:select * from table with (tablock) --对表加共享锁,且事务不完成,共享锁不释放。

    T2:select * from table with (holdlock) --对涉及范围内加共享锁,事务不完成,共享锁不释放。

    我感觉可能大多数情况下是不需要我们手动加锁的,因为我们不是专业搞数据库的,很多场景可能预想不到,就会导致一些错误,我们管理事务的隔离级别就可以了,数据库会根据隔离级别的不同,按照策略来加锁。

    三、锁的粒度

    锁的粒度指的是锁生效的范围,即行锁,页锁,或者是表锁。锁的粒度一般由数据库自主管理,不同的事物隔离级别,数据库会有不同的加锁策略(比如加什么类型的锁,加什么粒度的锁)。也可以手动指定。在下面的例子中,我们假设id是自增的主键。

    例9:

    T1::select * from table with (paglock) --页锁

    T2::update table set column1=‘hello’ where id>10

    T1执行时,会先对第一页加共享(S)锁,读完第一页后,释放锁,再对第二页加共享锁,依此类推。假设前10行记录恰好是一页(当然,一般不可能一页只有10行记录),那么T1执行到第一页查询时,并不会阻塞T2的更新。

    例10:

    T1:select * from table with (rowlock) --行锁

    T2:update table set column1=‘hello’ where id=10

    T1执行时,对每行加共享锁,读取,然后释放,再对下一行加锁;T2执行时,会对id=10的那一行试图加锁,只要该行没有被T1加上行锁,T2就可以顺利执行update操作。

    例11:

    T1:select * from table with (tablock) --表锁

    T2:update table set column1=‘hello’ where id = 10

    T1执行,对整个表加共享锁。T1必须完全查询完,T2才可以允许加锁,并开始更新。

    通过分析以上3个例子可以得出结论:锁的粒度与系统的并发性和系统开销密切相关。粒度越小,则并发度越大,开销越大;反之,粒度越大,则并发度越小,开销越小。

    四.锁与事务隔离级别的优先级

    先上结论,手工指定的锁优先。

    例12:

    T1:GO

    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

    GO

    BEGIN TRANSACTION

    SELECT * FROM table with (NOLOCK)

    GO

    T2:update table set column1=‘hello’ where id=10

    T1是事物隔离级别为最高级,串行读,数据库系统本应对后面的select语句自动加表级锁,但因为手工指定了NOLOCK,所以该select语句不会加任何锁,所以T2也就不会有任何阻塞。

    五、数据库的几个 重要Hint及他们的区别

    1. holdlock 对表加共享锁,且事物不完成,共享锁不释放。

    2. tablock 对表加共享锁,只要statement不完成,共享锁不释放。

    3. TABLOCKX 对表加排他锁,事务不完成,排他锁不释放。

    4. xlock 加排他锁,和tablockx的区别:tablockx只能对整张表加锁,而xlock可以指定锁的粒度。

    例13:

    select * from table with (xlock paglock) --对page加排他锁

    select * from table with (xlock tablock) --效果等同于select * from table with (tablockx)

    六、如何提高并发效率

    1.悲观锁
    利用数据库本身的锁机制实现。通过上面对数据库锁的了解,可以根据具体业务情况综合使用事务隔离级别与合理的手工指定锁的方式比如降低锁的粒度等减少并发等待。

    2.乐观锁
    利用程序处理并发。原理都比较好理解,基本一看即懂。方式大概有以下3种:

    (1)对记录加版本号。
    
    (2)对记录加时间戳。
    
    (3)对将要更新的数据进行提前读取、事后对比。
    
    首先说明一点的是:乐观锁在数据库上的实现完全是逻辑的,数据库本身不提供支持,而是需要开发者自己来实现。
    

    常见的做法有两种:版本号控制及时间戳控制。

    版本号控制的原理:

    为表中加一个 version 字段;
    当读取数据时,连同这个 version 字段一起读出;
    数据每更新一次就将此值加一;
    当提交更新时,判断数据库表中对应记录的当前版本号是否与之前取出来的版本号一致,如果一致则可以直接更新,如果不一致则表示是过期数据需要重试或者做其它操作(PS:这完完全全就是 CAS 的实现逻辑呀~)
    至于时间戳控制,其原理和版本号控制差不多,也是在表中添加一个 timestamp 的时间戳字段,然后提交更新时判断数据库中对应记录的当前时间戳是否与之前取出来的时间戳一致,一致就更新,不一致就重试。
    在这里插入图片描述
    CAS是什么?
    CAS是英文单词CompareAndSwap的缩写,中文意思是:比较并替换。CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。

    CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。整个比较并替换的操作是一个原子操作。

    CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

    展开全文
  • 1.锁? 1.1何为锁 锁在现实中的意义为:封闭的器物,以钥匙或暗码开启。在计算机中的锁一般用来管理对共享资源的...1.2为什么要懂数据库锁? 通常来说对于一般的开发人员,在使用数据库的时候一般懂点DQL(select)...

    1.锁?

    1.1何为锁

    锁在现实中的意义为:封闭的器物,以钥匙或暗码开启。在计算机中的锁一般用来管理对共享资源的并发访问,比如我们java同学熟悉的Lock,synchronized等都是我们常见的锁。当然在我们的数据库中也有锁用来控制资源的并发访问,这也是数据库和文件系统的区别之一。

    1.2为什么要懂数据库锁?

    通常来说对于一般的开发人员,在使用数据库的时候一般懂点DQL(select),DML(insert,update,delete)就够了。

    小明是一个刚刚毕业在互联网公司工作的Java开发工程师,平常的工作就是完成PM的需求,当然在完成需求的同时肯定逃脱不了spring,springmvc,mybatis的那一套框架,所以一般来说sql还是自己手写,遇到比较复杂的sql会从网上去百度一下。对于一些比较重要操作,比如交易啊这些,小明会用spring的事务来对数据库的事务进行管理,由于数据量比较小目前还涉及不了分布式事务。

    前几个月小明过得都还风调雨顺,知道有一天,小明接了一个需求,商家有个配置项,叫优惠配置项,可以配置买一送一,买一送二等等规则,当然这些配置是批量传输给后端的,这样就有个问题每个规则都得去匹配他到底是删除还是添加还是修改,这样后端逻辑就比较麻烦,聪明的小明想到了一个办法,直接删除这个商家的配置,然后全部添加进去。小明马上开发完毕,成功上线。

    开始上线没什么毛病,但是日志经常会出现一些mysql-insert-deadlock异常。由于小明经验比较浅,对于这类型的问题第一次遇见,于是去问了他们组的老司机-大红,大红一看见这个问题,然后看了他的代码之后,输出了几个命令看了几个日志,马上定位了问题,告诉了小明:这是因为delete的时候会加间隙锁,但是间隙锁之间却可以兼容,但是插入新的数据的时候就会因为插入意向锁会被间隙锁阻塞,导致双方被资源被互占,导致死锁。小明听了之后似懂非懂,由于大红的事情比较多,不方便一直麻烦大红,所以决定自己下来自己想。下班过后,小明回想大红说的话,什么是间隙锁,什么是插入意向锁,看来作为开发者对数据库不应该只会写SQL啊,不然遇到一些疑难杂症完全没法解决啊。想完,于是小明就踏上了学习Mysql锁这条不归之路。

    2.InnoDB

    2.1mysql体系架构

    小明没有着急去了解锁这方面的知识,他首先先了解了下Mysql体系架构:

     

    可以发现Mysql由连接池组件、管理服务和工具组件、sql接口组件、查询分析器组件、优化器组件、 缓冲组件、插件式存储引擎、物理文件组成。

     

    小明发现在mysql中存储引擎是以插件的方式提供的,在Mysql中有多种存储引擎,每个存储引擎都有自己的特点。随后小明在命令行中打出了:

    show engines \G;
    复制代码

    一看原来有这么多种引擎。

    又打出了下面的命令,查看当前数据库默认的引擎:

    show variables like '%storage_engine%';
    复制代码

     

     

    小明恍然大悟:原来自己的数据库是使用的InnoDB,依稀记得自己在上学的时候好像听说过有个引擎叫MyIsAM,小明想这两个有啥不同呢?马上查找了一下资料:

     

    对比项InnoDBMyIsAM
    事务支持不支持
    支持MVCC行锁表锁
    外键支持不支持
    存储空间存储空间由于需要高速缓存,较大可压缩
    适用场景有一定量的update和Insert大量的select

    小明大概了解了一下InnoDB和MyIsAM的区别,由于使用的是InnoDB,小明就没有过多的纠结这一块。

    2.2事务的隔离性

    小明在研究锁之前,又回想到之前上学的时候教过的数据库事务隔离性,其实锁在数据库中其功能之一也是用来实现事务隔离性。而事务的隔离性其实是用来解决,脏读,不可重复读,幻读几类问题。

    2.2.1 脏读

    一个事务读取到另一个事务未提交的更新数据。 什么意思呢?

    时间点事务A事务B
    1begin; 
    2select * from user where id = 1;begin;
    3 update user set namm = 'test' where id = 1;
    4select * from user where id = 1; 
    5commit;commit;

    在事务A,B中,事务A在时间点2,4分别对user表中id=1的数据进行了查询了,但是事务B在时间点3进行了修改,导致了事务A在4中的查询出的结果其实是事务B修改后的。破坏了数据库中的隔离性。

    2.2.2 不可重复读

    在同一个事务中,多次读取同一数据返回的结果不同,和脏读不同的是这里读取的是已经提交过后的。

    时间点事务A事务B
    1begin; 
    2select * from user where id = 1;begin;
    3 update user set namm = 'test' where id = 1;
    4 commit;
    5select * from user where id = 1; 
    6commit; 

    在事务B中提交的操作在事务A第二次查询之前,但是依然读到了事务B的更新结果,也破坏了事务的隔离性。

    2.2.3 幻读

    一个事务读到另一个事务已提交的insert数据。

    时间点事务A事务B
    1begin; 
    2select * from user where id > 1;begin;
    3 insert user select 2;
    4 commit;
    5select * from user where id > 1; 
    6commit; 

    在事务A中查询了两次id大于1的,在第一次id大于1查询结果中没有数据,但是由于事务B插入了一条Id=2的数据,导致事务A第二次查询时能查到事务B中插入的数据。

    事务中的隔离性:

    隔离级别脏读不可重复读幻读
    未提交读(RUC)NONONO
    已提交读(RC)YESNONO
    可重复读(RR)YESYESNO
    可串行化YESYESYES

    小明注意到在收集资料的过程中,有资料写到InnoDB和其他数据库有点不同,InnoDB的可重复读其实就能解决幻读了,小明心想:这InnoDB还挺牛逼的,我得好好看看到底是怎么个原理。

    2.3 InnoDB锁类型

    小明首先了解一下Mysql中常见的锁类型有哪些:

    2.3.1 S or X

    在InnoDb中实现了两个标准的行级锁,可以简单的看为两个读写锁:

    • S-共享锁:又叫读锁,其他事务可以继续加共享锁,但是不能继续加排他锁。
    • X-排他锁: 又叫写锁,一旦加了写锁之后,其他事务就不能加锁了。

    兼容性:是指事务A获得一个某行某种锁之后,事务B同样的在这个行上尝试获取某种锁,如果能立即获取,则称锁兼容,反之叫冲突。

    纵轴是代表已有的锁,横轴是代表尝试获取的锁。

    .XS
    X冲突冲突
    S冲突兼容

    2.3.2 意向锁

    意向锁在InnoDB中是表级锁,和他的名字一样他是用来表达一个事务想要获取什么。意向锁分为:

    • 意向共享锁:表达一个事务想要获取一张表中某几行的共享锁。
    • 意向排他锁:表达一个事务想要获取一张表中某几行的排他锁。

    这个锁有什么用呢?为什么需要这个锁呢? 首先说一下如果没有这个锁,如果要给这个表加上表锁,一般的做法是去遍历每一行看看他是否有行锁,这样的话效率太低,而我们有意向锁,只需要判断是否有意向锁即可,不需要再去一行行的去扫描。

    在InnoDB中由于支持的是行级的锁,因此InnboDB锁的兼容性可以扩展如下:

    .IXISXS
    IX兼容兼容冲突冲突
    IS兼容兼容冲突兼容
    X冲突冲突冲突冲突
    S冲突兼容冲突兼容

    2.3.3 自增长锁

    自增长锁是一种特殊的表锁机制,提升并发插入性能。对于这个锁有几个特点:

    • 在sql执行完就释放锁,并不是事务执行完。
    • 对于Insert...select大数据量插入会影响插入性能,因为会阻塞另外一个事务执行。
    • 自增算法可以配置。

    在MySQL5.1.2版本之后,有了很多优化,可以根据不同的模式来进行调整自增加锁的方式。小明看到了这里打开了自己的MySQL发现是5.7之后,于是便输入了下面的语句,获取到当前锁的模式:

    mysql> show variables like 'innodb_autoinc_lock_mode';
    +--------------------------+-------+
    | Variable_name            | Value |
    +--------------------------+-------+
    | innodb_autoinc_lock_mode | 2     |
    +--------------------------+-------+
    1 row in set (0.01 sec)
    复制代码

    在MySQL中innodb_autoinc_lock_mode有3种配置模式:0、1、2,分别对应”传统模式”, “连续模式”, “交错模式”。

    1. 传统模式:也就是我们最上面的使用表锁。
    2. 连续模式:对于插入的时候可以确定行数的使用互斥量,对于不能确定行数的使用表锁的模式。
    3. 交错模式:所有的都使用互斥量,为什么叫交错模式呢,有可能在批量插入时自增值不是连续的,当然一般来说如果不看重自增值连续一般选择这个模式,性能是最好的。

    2.4InnoDB锁算法

    小明已经了解到了在InnoDB中有哪些锁类型,但是如何去使用这些锁,还是得靠锁算法。

    2.4.1 记录锁(Record-Lock)

    记录锁是锁住记录的,这里要说明的是这里锁住的是索引记录,而不是我们真正的数据记录。

    • 如果锁的是非主键索引,会在自己的索引上面加锁之后然后再去主键上面加锁锁住.
    • 如果没有表上没有索引(包括没有主键),则会使用隐藏的主键索引进行加锁。
    • 如果要锁的列没有索引,则会进行全表记录加锁。

    2.4.2 间隙锁

    间隙锁顾名思义锁间隙,不锁记录。锁间隙的意思就是锁定某一个范围,间隙锁又叫gap锁,其不会阻塞其他的gap锁,但是会阻塞插入间隙锁,这也是用来防止幻读的关键。

     

     

     

    2.4.3 next-key锁

    这个锁本质是记录锁加上gap锁。在RR隔离级别下(InnoDB默认),Innodb对于行的扫描锁定都是使用此算法,但是如果查询扫描中有唯一索引会退化成只使用记录锁。为什么呢? 因为唯一索引能确定行数,而其他索引不能确定行数,有可能在其他事务中会再次添加这个索引的数据会造成幻读。

    这里也说明了为什么Mysql可以在RR级别下解决幻读。

    2.4.4 插入意向锁

    插入意向锁Mysql官方对其的解释:

    An insert intention lock is a type of gap lock set by INSERT operations prior to row insertion. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap. Suppose that there are index records with values of 4 and 7. Separate transactions that attempt to insert values of 5 and 6, respectively, each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting.

    可以看出插入意向锁是在插入的时候产生的,在多个事务同时写入不同数据至同一索引间隙的时候,并不需要等待其他事务完成,不会发生锁等待。假设有一个记录索引包含键值4和7,不同的事务分别插入5和6,每个事务都会产生一个加在4-7之间的插入意向锁,获取在插入行上的排它锁,但是不会被互相锁住,因为数据行并不冲突。

    这里要说明的是如果有间隙锁了,插入意向锁会被阻塞。

    2.5 MVCC

    MVCC,多版本并发控制技术。在InnoDB中,在每一行记录的后面增加两个隐藏列,记录创建版本号和删除版本号。通过版本号和行锁,从而提高数据库系统并发性能。

    在MVCC中,对于读操作可以分为两种读:

    • 快照读:读取的历史数据,简单的select语句,不加锁,MVCC实现可重复读,使用的是MVCC机制读取undo中的已经提交的数据。所以它的读取是非阻塞的。
    • 当前读:需要加锁的语句,update,insert,delete,select...for update等等都是当前读。

    在RR隔离级别下的快照读,不是以begin事务开始的时间点作为snapshot建立时间点,而是以第一条select语句的时间点作为snapshot建立的时间点。以后的select都会读取当前时间点的快照值。

    在RC隔离级别下每次快照读均会创建新的快照。

    具体的原理是通过每行会有两个隐藏的字段一个是用来记录当前事务,一个是用来记录回滚的指向Undolog。利用undolog就可以读取到之前的快照,不需要单独开辟空间记录。

    3.加锁分析

    小明到这里,已经学习很多mysql锁有关的基础知识,所以决定自己创建一个表搞下实验。首先创建了一个简单的用户表:

    CREATE TABLE `user` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(11) CHARACTER SET utf8mb4 DEFAULT NULL,
      `comment` varchar(11) CHARACTER SET utf8 DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `index_name` (`name`)
    ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
    复制代码

    然后插入了几条实验数据:

    insert user select 20,333,333;
    insert user select 25,555,555;
    insert user select 20,999,999;
    
    复制代码

    数据库事务隔离选择了RR

    3.1 实验1

    小明开启了两个事务,进行实验1.

    时间点事务A事务B
    1begin; 
    2select * from user where name = '555' for update;begin;
    3 insert user select 31,'556','556';
    4 ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

    小明开启了两个事务并输入了上面的语句,发现事务B居然出现了超时,小明看了一下自己明明是对name = 555这一行进行的加锁,为什么我想插入name=556给我阻塞了。于是小明打开命令行输入:

    select * from information_schema.INNODB_LOCKS
    复制代码

    发现在事务A中给555加了Next-key锁,事务B插入的时候会首先进行插入意向锁的插入,于是得出下面结论:

     

    可以看见事务B由于间隙锁和插入意向锁的冲突,导致了阻塞。

     

    3.2 实验2

    小明发现上面查询条件用的是普通的非唯一索引,于是小明就试了一下主键索引:

    时间点事务A事务B
    1begin; 
    2select * from user where id = 25 for update;begin;
    3 insert user select 26,'666','666';
    4 Query OK, 1 row affected (0.00 sec)

    Records: 1 Duplicates: 0 Warnings: 0

    居然发现事务B并没有发生阻塞,哎这个是咋回事呢,小明有点疑惑,按照实验1的套路应该会被阻塞啊,因为25-30之间会有间隙锁。于是小明又祭出了命令行,发现只加了X记录锁。原来是因为唯一索引会降级记录锁,这么做的理由是:非唯一索引加next-key锁由于不能确定明确的行数有可能其他事务在你查询的过程中,再次添加这个索引的数据,导致隔离性遭到破坏,也就是幻读。唯一索引由于明确了唯一的数据行,所以不需要添加间隙锁解决幻读。

     

     

     

    3.3 实验3

    上面测试了主键索引,非唯一索引,这里还有个字段是没有索引,如果对其加锁会出现什么呢?

    时间点事务A事务B
    1begin; 
    2select * from user where comment = '555' for update;begin;
    3 insert user select 26,'666','666';
    4 ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    5 insert user select 31,'3131','3131';
    6 ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    7 insert user select 10,'100','100';
    8 ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

    小明一看哎哟我去,这个咋回事呢,咋不管是用实验1非间隙锁范围的数据,还是用间隙锁里面的数据都不行,难道是加了表锁吗?

    的确,如果用没有索引的数据,其会对所有聚簇索引上都加上next-key锁。

     

     

     

    所以大家平常开发的时候如果对查询条件没有索引的,一定进行一致性读,也就是加锁读,会导致全表加上索引,会导致其他事务全部阻塞,数据库基本会处于不可用状态。

    4.回到事故

    4.1 死锁

    小明做完实验之后总算是了解清楚了加锁的一些基本套路,但是之前线上出现的死锁又是什么东西呢?

    死锁:是指两个或两个以上的事务在执行过程中,因争夺资源而造成的一种互相等待的现象。说明有等待才会有死锁,解决死锁可以通过去掉等待,比如回滚事务。

    解决死锁的两个办法:

    1. 等待超时:当某一个事务等待超时之后回滚该事务,另外一个事务就可以执行了,但是这样做效率较低,会出现等待时间,还有个问题是如果这个事务所占的权重较大,已经更新了很多数据了,但是被回滚了,就会导致资源浪费。
    2. 等待图(wait-for-graph): 等待图用来描述事务之间的等待关系,当这个图如果出现回路如下:

     

     

    就出现回滚,通常来说InnoDB会选择回滚权重较小的事务,也就是undo较小的事务。

     

    4.2 线上问题

    小明到这里,基本需要的基本功都有了,于是在自己的本地表中开始复现这个问题:

    时间点事务A事务B
    1begin;begin;
    2delete from user where name = '777';delete from user where name = '666';
    3insert user select 27,'777','777';insert user select 26,'666','666';
    4ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transactionQuery OK, 1 row affected (14.32 sec) Records: 1 Duplicates: 0 Warnings: 0

    可以看见事务A出现被回滚了,而事务B成功执行。 具体每个时间点发生了什么呢?

    时间点2:事务A删除name = '777'的数据,需要对777这个索引加上next-Key锁,但是其不存在,所以只对555-999之间加间隙锁,同理事务B也对555-999之间加间隙锁。间隙锁之间是兼容的。

    时间点3:事务A,执行Insert操作,首先插入意向锁,但是555-999之间有间隙锁,由于插入意向锁和间隙锁冲突,事务A阻塞,等待事务B释放间隙锁。事务B同理,等待事务A释放间隙锁。于是出现了A->B,B->A回路等待。

    时间点4:事务管理器选择回滚事务A,事务B插入操作执行成功。

     

     

     

    4.3 修复BUG

    这个问题总算是被小明找到了,就是因为间隙锁,现在需要解决这个问题,这个问题的原因是出现了间隙锁,那就来去掉他吧:

    • 方案一:隔离级别降级为RC,在RC级别下不会加入间隙锁,所以就不会出现毛病了,但是在RC级别下会出现幻读,可提交读都破坏隔离性的毛病,所以这个方案不行。
    • 方案二:隔离级别升级为可序列化,小明经过测试后发现不会出现这个问题,但是在可序列化级别下,性能会较低,会出现较多的锁等待,同样的也不考虑。
    • 方案三:修改代码逻辑,不要直接删,改成每个数据由业务逻辑去判断哪些是更新,哪些是删除,那些是添加,这个工作量稍大,小明写这个直接删除的逻辑就是为了不做这些复杂的事的,所以这个方案先不考虑。
    • 方案四:较少的修改代码逻辑,在删除之前,可以通过快照查询(不加锁),如果查询没有结果,则直接插入,如果有通过主键进行删除,在之前第三节实验2中,通过唯一索引会降级为记录锁,所以不存在间隙锁。

    经过考虑小明选择了第四种,马上进行了修复,然后上线观察验证,发现现在已经不会出现这个Bug了,这下小明总算能睡个安稳觉了。

    4.4 如何防止死锁

    小明通过基础的学习和平常的经验总结了如下几点:

    • 以固定的顺序访问表和行。交叉访问更容易造成事务等待回路。
    • 尽量避免大事务,占有的资源锁越多,越容易出现死锁。建议拆成小事务。
    • 降低隔离级别。如果业务允许(上面4.3也分析了,某些业务并不能允许),将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。
    • 为表添加合理的索引。防止没有索引出现表锁,出现的死锁的概率会突增。

    我有一个微信公众号,经常会分享一些Java技术相关的干货;如果你喜欢我的分享,可以用微信搜索“Java团长”或者“javatuanzhang”关注。

    展开全文
  • Java中的数据库

    千次阅读 2019-04-05 10:33:15
    面试中经常被问到,Java中有锁,数据库中也有锁,这两个中的都是怎么分类的呢?有两篇博客关于这个问题讲的特别好,详见下文~ Java中的 在读很多并发文章中,会提及各种各样如公平,乐观等等,这篇...

    面试中经常被问到锁,Java中有锁,数据库中也有锁,这两个中的锁都是怎么分类的呢?有两篇博客关于这个问题讲的特别好,详见下文~

    Java中的锁

    在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类。介绍的内容如下:

    • 公平锁/非公平锁
    • 可重入锁
    • 独享锁/共享锁
    • 互斥锁/读写锁
    • 乐观锁/悲观锁
    • 分段锁
    • 偏向锁/轻量级锁/重量级锁
    • 自旋锁

    上面是很多锁的名词,这些分类并不是全是指锁的状态,有的指锁的特性,有的指锁的设计,下面总结的内容是对每个锁的名词进行一定的解释。

    公平锁/非公平锁

    公平锁是指多个线程按照申请锁的顺序来获取锁。
    非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
    对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。
    对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。

    可重入锁

    可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。
    对于Java ReentrantLock而言, 他的名字就可以看出是一个可重入锁,其名字是Re entrant Lock重新进入锁。
    对于Synchronized而言,也是一个可重入锁。可重入锁的一个好处是可一定程度避免死锁。

    synchronized void setA() throws Exception{
        Thread.sleep(1000);
        setB();
    }
    
    synchronized void setB() throws Exception{
        Thread.sleep(1000);
    }

    上面的代码就是一个可重入锁的一个特点,如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。

    独享锁/共享锁

    独享锁是指该锁一次只能被一个线程所持有。
    共享锁是指该锁可被多个线程所持有。

    对于Java ReentrantLock而言,其是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。
    读锁的共享锁可保证并发读是非常高效的,读写,写读 ,写写的过程是互斥的。
    独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。
    对于Synchronized而言,当然是独享锁。

    互斥锁/读写锁

    上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。
    互斥锁在Java中的具体实现就是ReentrantLock
    读写锁在Java中的具体实现就是ReadWriteLock

    乐观锁/悲观锁

    乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。
    悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。
    乐观锁则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。

    从上面的描述我们可以看出,悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,不加锁会带来大量的性能提升。
    悲观锁在Java中的使用,就是利用各种锁。
    乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。

    分段锁

    分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。
    我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。
    当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在那一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。
    但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。
    分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。

    偏向锁/轻量级锁/重量级锁

    这三种锁是指锁的状态,并且是针对Synchronized。在Java 5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。
    偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
    轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
    重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。

    自旋锁

    在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
    典型的自旋锁实现的例子,可以参考自旋锁的实现

    转自https://www.cnblogs.com/qifengshi/p/6831055.html

    数据库中的锁

    详见https://blog.csdn.net/weixin_39651041/article/details/79985715,讲的非常详细

    展开全文
  • 数据库数据库机制及原理

    万次阅读 多人点赞 2018-03-08 18:06:13
    数据库锁 先看一张图自己整理的数据库锁的树形图 概要 数据库锁一般可以分为两类,一个是悲观锁,一个是乐观锁。 乐观锁一般是指用户自己实现的一种锁机制,假设认为数据一般情况下不会造成冲突,所以...
  • 数据库锁 先看一张图自己整理的数据库锁的树形图 概要 数据库锁一般可以分为两类,一个是悲观锁,一个是乐观锁。 乐观锁一般是指用户自己实现的一种锁机制,假设认为数据一般情况下不会造成冲突,所以在数据进行...
  • 数据库学习】数据库总结

    万次阅读 多人点赞 2018-07-26 13:26:41
    1,数据库 1)概念 数据库是长期存储在计算机内、有组织的、可共享的大量数据的集合。 常见数据库管理系统有:Access、mysql、sql server 2)特点 ①数据库数据特点 永久存储、有组织...
  • MySQL innoDB 中的锁升级

    千次阅读 2019-12-22 15:14:08
    什么是锁升级锁升级是指将当前锁的粒度降低,如一把行锁升级唯一把页锁,或者将页锁升级为表锁,如果在数据库设计中认为锁是一中稀有资源,哪么就会频繁有锁升级的现象 发生锁升级的现象 当一条SQL语句对一个...
  • 数据库锁的几种类型

    千次阅读 2018-03-21 12:13:57
    1 前言数据库大并发操作要考虑死锁和的性能问题。看到网上大多语焉不详(尤其更新),所以这里做个简明解释,为下面描述方便,这里用T1代表一个数据库执行请求,T2代表另一个请求,也可以理解为T1为一个线程,T2...
  • 数据库锁机制 很详细的教程,易懂

    万次阅读 多人点赞 2017-05-18 22:38:30
    数据库大并发操作要考虑死锁和的性能问题。看到网上大多语焉不详(尤其更新),所以这里做个简明解释,为下面描述方便,这里用T1代表一个数据库执行请求,T2代表另一个请求,也可以理解为T1为一个线程,T2 为另...
  • 数据库锁细分及详解

    千次阅读 2018-08-16 15:37:31
    简单的数据库锁的大致类型有三种:  共享(S)锁:多个事务可封锁一个共享页;任何事务都不能修改该页; 通常是该页被读取完毕,S锁立即被释放。  排它(X)锁:仅允许一个事务封锁此页;其他任何事务必须等到X锁被...
  • 1、什么是 911 ? 911 是 db2 数据库的一种错误码,表示超时或死锁。超时就是一个事务 A 需要的资源正在被别的事务 B 占有,假如数据库设置的超时时间为 60 秒,超过了 60 秒,事务 B 仍没有释放资源,那么...
  • Mysql数据库中的各种

    万次阅读 多人点赞 2019-04-26 16:46:32
    本文便着重对Mysql数据库中的进行介绍 概述 相对其他数据库而言,MySQL的机制比较简单,其最显著的特点是不同的存储引擎支持不同的机制。 MySQL大致可归纳为以下3种: 表级:开销小,加锁快;不会...
  • 如何解决达梦数据库锁表的问题

    千次阅读 2020-03-06 14:41:07
    我们在数据库的使用过程中,经常会遇到表的问题。遇到此问题的时候,一般用如下方法解决: 1、首先创建一张test99的表,插入几条数据(提交),delete一条数据,不提交。 2、update test99 set a=‘abc’ where a...
  • ArcSDE地理数据库 以及 解锁的概述

    千次阅读 2019-10-22 16:33:59
    概述 将锁应用于地理数据库对象以确保数据完整性。...地理数据库锁始终处于以下两种不同模式之一:共享或排它。 共享锁 ArcGIS 会自动获取使用中的单个数据集上的共享锁,例如,当用户编辑或...
  • 数据库锁的几种原因和解决办法

    千次阅读 2018-04-20 14:46:00
    用户A去查询A表(会获取共享)这时候又去修改某一条数据就会企图升级为排它,这时候用户B也查询A表(同样也获取了共享)这时候用户B也去修改数据也会企图升级为排它,就会等待用户A释放共享,B同样在等待A...
  • 数据库 机制 详解

    2019-03-07 17:26:00
    **数据库锁一般可以分为两大类(乐观锁和悲观锁)**: 乐观锁是指用户实现的锁机制,悲观锁一般就是我们通常说的数据库锁机制。 悲观锁: 顾名思义,就是很悲观的意思,认为数据随时都可能会被修改,所以每次操作...
  • 数据库机制

    万次阅读 2016-08-19 10:53:48
    数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。下面举例说明并发操作带来的数据不一致性问题: 现有两处火车票售票点,同时...
  • MySQL数据库 --- 六种分类 - 14种详细介绍

    千次阅读 热门讨论 2020-12-04 13:47:42
    是计算机协调多个进程或线程并发访问某一个资源的机制,在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所在有数据库必须...
  • 数据库锁机制

    万次阅读 多人点赞 2016-08-15 12:38:50
    数据库大并发操作要考虑死锁和的性能问题。看到网上大多语焉不详(尤其更新),所以这里做个简明解释,为下面描述方便,这里用T1代表一个数据库执行请求,T2代表另一个请求,也可以理解为T1为一个线程,T2 为另一...
  • 事务的隔离级别为:可重复读时, 如果有索引(包括主键索引),以索引列为条件更新数据,会存在间隙,行锁,页,而住一些行。 如果没有索引,更新数据时会住整张表。 ...
  • 数据库锁

    2015-11-21 14:11:57
    详细讲解数据库锁的种类以及作用
  • 数据库锁的粒度

    千次阅读 2014-10-10 18:50:35
    所谓粒度,即细化的程度。的粒度越大,则并发性越低且开销大;的粒度越小,则并发性高且开销小。
  • 共享(S):多个事务可封锁一个共享页;任何事务都不能修改该页; 通常是该页被读取完毕,S立即被释放。...当被读取的页将要被更新时,则升级为X;U一直到事务结束时才能被释放。 详细解...
  • 数据库事务与详解

    万次阅读 多人点赞 2016-09-07 16:31:12
    什么是事务(Transaction)?是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组...
  • MySQL数据库的全局和表锁

    万次阅读 2019-07-25 18:43:43
    数据库锁设计的初衷是处理并发问题。作为多用户共享的资源,当出现并发访问的时候,数据库需要合理地控制资源的访问规则。而锁就是用来实现这些访问规则的重要数据结构。 2、锁的分类 根据加锁的范围,MySQL里面...
  • 数据库 机制

    千次阅读 2019-02-23 09:45:13
    S一般用于表级,事务T对数据对象A加上S,则事务T可以读A但是不能修改它,而在T释放S之前,其他事务只能对A再加S,而不能再加X,这就保证了在事务T释放A上的共享之前,获取的其他事务只能对A读而不能...
  • 数据库隔离级别与数据库锁

    千次阅读 2018-01-04 15:02:48
    数据库事务隔离级别与数据库锁: 在数据库中,存在四种数据库隔离级别,分别是 1、read uncommited 在当前事务未提交的时候其他事务能够读到当前事务写的内容,这种隔离级别会导致脏读。其具体的加锁方式是在事务...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 61,274
精华内容 24,509
关键字:

数据库锁升级是什么