精华内容
下载资源
问答
  • 锁机制
    千次阅读
    2021-12-09 15:31:36

    MySQL的锁机制


    首先对mysql锁进行划分:

    1. 按照锁的粒度划分:行锁、表锁、页锁
    2. 按照锁的使用方式划分:共享锁、排它锁(悲观锁的一种实现)
    3. 还有两种思想上的锁:悲观锁、乐观锁。
    4. InnoDB中有几种行级锁类型:Record Lock、Gap Lock、Next-key Lock

    MySQL的锁机制最显著的特点是不同的存储引擎支持不同的锁机制。比如,MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking);BDB存储引擎采用的是页面锁(page-level locking),但也支持表级锁;InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。

    1.行锁

    ​ 行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。有可能会出现死锁的情况。 行级锁按照使用方式分为共享锁和排他锁。

    共享锁用法(S锁 读锁):
    ​ 若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。

    select ... lock in share mode;
    

    共享锁就是允许多个线程同时获取一个锁,一个锁可以同时被多个线程拥有。

    排它锁用法(X 锁 写锁):
    ​ 若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。这保证了其他事务在T释放A上的锁之前不能再读取和修改A。

    select ... for update
    

    排它锁,也称作独占锁,一个锁在某一时刻只能被一个线程占有,其它线程必须等待锁被释放之后才可能获取到锁。

    MySQL的行锁是在引擎层由各个引擎自己实现的。但并不是所有的引擎都支持行锁,比如 MyISAM引擎就不支持行锁。不支持行锁意味着并发控制只能使用表锁,对于这种引擎的表,同 一张表上任何时刻只能有一个更新在执行,这就会影响到业务并发度。InnoDB是支持行锁的, 这也是MyISAM被InnoDB替代的重要原因之一。

    2.表锁

    表级锁是mysql锁中粒度最大的一种锁,表示当前的操作对整张表加锁,资源开销比行锁少,不会出现死锁的情况,但是发生锁冲突的概率很大,因为同 一张表上任何时刻只能有一个更新在执行。被大部分的mysql引擎支持,MyISAM和InnoDB都支持表级锁,但是InnoDB默认的是行级锁

    表级锁按照使用方式也可分为共享锁和排他锁。
    共享锁用法:

    LOCK TABLE table_name [ AS alias_name ] READ
    

    排它锁用法:

    LOCK TABLE table_name [AS alias_name][ LOW_PRIORITY ] WRITE
    

    解锁用法:

    unlock tables;
    

    3.页锁

    页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。BDB存储引擎支持页级锁

    全局锁
    除了行锁,表锁,页锁外,这里还补充一种全局锁,这种锁的范围最大。
    全局锁就是对整个数据库实例加锁。MySQL提供了一个加全局读锁的方法,命令是 Flush tables with read lock (FTWRL)。当你需要让整个库处于只读状态的时候,可以使用这个命 令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括 建表、修改表结构等)和更新类事务的提交语句。

    全局锁的典型使用场景是,做全库逻辑备份。 全 也就是把整库每个表都select出来存成文本。

    4.乐观锁和悲观锁

    4.1悲观锁

    在关系数据库管理系统里,悲观并发控制又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作对某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。

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

    mysql中实现悲观锁的具体流程:

    在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)
    如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。 具体响应方式由开发者根据实际需要决定。
    如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。
    其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。

    总而言之就是一句话:mysql中悲观锁的实现是通过排他锁来实现的

    在mysql(InnoDB)中使用悲观锁:

    1.开始事务
    begin;/begin work;/start transaction; (三者选一就可以)
    2.查询出商品信息
    select ... for update;(这里是使用的行锁的排他锁)
    4.提交事务
    commit;/commit work;
    

    通过下面的例子来说明:
    1.当你手动加上排它锁,但是并没有关闭mysql中的autocommit。

    1.这里先给user表所有的行加上行锁的排他锁
    mysql> select * from user for update;
    +----+------+--------+
    | id | name | psword |
    +----+------+--------+
    |  1 | a    | 1      |
    |  2 | b    | 2      |
    |  3 | c    | 3      |
    +----+------+--------+
    3 rows in set
    
    2.再对加了排他锁的行更改数据时,这里他会一直提示Unknown
    mysql> update user set name=aa where id=1;
    1054 - Unknown column 'aa' in 'field list'
    mysql> insert into user values(4,d,4);
    1054 - Unknown column 'd' in 'field list'
    
    

    2.关闭mysql中的autocommit后的正常流程

    窗口1:
    mysql> set autocommit=0;(先关闭mysql中的autocommit)
    Query OK, 0 rows affected
    我这里锁的是表中的所有行
    mysql> select * from user for update;
    +----+-------+
    | id | price |
    +----+-------+
    |  1 |   500 |
    |  2 |   800 |
    +----+-------+
    2 rows in set
    
    窗口2:
    mysql> update user set price=price-100 where id=1;
    执行上面操作的时候,会显示等待状态,一直到窗口1执行commit提交事务才会出现下面的显示结果
    Database changed
    Rows matched: 1  Changed: 1  Warnings: 0
    
    窗口1:执行commit手动提交事务
    mysql> commit;
    Query OK, 0 rows affected
    再查询一下user表,发现已经执行了窗口2的更新操作
    mysql> select * from user;
    +----+-------+
    | id | price |
    +----+-------+
    |  1 |   400 |
    |  2 |   800 |
    +----+-------+
    2 rows in set
    

    上面的例子展示了排它锁的原理:一个锁在某一时刻只能被一个线程占有,其它线程必须等待锁被释放之后才可能获取到锁或者进行数据的操作。

    悲观锁的优点和不足:
    悲观锁实际上是采取了“先取锁在访问”的策略,为数据的处理安全提供了保证,但是在效率方面,由于额外的加锁机制产生了额外的开销,并且增加了死锁的机会。并且降低了并发性;当一个事务加锁一行数据的时候,其他事务必须等待该事务提交之后,才能操作这行数据。

    4.2乐观锁

    在关系数据库管理系统里,乐观并发控制又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。

    乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。

    相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制一般的实现乐观锁的方式就是记录数据版本。

    mysql实现乐观锁一般来说有2种方式:
    1.使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。
    一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。
    当提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,就进行更新操作,否则认为是过期数据,正在提交的事务会进行回滚。
    2.第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致就更新,否则就是版本冲突。

    乐观锁的优点和不足:
    ​ 乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题。

    乐观锁和MVCC的区别?
    在数据库中,并发控制是指在多个用户/进程/线程同时对数据库进行操作时,如何保证事务的一致性和隔离性的,同时最大程度地并发。

    当多个用户/进程/线程同时对数据库进行操作时,会出现3种冲突情形:

    1. 读-读,不存在任何问题
    2. 读-写,有隔离性问题,可能遇到脏读(会读到未提交的数据) ,幻影读等。
    3. 写-写,可能丢失更新

    要解决冲突,一种办法是是锁,即基于锁的并发控制,比如2PL两阶段锁协议,这种方式开销比较高,而且无法避免死锁。而基于无锁的并发控制有两种方式:就是MVCC多版本并发控制和OCC乐观并发控制,这两种方式分别解决上面的第2,3种情况。

    多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。 这样在读操作不用阻塞写操作,写操作不用阻塞读操作的同时,避免了脏读和不可重复读

    乐观并发控制(OCC)是一种用来解决写-写冲突的无锁并发控制,认为事务间争用没有那么多,所以先进行修改,在提交事务前,检查一下事务开始后,有没有新提交改变,如果没有就提交,如果有就放弃并重试。乐观并发控制类似自旋锁。乐观并发控制适用于低数据争用,写冲突比较少的环境。

    多版本并发控制可以结合基于锁的并发控制来解决写-写冲突,即MVCC+2PL,也可以结合乐观并发控制来解决写-写冲突。

    5.1InnoDB锁的特性

    1. 在不通过索引条件查询的时候,InnoDB使用的确实是表锁(锁的是整张表)!
    2. 由于 MySQL 的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。
    3. 当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论 是使用主键索引、唯一索引或普通索引,InnoDB都会使用行锁来对数据加锁。
    4. 即便在条件中使用了索引字段,但是否使用索引来检索数据是由 MySQL 通过判断不同 执行计划的代价来决定的,如果 MySQL 认为全表扫效率更高,比如对一些很小的表,它 就不会使用索引,这种情况下 InnoDB 将使用表锁,而不是行锁。因此,在分析锁冲突时, 别忘了检查SQL 的执行计划(explain查看),以确认是否真正使用了索引。

    1.通过非索引项检索数据,加表锁!

    price属性并没有加索引,因此这时候虽然是用的行锁,锁的却是整张表
    窗口1:
    mysql> select * from product where price=88 for update;
    +----+------+-------+-----+
    | id | name | price | num |
    +----+------+-------+-----+
    |  2 | 蒙牛 |    88 |   1 |
    +----+------+-------+-----+
    
    窗口2:
    mysql> update product set price=price-100 where id=6;
    这里会等待,直到窗口1 commit后显示下面结果!
    Query OK, 1 row affected
    Rows matched: 1  Changed: 1  Warnings: 0
    

    2.使用相同索引值但是不同行引发的冲突

    这里的num属性 加上了普通索引,price属性并没有索引
    窗口1:
    mysql> set autocommit=0;
    Query OK, 0 rows affected
    
    mysql> select * from product where num=1 and price=68 for update;
    +----+------+-------+-----+
    | id | name | price | num |
    +----+------+-------+-----+
    |  1 | 伊利 |    68 |   1 |
    +----+------+-------+-----+
    
    窗口2:
    mysql> update product set price=price+100 where num=1 and price=88;
    这里会发生等待,直到窗口1 commit 显示下面结果
    Query OK, 1 row affected
    Rows matched: 1  Changed: 1  Warnings: 0
    mysql> select * from product;
    +----+----------+-------+-----+
    | id | name     | price | num |
    +----+----------+-------+-----+
    |  1 | 伊利     |    68 |   1 |
    |  2 | 蒙牛     |   188 |   1 |
    +----+----------+-------+-----+
    

    3.当使用索引检索数据时不同事务可以操作不同行数据

    锁一行数据,DML操作其他行并没有影响
    窗口1:
    mysql> select * from user where id=1 for update;
    +----+-------+
    | id | price |
    +----+-------+
    |  1 |   400 |
    +----+-------+
    
    窗口2:
    mysql> update user set price=price+100 where id=2;
    无需等待窗口1 commit
    Database changed
    Rows matched: 1  Changed: 1  Warnings: 0
    

    DML(Data Manipulation Language)数据操纵语言,对数据库中的数据进行一些简单操作,如insert、delete、update、select等。DML操作是可以手动控制事务的开启、提交和回滚的。
    DDL(Data Definition Language)数据定义语言,对数据库中的某些对象(例如database、table)进行管理,如create、alter和drop。DDL操作是隐性提交的,不能rollback。
    DDL有助于更改数据库的结构,而DML有助于管理数据库中的数据。

    参考:MySQL的锁机制和加锁原理

    更多相关内容
  • 本文实例讲述了MySQL锁机制与用法。分享给大家供大家参考,具体如下: MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。比如,MyISAM和MEMORY存储引擎采用的是表级锁;BDB存储引擎采用的是...
  • java锁机制Synchronized.pdf
  • MYSQL锁机制

    2018-07-26 19:23:56
    本文详细描述了MYSQL锁机制,掌握锁机制,对于避免死锁,提高软件的工作效率,有很大的帮助.
  • JUC多线程:synchronized锁机制原理 与 Lock锁机制

    万次阅读 多人点赞 2021-08-26 08:53:31
    synchronized 通过当前线程持有对象锁,从而拥有访问权限,而其他没有持有当前对象锁的线程无法拥有访问权限,保证在同一...synchronized 锁机制在 Java 虚拟机中的同步是基于进入和退出监视器锁对象 monitor 实现的

    前言:

            线程安全是并发编程中的重要关注点,造成线程安全问题的主要原因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据。因此为了解决这个问题,我们可能需要这样一个方案,当存在多个线程操作共享数据时,需要保证同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再进行,这种方式叫互斥锁,即能达到互斥访问目的的锁,也就是说当一个共享数据被当前正在访问的线程加上互斥锁后,在同一个时刻,其他线程只能处于等待的状态,直到当前线程处理完毕释放该锁。

    一、synchronized锁机制:

     1、synchronized 的作用:

             synchronized 通过当前线程持有对象锁,从而拥有访问权限,而其他没有持有当前对象锁的线程无法拥有访问权限,保证在同一时刻,只有一个线程可以执行某个方法或者某个代码块,从而保证线程安全。synchronized 可以保证线程的可见性,synchronized 属于隐式锁,锁的持有与释放都是隐式的,我们无需干预。synchronized最主要的三种应用方式:

    • 修饰实例方法:作用于当前实例加锁,进入同步代码前要获得当前实例的锁
    • 修饰静态方法:作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
    • 修饰代码块:指定加锁对象,进入同步代码库前要获得给定对象的锁

    2、synchronized 底层语义原理:

            synchronized 锁机制在 Java 虚拟机中的同步是基于进入和退出监视器锁对象 monitor 实现的(无论是显示同步还是隐式同步都是如此),每个对象的对象头都关联着一个 monitor 对象,当一个 monitor 被某个线程持有后,它便处于锁定状态。在 HotSpot 虚拟机中,monitor 是由 ObjectMonitor 实现的,每个等待锁的线程都会被封装成 ObjectWaiter 对象,ObjectMonitor 中有两个集合,_WaitSet 和 _EntryList,用来保存 ObjectWaiter 对象列表 ,owner 区域指向持有 ObjectMonitor 对象的线程。当多个线程同时访问一段同步代码时,首先会进入 _EntryList 集合尝试获取 moniter,当线程获取到对象的 monitor 后进入 _Owner 区域并把 _owner 变量设置为当前线程,同时 monitor 中的计数器 count 加1;若线程调用 wait() 方法,将释放当前持有的 monitor,count自减1,owner 变量恢复为 null,同时该线程进入 _WaitSet 集合中等待被唤醒。若当前线程执行完毕也将释放 monitor 并复位变量的值,以便其他线程获取 monitor。如下图所示:

    _EntryList:存储处于 Blocked 状态的 ObjectWaiter 对象列表。
    _WaitSet:存储处于 wait 状态的 ObjectWaiter 对象列表。

    3、 synchronized 的显式同步与隐式同步:

            synchronized 分为显式同步(同步代码块)和隐式同步(同步方法),显式同步指的是有明确的 monitorenter 和 monitorexit 指令,而隐式同步并不是由 monitorenter 和 monitorexit 指令来实现同步的,而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的。

    3.1、synchronized 代码块底层原理:

            synchronized 同步语句块的实现是显式同步的,通过 monitorenter 和 monitorexit 指令实现,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置,当执行 monitorenter 指令时,当前线程将尝试获取 objectref(即对象锁)所对应的 monitor 的持有权:

    • 当对象锁的 monitor 的进入计数器为 0,那线程可以成功取得 monitor,并将计数器值设置为 1,取锁成功。
    • 如果当前线程已经拥有对象锁的 monitor 的持有权,那它可以重入这个 monitor,重入时计数器的值也会加1。
    • 若其他线程已经拥有对象锁的 monitor 的所有权,那当前线程将被阻塞,直到正在执行线程执行完毕,即monitorexit 指令被执行,执行线程将释放 monitor 并设置计数器值为0,其他线程将有机会持有 monitor。

            编译器会确保无论方法通过何种方式完成,无论是正常结束还是异常结束,代码中调用过的每条 monitorenter 指令都有执行其对应 monitorexit 指令。为了保证在方法异常完成时,monitorenter 和 monitorexit 指令依然可以正确配对执行,编译器会自动产生一个异常处理器,这个异常处理器可处理所有的异常,它的目的就是用来执行 monitorexit 指令。

    3.2、synchronized 方法底层原理:

            synchronized 同步方法的实现是隐式的,无需通过字节码指令来控制,它是在方法调用和返回操作之中实现。JVM 可以通过方法常量池中的方法表结构(method_info Structure)中的 ACC_SYNCHRONIZED 访问标志 判断一个方法是否同步方法。当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,标识该方法是一个同步方法,执行线程将先持有 monitor, 然后再执行方法,最后再方法完成(无论是正常完成还是非正常完成)时释放 monitor。在方法执行期间,执行线程持有了 monitor,其他任何线程都无法再获得同一个 monitor。

            如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理此异常,那这个同步方法所持有的 monitor 将在异常抛到同步方法之外时自动释放。

     4、JVM 对 synchronized 锁的优化

            在早期版本中,synchronized 属于重量级锁,效率低下,因为监视器锁 monitor 是依赖于操作系统的 Mutex 互斥量来实现的,操作系统实现线程之间的切换时需要从用户态转换到核心态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高。在 JDK6 之后,synchronized 在 JVM 层面做了优化,减少锁的获取和释放所带来的性能消耗,主要优化方向有以下几点:

    4.1、锁升级:偏向锁->轻量级锁->自旋锁->重量级锁

            锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁,但是锁的升级是单向的,只能从低到高升级,不会出现锁的降级。重量级锁基于从操作系统的互斥量实现的,而偏向锁与轻量级锁不同,他们是通过 CAS 并配合 Mark Word 一起实现的。

    4.1.1、synchronized 的 Mark word 标志位:

            synchronized 使用的锁对象是存储在 Java 对象头里的,那么 Java 对象头是什么呢?对象实例分为:

    • 对象头
      • Mark Word
      • 指向类的指针
      • 数组长度
    • 实例数据
    • 对齐填充

    其中,Mark Word 记录了对象的 hashcode、分代年龄、锁标记位相关的信息,由于对象头的信息是与对象自身定义的数据没有关系的额外存储成本,因此考虑到 JVM 的空间效率,Mark Word 被设计成为一个非固定的数据结构,以便存储更多有效的数据,它会根据对象本身的状态复用自己的存储空间,在 32位 JVM 中的长度是 32 位,具体信息如下图所示:

    4.1.2、锁升级过程:

    (1)偏向锁:如果一个线程获得了锁,那么进入偏向模式,当这个线程再次请求锁的时候,只需去对象头的 Mark Word 中判断偏向线程ID是否指向它自己,无需再进入 monitor 中去竞争对象,这样就省去了大量锁申请的操作,适用于连续多次都是同一个线程申请相同的锁的场景。偏向锁只有初始化的时候需要一次 CAS 操作,但如果出现其他线程竞争锁资源,那么偏向锁就会被撤销,并升级为轻量级锁。

    (2)轻量级锁:不需要申请互斥量,允许短时间内的锁竞争,每次申请、释放锁都至少需要一次 CAS,适用于多个线程交替执行同步代码块的场景

    (3)自旋锁:自旋锁假设在不久将来,当前的线程可以获得锁,因此在轻量级锁升级成为重量级锁之前,虚拟机会让当前想要获取锁的线程做几个空循环,在经过若干次循环后,如果得到锁,就顺利进入临界区,如果还不能获得锁,那就会将线程在操作系统层面挂起。

    这种方式确实可以提升效率的,但是当线程越来越多竞争很激烈时,占用 CPU 的时间变长会导致性能急剧下降,因此 JVM 对于自旋锁有一定的次数限制,可能是50或者100次循环后就放弃,直接挂起线程,让出CPU资源。

    (4)自适应自旋锁:自适应自旋解决的是 “锁竞争时间不确定” 的问题,自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。

    • 如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间,比如100个循环。
    • 相反的,如果对于某个锁,自旋很少成功获得过,那在以后要获取这个锁时将可能减少自旋时间甚至省略自旋过程,以避免浪费处理器资源。

    但自旋锁带来的副作用就是不公平的锁机制:处于阻塞状态的线程,并没有办法立刻竞争被释放的锁。然而,处于自旋状态的线程,则很有可能优先获得这把锁。

    (5)重量级锁:适用于多个线程同时执行同步代码块的场景,且锁竞争时间长。在这个状态下,未抢到锁的线程都会进入到 Monitor 中并阻塞在 _EntryList 集合中(具体的争夺锁的原理见该部分的第2点:synchronized 底层语义原理)。

     锁升级过程详细解析推荐阅读:https://juejin.cn/post/6888137809929093133

    4.2、锁消除:

            消除锁属于编译器对锁的优化,JIT 编译时(可以简单理解为当某段代码即将第一次被执行时进行编译,又称即时编译)会使用逃逸分析技术,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过这种方式消除没有必要的锁,可以节省毫无意义的请求锁时间。

    4.3、锁粗化:

            JIT 编译器动态编译时,如果发现几个相邻的同步块使用的是同一个锁实例,那么 JIT 编译器将会把这几个同步块合并为一个大的同步块,从而避免一个线程“反复申请、释放同一个锁“所带来的性能开销。

    5、偏向锁的废除:

            在 JDK6 中引入的偏向锁能够减少竞争锁定的开销,使得 JVM 的性能得到了显著改善,但是 JDK15 却将决定将偏向锁禁用,并在以后删除它,这是为什么呢?主要有以下几个原因:

    • 为了支持偏向锁使得代码复杂度大幅度提升,并且对 HotSpot 的其他组件产生了影响,这种复杂性已成为理解代码的障碍,也阻碍了对同步系统进行重构
    • 在更高的 JDK 版本中针对多线程场景推出了性能更高的并发数据结构,所以过去看到的性能提升,在现在看来已经不那么明显了。
    • 围绕线程池队列和工作线程构建的应用程序,性能通常在禁用偏向锁的情况下变得更好。

    二、Lock 锁机制:

            讲到 Synchronized 锁机制,肯定离不开的话题就是 Lock 的锁机制,那这里我们就简单介绍下 Lock 锁机制。

    1、Lock 锁是什么:

            Lock 锁其实指的是 JDK5 之后在 JUC 中引入的 Lock 接口,该接口中只有6个方法的声明,对于实现该接口的所有锁可以称为 Lock 锁。Lock 锁是显式锁,锁的持有与释放都必须手动编写,当前线程使用 lock() 方法与 unlock() 对临界区进行加锁与释放锁,当前线程获取到锁之后,其他线程由于无法持有锁将无法进入临界区,直到当前线程释放锁,unlock() 操作必须在 finally 代码块中,这样可以确保即使临界区执行抛出异常,线程最终也能正常释放锁。

    2、ReentrantLock 重入锁:

            ReentrantLock 重入锁是基于 AQS 框架并实现了 Lock 接口,支持一个线程对资源重复加锁,作用与 synchronized 锁机制相当,但比 synchronized 更加灵活,同时也支持公平锁和非公平锁。

    对AQS抽象队列同步器原理感兴趣的读者可以阅读这篇文章:https://blog.csdn.net/a745233700/article/details/120668889

    2.1、ReentrantLock 与 synchronized 的区别:

    (1)使用的区别:synchronized 是 Java 的关键字,是隐式锁,依赖于 JVM 实现,当 synchronized 方法或者代码块执行完之后,JVM 会自动让线程释放对锁的占用;ReentrantLock 依赖于 API,是显式锁,需要 lock() 和 unlock() 方法配合 try/finally 语句块来完成。在发生异常时,JVM 会自动释放 synchronized 锁,因此不会导致死锁;而 ReentrantLock 在发生异常时,如果没有主动通过 unLock() 去释放锁,则很可能造成死锁现象,这也是 unLock() 语句必须写在 finally 语句块的原因。

    (2)功能的区别:ReentrantLock 相比于 synchronzied 更加灵活, 除了拥有 synchronzied 的所有功能外,还提供了其他特性:

    • ReentrantLock 可以实现公平锁,而 synchronized 不能保证公平性。

    • ReentrantLock 可以知道有没有成功获取锁(tryLock),而 synchronized 不支持该功能

    • ReentrantLock 可以让等待锁的线程响应中断,而使用 synchronized 时,等待的线程不能够响应中断,会一直等待下去;

    • ReentrantLock 可以基于 Condition 实现多条件的等待唤醒机制,而如果使用 synchronized,则只能有一个等待队列

    (3)性能的区别:在 JDK6 以前,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时,此时 ReentrantLock 的性能要远远优于 synchronizsed。但是在 JDK6 及以后的版本,JVM 对 synchronized 进行了优化,所以两者的性能变得差不多了

            总的来说,synchronizsed 和 ReentrantLock 都是可重入锁,在使用选择上需要根据具体场景而定,大部分情况下依然建议使用 synchronized 关键字,原因之一是使用方便语义清晰,二是性能上虚拟机已为我们自动优化。如果确实需要使用到 ReentrantLock 提供的多样化特性时,我们可以选择ReentrantLock

    “可重入锁”概念是:自己可以再次获取自己的内部锁。比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可重入的话,就会造成死锁。同一个线程每次获取锁,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁。

    3、ReadWriteLock 读写锁:

            ReentrantLock 某些时候有局限,如果使用 ReentrantLock,主要是为了防止线程A在写数据、线程B在读数据造成的数据不一致,但如果线程C在读数据、线程D也在读数据,由于读数据是不会改变数据内容的,所以就没有必要加锁,但如果使用了 ReentrantLock,那么还是加锁了,反而降低了程序的性能,因此诞生了读写锁 ReadWriteLock。ReadWriteLock 是一个接口,而 ReentrantReadWriteLock 是 ReadWriteLock 接口的具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和写之间才会互斥,提升了读写的性能。

    参考文章:https://blog.csdn.net/javazejian/article/details/72828483

    展开全文
  • 数据库锁机制

    千次阅读 2022-04-04 15:31:30
    这些问题的本质都是数据库的多事务并发问题,为了解决多事务并发问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制隔离机制,用一整套机制来解决多事务并发问题。 锁是计算机协调多个进程或线程并发访问...

    目录

    1、表锁和行锁

    2、间隙锁(Gap Lock)和临键锁(Next-key Locks)

    3、锁机制分析


    数据库一般都会并发执行多个事务,多个事务可能会并发的对相同的一批数据进行增删改查操作,可能就会导致我们说的脏写、脏读、不可重复读、幻读这些问题。这些问题的本质都是数据库的多事务并发问题,为了解决多事务并发问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制隔离机制,用一整套机制来解决多事务并发问题。

    是计算机协调多个进程或线程并发访问某一资源的机制

    在数据库中,除了传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供需要用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。

    1、表锁和行锁

    (1)从性能上分为乐观锁(用版本对比来实现)和悲观锁

    (2)从对数据库操作的类型分,分为读锁写锁(都属于悲观锁)

    读锁(共享锁,S锁(Shared)):针对同一份数据,多个读操作可以同时进行而不会互相影响

    写锁(排它锁,X锁(eXclusive)):当前写操作没有完成前,它会阻断其他写锁和读锁

    (3)从对数据操作的粒度分,分为表锁行锁

    表锁:每次操作锁住整张表。开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;一般用在整表数据迁移的场景。

    行锁:每次操作锁住一行数据。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高。

    表锁案例分析

    --建表SQL
    CREATE TABLE `mylock` (
    	`id` INT (11) NOT NULL AUTO_INCREMENT,
    	`NAME` VARCHAR (20) DEFAULT NULL,
    	PRIMARY KEY (`id`)
    ) ENGINE = MyISAM DEFAULT CHARSET = utf8;
    
    --插入数据
    INSERT INTO mylock (`id`, `NAME`) VALUES ('1', 'a');
    INSERT INTO mylock (`id`, `NAME`) VALUES ('2', 'b');
    INSERT INTO mylock (`id`, `NAME`) VALUES ('3', 'c');
    INSERT INTO mylock (`id`, `NAME`) VALUES ('4', 'd');

    操作命令

    --手动增加表锁
    lock table 表名称 read(write),表名称2 read(write);
    
    --查看表上加过的锁
    show open tables;
    
    --删除表锁
    unlock tables;

    加读锁:当前session和其他session都可以读该表,当前session中插入或者更新锁定的表都会报错,其他session插入或更新则会等待。

    加写锁:当前session对该表的增删改查都没有问题,其他session对该表的所有操作被阻塞。

    案例结论

    1、对MyISAM表的读操作(加读锁) ,不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。

    2、对MylSAM表的写操作(加写锁) ,会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作。

    行锁案例分析

    InnoDB与MYISAM的最大不同有两点:

    (1)InnoDB支持事务(TRANSACTION),(2)InnoDB支持行级锁

    --建表
    CREATE TABLE `account` (
      `id` INT (11) NOT NULL AUTO_INCREMENT,
    	`name` varchar(255) DEFAULT NULL,
    	`balance` int(11) DEFAULT NULL,
    	PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    --插入数据
    INSERT INTO `account` (`name`, `balance`) VALUES ('mike', '500');
    INSERT INTO `account` (`name`, `balance`) VALUES ('bob', '500');
    INSERT INTO `account` (`name`, `balance`) VALUES ('lucy', '500');

    行锁效果:一个session开启事务更新不提交,另一个session更新同一条记录会阻塞,更新不同记录不会阻塞

    总结:

    MyISAM在执行查询语句SELECT前,会自动给涉及的所有表加读锁,在执行update、insert、delete操作会自动给涉及的表加写锁。

    InnoDB在执行查询语句SELECT时(非串行隔离级别),不会加锁。但是update、insert、delete操作会加行锁。

    简而言之,就是读锁会阻塞写,但是不会阻塞读。而写锁则会把读和写都阻塞。

    2、间隙锁(Gap Lock)和临键锁(Next-key Locks)

    间隙锁,锁的就是两个值之间的空隙。Mysql默认级别是repeatable-read(可重复读),间隙锁在某些情况下可以解决幻读问题。

    如果account表里数据如下:

    那么间隙就有 id 为 (3,7),(7,16),(16,正无穷) 这三个区间。

    范围修改:如果在Session_1下面执行

    --范围修改
    update account set name = 'xiaoming' where id > 5 and id < 11;

    那么其他Session没法新增或修改id在(3,16]区间的任何一行数据,注意最后那个16也是包含在内的。

    间隙锁是在可重复读隔离级别下才会生效。

    临键锁(Next-key Locks)

    Next-Key Locks是行锁与间隙锁的组合。像上面那个例子里的这个(3,20]的整个区间可以叫做临键锁。

    3、锁机制分析

    (1)无索引行锁会升级为表锁(RR级别会升级为表锁,RC级别不会升级为表锁)

    锁主要是加在索引上,如果对非索引字段更新,行锁可能会变表锁。

    InnoDB的行锁是针对索引加的锁,不是针对记录加的锁。并且该索引不能失效,否则都会从行锁升级为表锁。

    锁定某一行还可以用 lock in share mode (共享锁) 和 for update(排它锁),例如:

    select * from account where id = 1 for update;

    结论:Innodb存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会要更高一下,但是在整体并发处理能力方面要远远优于MYISAM的表级锁定的。当系统并发量高的时候,Innodb的整体性能和MYISAM相比就会有比较明显的优势了。

    但是,Innodb的行级锁定同样也有其脆弱的一面,当我们使用不当的时候,可能会让Innodb的整体性能表现不仅不能比MYISAM高,甚至可能会更差。

    (2)行锁分析

    通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况

    show status like 'innodb_row_lock%';

    对各个状态量的说明如下:

    • Innodb_row_lock_current_waits: 当前正在等待锁定的数量
    • Innodb_row_lock_time: 从系统启动到现在锁定总时间长度(等待总时长)
    • Innodb_row_lock_time_avg: 每次等待所花平均时间(等待平均时长)
    • Innodb_row_lock_time_max:从系统启动到现在等待最长的一次所花时间
    • Innodb_row_lock_waits: 系统启动后到现在总共等待的次数(等待总次数)

    当等待次数很高,而且每次等待时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手制定优化计划。

    (3)查看INFORMATION_SCHEMA系统库锁相关数据表

    -- 查看事务
    select * from INFORMATION_SCHEMA.INNODB_TRX;
    
    -- 查看锁
    select * from INFORMATION_SCHEMA.INNODB_LOCKS;
    
    -- 查看锁等待
    select * from INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
    
    -- 释放锁,trx_mysql_thread_id可以从INNODB_TRX表里查看到
    kill trx_mysql_thread_id
    
    -- 查看锁等待详细信息
    show engine innodb status\G; 

    (4)锁优化建议

    • 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
    • 合理设计索引,尽量缩小锁的范围
    • 尽可能减少检索条件范围,避免间隙锁
    • 尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的sql尽量放在事务最后执行
    • 尽可能低级别事务隔离
    展开全文
  • 腾讯云数据库负责人林晓斌说过:“我们面试MySQL同事时只考察两点,索引和锁”。言简意赅,MySQL锁的重要性不言而喻。...本文通过同事“侨总”的一场面试,带你通俗易懂的掌握MySQL各种锁机制,希望可以帮到你!

    小伙伴想精准查找自己想看的MySQL文章?喏 → MySQL江湖路 | 专栏目录

      腾讯云数据库负责人林晓斌说过:“我们面试MySQL同事时只考察两点,索引和锁”。言简意赅,MySQL锁的重要性不言而喻。
      本文通过同事“侨总”的一场面试,带你通俗易懂的掌握MySQL各种锁机制,希望可以帮到你!近期会继续整理深入性的锁机制文章,有兴趣的老铁,记得关注一下,到时叫你❤️❤️~

      今天的主人公是我们公司同事侨总,传说中手上有10个比特币的男人。自从比特币大涨以来,养成了几个小爱好:周末听戏坐包厢,骑马酒吧滑雪场。

    在这里插入图片描述

      这不,前两天侨总又双叒叕出来体验面试了,晚上请我烧烤时跟我聊了聊这次有趣的面试经历,真是意犹未尽,趁他回味之余我又吃了十几串儿腰子和羊肉~ 嗯,真香!

      对不住,跑题了。。人到中年嘛,保温杯里泡枸杞之余总会。。。

      来不及解释了,快上车!

    大家好,我是陈哈哈的同事“侨总”,领导一般不敢喊我名字,都叫我小侨~

    下面是我的一次面试经历,面试官是技术经理和HR,大家吃好喝好~

    在这里插入图片描述

    侨总:马…小马哥好!

    面试官:你好,小侨啊,看你简历写着精通MySQL锁,你认为精通应该是啥水平呢?

    侨总:马哥我是个老实人,我认为精通就是,比面试官知道的多就完了。。

    面试官:(??怎么有种似曾相识的感觉?《听我讲完redo log、binlog原理,面试官老脸一红》

    面试官:行,那你先给我说说MySQL设计这个锁是干啥用的呀?

    侨总:数据库锁设计的初衷是处理并发问题。作为多用户共享的资源,当出现并发访问的时候,为了保证数据的一致性,数据库需要合理地控制资源的访问规则。而锁就是用来实现这些访问规则的重要机制。

      简单说,数据表就像公共厕所。emmm…换个下饭的说法,数据表就好比您开的一家酒店,而每行数据就像酒店的房间,如果大家随意进出,就会出现多人抢夺同一个房间的情况,而在房间上装上锁,申请到钥匙的人才可以入住并且将房间锁起来,其他人只有等他用完退房后才可以再次使用,这样保证了房间的一致性,方便酒店进行管理。

      MySQL锁机制的初衷便是如此,当然,MySQL数据库由于其自身架构的特点,存在多种数据存储引擎,每种存储引擎所针对的应用场景特点都不太一样,为了满足各自特定应用场景的需求,每种存储引擎的锁定机制都是为各自所面对的特定场景而优化设计,所以各存储引擎的锁定机制也有较大区别。

    面试官:嗯,那你说一下MySQL都分为哪些锁。

    在这里插入图片描述

    侨总

    1. 按锁粒度从大到小分类:表锁页锁行锁;以及特殊场景下使用的全局锁

    2. 如果按锁级别分类则有:共享(读)锁排他(写)锁意向共享(读)锁意向排他(写)锁

    3. 以及Innodb引擎为解决幻读等并发场景下事务存在的数据问题,引入的Record Lock(行记录锁)Gap Lock(间隙锁)Next-key Lock(Record Lock + Gap Lock结合)等;

    4. 还有就是我们面向编程的两种锁思想:悲观锁、乐观锁。

    面试官:袁芳你怎么看?

    HR小姐姐:。。。

    面试官:小侨啊,那你来谈一谈你对表锁、行锁的理解吧。

    表锁

    侨总:表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特点是实现逻辑非常简单,带来的系统负面影响最小。所以获取锁和释放锁的速度很快。由于表级锁一次会将整个表锁定,所以可以很好的避免困扰我们的死锁问题。

      当然,锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高,大大降低并发度。

      使用表级锁定的主要是MyISAM,MEMORY,CSV等一些非事务性存储引擎。

    行锁

    侨总:与表锁正相反,行锁最大的特点就是锁定对象的颗粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力从而提高系统的整体性能。

      虽然能够在并发处理能力上面有较大的优势,但是行级锁定也因此带来了不少弊端。由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗自然也就更大了。此外,行级锁定也最容易发生死锁

      使用行级锁定的主要是InnoDB存储引擎。

    • 适用场景:从锁的角度来说,表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用,如Web应用;而行级锁则更适合于有大量按索引条件并发更新数据的情况,同时又有并发查询的应用场景。

    页锁

      除了表锁、行锁外,MySQL还有一种相对偏中性的页级锁,页锁是MySQL中比较独特的一种锁定级别,在其他数据库管理软件中也并不是太常见。页级锁定的特点是锁定颗粒度介于行级锁定与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力也同样是介于上面二者之间。另外,页级锁定和行级锁定一样,会发生死锁。

      使用页级锁定的主要是BerkeleyDB存储引擎。

    面试官:那全局锁是什么时候用的呢?

    全局锁

    侨总:首先全局锁,是对整个数据库实例加锁。使用场景一般在全库逻辑备份时。

      MySQL提供加全局读锁的命令:Flush tables with read lock (FTWRL)

      这个命令可以使整个库处于只读状态。使用该命令之后,数据更新语句、数据定义语句和更新类事务的提交语句等修改数据库的操作都会被阻塞。

    风险:

    1. 如果在主库备份,在备份期间不能更新,业务停摆
    2. 如果在从库备份,备份期间不能执行主库同步的binlog,导致主从延迟同步

      还有一种锁全局的方式:set global readonly=true ,相当于将整个库设置成只读状态,但这种修改global配置量级较重,和全局锁不同的是:如果执行Flush tables with read lock 命令后,如果客户端发生异常断开,那么MySQL会自动释放这个全局锁,整个库回到可以正常更新的状态。但将库设置为readonly后,客户端发生异常断开,数据库依旧会保持readonly状态,会导致整个库长时间处于不可写状态,试想一下微信只能看,不能打字~~

    HR小姐姐:那微信不就完蛋了?

    侨总:是啊,抓紧找老实人背锅!

    面试官:不错,你把这几种锁的侧重点都表述清楚了。那你再说一下你对不同级别的那几种锁的使用场景和理解吧?

    侨总:MySQL基于锁级别又分为:共享(读)锁排他(写)锁意向共享(读)锁意向排他(写)锁

    共享(读)锁、排他(写)锁、意向共享(读)锁、意向排他(写)锁

    侨总:对于共享(读)锁排他(写)锁,比如咱们住酒店,入住前顾客都是有权看房的,只看不住想白嫖都是可以的,前台小姐姐会把门给你打开。当然,也允许不同的顾客一起看(共享 读),比如和这位杀马特小伙子。

    在这里插入图片描述

      看房时房间相当于公共场所,小姐姐嘱咐不能乱涂乱画,也不能偷喝免费的矿泉水。。如果你觉得不错,偷偷跑到前台要定这间房,交钱后会给你这个房间的钥匙并将房间状态改为已入住,不再允许其他人看房(排他 写)。

      对了,当办理入住时前台小姐姐也会通知看房的杀马特小伙子说这间房已经有人定了!!等看房的杀马特小伙儿骂骂咧咧出门后,看到满头大汗的你,鄙夷着咽了一口口水,咳tui!然后你锁上门哼着歌儿,开始干那些见不得人的事儿~~直到你退房前,其他人无法在看你的房

      可见,读锁是可以并发获取的(共享的),而写锁只能给一个事务处理(排他的)。当你想获取写锁时,需要等待之前的读锁都释放后方可加写锁;而当你想获取读锁时,只要数据没有被写锁锁住,你都可以获取到读锁,然后去看房。

      另外还有意向读\写锁,严格来说他们并不是一种锁,而是存放表中所有行锁的信息。就像我们在酒店,当我们预定一个房间时,就对该行(房间)添加 意向写锁,但是同时会在酒店的前台对该行(房间)做一个信息登记(旅客姓名、男女、住多长时间、家里几头牛等)。大家可以把意向锁当成这个酒店前台,它并不是真正意义上的锁(钥匙),它维护表中每行的加锁信息,是共用的。后续的旅客通过酒店前台来看哪个房间是可选的,那么,如果没有意图锁,会出现什么情况呢?假设我要住房间,那么我每次都要到每一个房间看看这个房间有没有住人,显然这样做的效率是很低下的。杀马特小伙儿表示支持!

      读写锁、意向锁的兼容性如下所示;

    锁类型读锁写锁意向读锁意向写锁
    读锁兼容冲突兼容冲突
    写锁冲突冲突冲突冲突
    意向读锁兼容冲突兼容兼容
    意向写锁冲突冲突兼容兼容

    侨总:再回到MySQL原理上讲

    1 共享(读)锁(Share Lock)

      共享锁,又叫读锁,是读取操作(SELECT)时创建的锁。其他用户可以并发读取数据,但在读锁未释放前,也就是查询事务结束前,任何事务都不能对数据进行修改(获取数据上的写锁),直到已释放所有读锁。

      如果事务A数据B(1024房)加上读锁后,则其他事务只能对数据B上加读锁,不能加写锁。获得读锁的事务只能读数据,不能修改数据。

    SQL显示加锁写法:

    SELECTLOCK IN SHARE MODE;
    

      在查询语句后面增加LOCK IN SHARE MODE,MySQL就会对查询结果中的每行都加读锁,当没有其他线程对查询结果集中的任何一行使用写锁时,可以成功申请读锁,否则会被阻塞。其他线程也可以读取使用了读锁的表,而且这些线程读取的是同一个版本的数据。

    2 排他(写)锁(Exclusive Lock)

      排他锁又称写锁、独占锁,如果事务A数据B加上写锁后,则其他事务不能再对数据B加任何类型的锁。获得写锁的事务既能读数据,又能修改数据

    SQL显示加锁写法:

    SELECTFOR UPDATE;
    

      在查询语句后面增加FOR UPDATE,MySQL 就会对查询结果中的每行都加写锁,当没有其他线程对查询结果集中的任何一行使用写锁时,可以成功申请写锁,否则会被阻塞。另外成功申请写锁后,也要先等待该事务前的读锁释放才能操作。

    3 意向锁(Intention Lock)

      意向锁属于表级锁,其设计目的主要是为了在一个事务中揭示下一行将要被请求锁的类型。InnoDB 中的两个表锁:

    • 意向共享锁(IS):表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的IS锁;

    • 意向排他锁(IX):类似上面,表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先取得该表的IX锁。

      意向锁是 InnoDB 自动加的,不需要用户干预。

      再强调一下,对于INSERT、UPDATE和DELETE,InnoDB 会自动给涉及的数据加排他锁;对于一般的SELECT语句,InnoDB 不会加任何锁,事务可以通过以下语句显式加共享锁或排他锁。

    共享锁:SELECT … LOCK IN SHARE MODE;
    排他锁:SELECT … FOR UPDATE;

    面试官:(这小子有两下子)嗯,袁芳你怎么看?

    HR:通俗易懂,我听懂了~~

    面试官:好,那最后一个问题,你上面提到了乐观锁和悲观锁,谈谈你对它的看法吧。

    侨总:其实悲观锁和乐观锁,也并不是 MySQL 或者数据库中独有的概念,而是并发编程的基本概念。主要区别在于,操作共享数据时,“悲观锁”即认为数据出现冲突的可能性更大,而“乐观锁”则是认为大部分情况不会出现冲突,进而决定是否采取排他性措施。

      反映到 MySQL 数据库应用开发中,悲观锁一般就是利用类似 SELECT … FOR UPDATE 这样的语句,对数据加锁,避免其他事务意外修改数据。乐观锁则与 Java 并发包中的 AtomicFieldUpdater 类似,也是利用 CAS 机制,并不会对数据加锁,而是通过对比数据的时间戳或者版本号,来实现乐观锁需要的版本判断

      MySQL的多版本并发控制 (MVCC),其本质就可以看作是种乐观锁机制,而排他性的读写锁、两阶段锁等则是悲观锁的实现。

    面试官:好,小侨我看你对MySQL锁这块儿确实研究得比较透彻,连HR都听懂了,还是让我比较满意的。

    面试官:你平时有什么爱好么?

    侨总:我除了周末听戏坐包厢,骑马酒吧滑雪场。就是喜欢炒比特币啦!

    面试官:哦,不好意思,我们公司反对炒比特币的行为,回去等通知吧。

    侨总:???
    在这里插入图片描述

      说着面试官马经理走出了会议室,HR小姐姐表示我哪壶不开提哪壶,马老板高点买的比特币,现在泡沫炸了,亏成马,老板都差点亏没了。

    展开全文
  • mysql的锁机制

    千次阅读 2022-04-05 17:39:58
    是计算机协调多个进程或线程并发访问某一资源的机制。 在数据库中,除传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库...
  • java锁机制详解

    千次阅读 2022-03-21 00:48:35
    java锁机制 1. 乐观锁 VS 悲观锁 ​ 悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。Java中,synchronized关键字和Lock的实现类都是悲观锁...
  • Java锁机制详解.pdf

    2017-11-02 17:05:48
    Java锁机制详解.pdf java线程 java多线程 Java锁机制详解.pdf java线程 java多线程
  • 锁机制

    千次阅读 2020-11-21 23:06:20
    目录理解 参考链接 常见的锁机制 理解 锁机制:一种保护机制,在多线程的情况下,保证操作数据的正确性/一致性
  • Java中的锁机制

    千次阅读 2021-06-07 20:57:14
    锁机制无处不在,锁机制是实现线程同步的基础,锁机制并不是Java锁独有的,其他各种计算机语言中也有着锁机制相关的实现,数据库中也有锁的相关内容。这篇文章就是从Java入手,深入学习、理解Java中的锁机制,提升...
  • 要说锁,应该追溯到操作系统中的多线程原理,锁机制在其中发挥着必不可少的作用,先抛出锁的定义 锁是计算机协调多个进程或线程并发访问某一资源的机制 在数据库中,除传统的计算资源(如CPU、RAM、I/O等)的争用...
  • MySQL锁机制

    千次阅读 2021-04-17 20:47:16
    是计算机协调多个线程或者线程并发访问某一资源的机制。 2、表锁(偏读) 2.1、表锁的基本操作 手动添加表锁:lock table 表名字 read | write,表名字2 read | write ...... 释放表锁:unlock tables 查看...
  • MySQL的锁机制,你真的理解了吗?

    千次阅读 2022-03-21 23:16:57
    MySQL的锁机制 MySQL的锁机制,你真的理解了吗? 我们都知道事务并发有可能导致脏写,脏读、不可重复读,幻读等问题,而这类问题归结起来可以分为以下三类(经典读写问题): “读-读”:两个事务都只是读取数据,...
  • SQL的锁机制

    2015-07-09 11:26:45
    SQL的锁机制,详细介绍sql Server的锁机制原理。
  • MySQL的锁机制和加锁原理

    万次阅读 多人点赞 2019-03-09 10:35:01
    MySQL的锁机制和加锁原理 文章目录MySQL的锁机制和加锁原理1.行锁2.表锁3.页锁4.乐观锁和悲观锁4.1悲观锁4.2乐观锁5.MySQL/InnoDB中的行锁和表锁问题5.1InnoDB锁的特性6.Record Lock、Gap Lock、Next-key Lock锁6.1...
  • 分布式锁机制是分布式文件系统的重要机制,Lustre是一种高性能的分布式文件系统,针对现有的范围锁机制,在处理多客户端大量并发的密集型共享文件访问时,存在操作延迟、文件数据一致性管理复杂的问题,为了解决这类...
  • MySQL中的锁机制详细说明

    千次阅读 多人点赞 2020-06-30 17:45:40
    一、MySQL锁机制起步 锁是计算机用以协调多个进程间并发访问同一共享资源的一种机制。MySQL中为了保证数据访问的一致性与有效性等功能,实现了锁机制,MySQL中的锁是在服务器层或者存储引擎层实现的。 二、行锁与...
  • MySQL事务处理与锁机制详解

    千次阅读 2019-05-22 03:39:27
    事务的基本概念、ACID特性、事务的隔离级别、提交与回滚、MyISAM表锁、InnoDB行锁、间隙(Next-Key
  • 从并发概念、场景分析出发,依次引出锁、等待队列等概念,直至分析清楚java锁机制实现的原理。并以java锁机制实现基类AbstractQueuedSynchronizer的实现为例,从类(核心属性、方法)设计思路,到对关键代码做注释...
  • MySQL的锁机制

    2016-06-04 12:36:20
    MySQL的锁、锁机制的解释。想看就看,勿过于勉励
  • Redis事务——锁机制

    千次阅读 2022-04-03 14:27:55
    (2)悲观锁机制:每次有人访问数据时,都会进行上锁,直到访问完才会解锁,其他用户才可以访问,如下图: (3)乐观锁机制:每次用户访问数据不会上锁,若数据有更新修改,会判断在此之前是否有其他人修改,若有,...
  • MySQL中锁机制的原理是什么

    千次阅读 2021-01-30 03:56:20
    MySQL中锁机制的原理是什么发布时间:2020-12-08 14:48:30来源:亿速云阅读:81作者:LeahMySQL中锁机制的原理是什么?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小...
  • 文章目录MySQL——锁机制和数据库并发问题解决方案1、锁机制概述2、MySQL并发事务访问相同数据情况2.1、读--读2.2、写--写2.3、读--写 或 写--读3、数据库并发问题解决方案 MySQL——锁机制和数据库并发问题解决方案...
  • 深入理解mysql事务隔离级别和锁机制

    千次阅读 2022-03-13 10:30:52
    其实本质上,出现出现这些问题的本质就是数据库的多事务并发问题,针对这些问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制机制等来解决并发问题。接下来,我会详细的介绍这些机制,让大家深入理解...
  • MySql各种锁机制的学习

    千次阅读 2021-12-03 00:30:20
    **是用于管理对公共资源的并发控制。** 也就是说在并发的情况下,会出现资源竞争,所以需要加锁。 加锁解决了 多用户环境下保证数据库完整性和一致性。 使用的对象是事务,事务用来锁定数据库的对象是表、页、...
  • mysql 事务操作与锁机制

    千次阅读 多人点赞 2022-03-21 11:01:18
    mysql 事务操作与锁机制 mysql 事务引入 mysql 事务具体的操作 mysql 的隔离级别 读未提交的脏读 读已提交引起的不可重复读 可重复读引起的幻读 串行化安全 锁引入 表级锁案例 读锁 写锁 行级锁案例 mysql 事务引入 ...
  • MySQL锁机制全面解析

    千次阅读 多人点赞 2021-09-28 20:40:16
    一、的作用 数据库使用是为了支持对共享资源的并发访问,同时保证数据的完整性和一致性。 二、的类型 2.1 全局 全局意味着对整个数据库实例加上。通常使用的是全局读——Flush tables with read ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 523,262
精华内容 209,304
关键字:

锁机制