1.Mysql锁的分类
并发事务中,'读-读'的情况一般不会引起什么问题,一般需要解决的问题在于'写-写'和'读-写'/'写-读'引起的一些问题,主要有两种方式解决:加锁或者MVCC
- 按操作方式
- 读锁(共享锁) S(Share Lock)
- 写锁(排他锁) X(Exclusive Lock)
- 按锁粒度
- 全局锁
- 表级锁
- 表级别的共享锁和排它锁
- 意向锁
- 自增锁
- 元数据锁
- 页级锁
- 行级锁
- 记录锁
- 间隙锁
- 临建锁
- 插入意向锁
- 按设计思想
- 乐观锁
- 悲观锁
- 按加锁方式
- 隐式锁
- 显式锁
共享锁(读锁)和排他锁(写锁) 共享锁: 对于同一个数据,多个事务对其同时进行操作,不会互相影响,相互不会阻塞。 排他锁: 当前写操作没有完成之前,会阻断其他的读锁和写锁。
总结: 不同事务中,只有都是共享锁才互不影响不会阻塞,只要有一个排他锁,都会阻塞,对于存储引擎InnoDB来说,共享锁和排他锁可以加在表数据上,也可以加在行数据上。
如何加锁:
加共享锁:SQL语句 + "LOCK IN SHARE MODE" / "FOR SHARE"; 加排他锁:SQL语句 + "FOR UPDATE";
读操作(select)可以加共享锁和排他锁,写操作(insert、update、delete)只能加排他锁。
全局锁 / 表级锁 / 页级锁 / 行级锁
全局锁
- 全局锁就是将整个数据库实例加锁,加锁后整个数据库实例就处于只读状态。后续的操作的事务提交的语句都会被阻塞。
- 使用场景: 做全库的逻辑备份,对所有的表进行定,从而保证数据的一致性。
- 添加/释放全局锁:
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();
}
}
}