关于Mysql锁的分类

120 阅读4分钟

1.Mysql锁的分类

并发事务中,'读-读'的情况一般不会引起什么问题,一般需要解决的问题在于'写-写'和'读-写'/'写-读'引起的一些问题,主要有两种方式解决:加锁或者MVCC

  • 按操作方式
    • 读锁(共享锁) S(Share Lock)
    • 写锁(排他锁) X(Exclusive Lock)
  • 按锁粒度
    • 全局锁
    • 表级锁
      • 表级别的共享锁和排它锁
      • 意向锁
      • 自增锁
      • 元数据锁
    • 页级锁
    • 行级锁
      • 记录锁
      • 间隙锁
      • 临建锁
      • 插入意向锁
  • 按设计思想
    • 乐观锁
    • 悲观锁
  • 按加锁方式
    • 隐式锁
    • 显式锁

共享锁(读锁)和排他锁(写锁) 共享锁: 对于同一个数据,多个事务对其同时进行操作,不会互相影响,相互不会阻塞。 排他锁: 当前写操作没有完成之前,会阻断其他的读锁和写锁。

总结: 不同事务中,只有都是共享锁才互不影响不会阻塞,只要有一个排他锁,都会阻塞,对于存储引擎InnoDB来说,共享锁和排他锁可以加在表数据上,也可以加在行数据上。


如何加锁:

加共享锁:SQL语句 + "LOCK IN SHARE MODE" / "FOR SHARE"; 加排他锁:SQL语句 + "FOR UPDATE";

读操作(select)可以加共享锁和排他锁,写操作(insert、update、delete)只能加排他锁。


全局锁 / 表级锁 / 页级锁 / 行级锁

全局锁

  1. 全局锁就是将整个数据库实例加锁,加锁后整个数据库实例就处于只读状态。后续的操作的事务提交的语句都会被阻塞。
  2. 使用场景: 做全库的逻辑备份,对所有的表进行定,从而保证数据的一致性
  3. 添加/释放全局锁:
FLUSH TABLES WITH READ LOCK;
UNLOCK TABLES;

表级锁:

表级锁是对当前操作的整张表进行加锁,优点是资源消耗比较小,而且MyISAM 与 InnoDB存储引擎都支持表锁。

页级锁:

页级锁是介于表锁和行锁之间的一种锁,表锁速度快,冲突多;行锁冲突少但是速度慢,所以取了折中的页级锁,一次锁定相邻的一组数据。

行级锁:

行级锁表⽰只针对当前操作⾏进⾏加锁。⾏级锁能⼤⼤减少数据库操作的冲突。其加锁粒度最⼩,但加锁的开销也最⼤。

乐观锁 / 悲观锁

乐观锁:

乐观锁是比较乐观态度的锁。在操作数据时,认为别的线程不会同时修改数据,只有到数据提交的时候才通过一种机制来验证数据是否存在冲突。一般使用CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量。

悲观锁:

悲观锁是比较悲观态度的锁。每次去拿数据的时候都认为数据会修改,所以每次在拿数据的时候都会上锁,这样其他线程想拿这个数据就会阻塞直到它拿到锁。比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。在Java中,synchronized从偏向锁、轻量级锁到重量级锁,全是悲观锁。JDK提供的Lock实现类全是悲观锁。一般用于多写的场景。

隐式锁 / 显示锁

隐式锁:

隐式锁中又分为同步代码块同步方法,但是都是基于Synchronized关键字来实现的,因为他只需要使用关键字就可以,不用显示加锁和解锁的过程,所以称之为隐式锁。

同步代码块: 在代码块前面加上Synchroized关键字,并在后面的小括号内传入锁对象,这里的this是指定了当前的这个对象作为锁,注意任何对象都是可以作为锁来使用的。使用同步代码块时,多个线程的锁对象必须唯一

synchronized(锁对象){
}

同步方法: 同步方法就是将Synchroized关键字方放在方法的返回值前面,这里的锁就是也就是this,谁调用它,谁就是他的锁。同步方法也是采用锁对象来执行的,当这个方法不是静态办法,那这个锁对象就是this,当这个方法是静态方法,锁对象就是xxx.class。即谁调用该方法谁就是锁对象。

public synchronized void 方法名(){
}

显示锁:

显式锁在使用的时候程序员可以控制上锁和解锁,所以称之为显式锁。Lock是一个接口,提供了无条件的、可轮询的、定时的、可中断的锁获取操作,所有的加锁和解锁操作方法都是显示的。实现类常见的有:

  • ReentrantLock(重入锁)
import java.util.concurrent.locks.ReentrantLock; 

public class DisplayLockExample { 
    private ReentrantLock lock = new ReentrantLock(); 
    public void doSomething() { 
        lock.lock();
        try { 
            // 执行需要同步访问的代码块 
        } finally { 
            lock.unlock(); 
        }
    } 
}

重入锁就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁。除此之外,该锁的还支持获取锁时的公平和非公平性选择。

  • ReentrantReadWriteLock(读写锁)

importjava.util.concurrent.locks.ReentrantReadWriteLock; 

public class DisplayLockExample { 
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); 

    public void readData() { 
        lock.readLock().lock(); 
        try { 
        // 执行读取共享资源的操作 
        } finally { 
            lock.readLock().unlock(); 
        }
    } 
    public void writeData() { 
        lock.writeLock().lock(); 
        try {
            // 执行写入共享资源的操作 
        } finally { 
            lock.writeLock().unlock(); 
        }
    } 
}