精华内容
下载资源
问答
  • 其次测试行锁的升级,测试索引项操作的等待;最后,减少操作索引项数据,查看行锁升级的临界值。 测试前,首先建立两个mysql连接,并且将mysql的autocommit设置为0,然后进行一下各个测试。执行的sql语句为: set ...

    创建数据表test,表定义如下所示:

    CREATE TABLE `test` (

    `id` int(11) NOT NULL AUTO_INCREMENT,

    `name` varchar(20) NOT NULL,

    PRIMARY KEY (`id`),

    KEY `idx_name` (`name`)

    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;

    创建测试数据集,插入SQL语句如下所示:

    insert into test value(null,'aaa'), (null,'aaa'), (null,'aaa'), (null,'aaa'), (null,'aaa'), (null,'aaa');

    insert into test value(null,'bbb'), (null,'ccc'), (null,'ddd'), (null,'eee'), (null,'fff'), (null,'ggg'), (null,'hhh'), (null,'iii'), (null,'jjj'), (null,'kkk'), (null,'lll'), (null,'mmm');

    测试

    测试过程从三个方面测试:首先测试行锁的有效性,即测试索引项的行锁操作;其次测试行锁的升级,测试索引项操作的锁等待;最后,减少操作索引项数据,查看行锁升级的临界值。

    测试前,首先建立两个mysql连接,并且将mysql的autocommit设置为0,然后进行一下各个测试。执行的sql语句为:

    set autocommit=0;

    1、行锁测试

    在mysql客户端1上执行以下操作:

    delete from test where name='ccc';

    在mysql客户端2上执行以下操作:

    insert into test values(null,’nnn’);

    在客户端1没有提交和回滚操作之前,在客户端2上执行插入操作成功。由此可见,mysql在操作时的确是使用了行级锁。

    验证结束之后,回滚操作,分别在两个客户端上执行回滚操作:

    rollback;

    2、行锁升级测试

    在mysql客户端1上执行以下操作:

    delete from test where name='aaa';

    在mysql客户端2上执行以下操作:

    insert into test values(null,’nnn’);

    客户端1没有提交和回滚的情况下,客户端2始终处于锁等待状态。只有当在客户端1上执行回滚操作或者提交操作后,客户端2上的操作才能执行。从行锁的理论来看,这是不符的。但是当操作的数据占整个数据的比例较大时,行锁将会升级,导致其他操作处于锁等待状态。

    3、行锁升级临界测试

    经测试,当删除数据为5条时有锁等待4条时没有。而如果name字段上没有索引则为表锁。

    展开全文
  • 这篇文章分为六个部分,不同特性的锁分类,并发锁的不同设计,Synchronized中的锁升级,ReentrantLock和ReadWriteLock的应用,帮助你梳理 Java 并发锁及相关的操作。一、锁有哪些分类一般我们提到的锁有以下这些:...

    这篇文章分为六个部分,不同特性的锁分类,并发锁的不同设计,Synchronized中的锁升级,ReentrantLock和ReadWriteLock的应用,帮助你梳理 Java 并发锁及相关的操作。

    一、锁有哪些分类

    一般我们提到的锁有以下这些:

    乐观锁/悲观锁

    公平锁/非公平锁

    可重入锁

    独享锁/共享锁

    互斥锁/读写锁

    分段锁

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

    自旋锁

    上面是很多锁的名词,这些分类并不是全是指锁的状态,有的指锁的特性,有的指锁的设计,下面分别说明。

    1、乐观锁 VS 悲观锁

    乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同角度,在Java和数据库中都有此概念对应的实际应用。

    (1)乐观锁

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

    乐观锁适用于多读的应用类型,乐观锁在Java中是通过使用无锁编程来实现,最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。

    CAS全称 Compare And Swap(比较与交换),是一种无锁算法。在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。java.util.concurrent包中的原子类就是通过CAS来实现了乐观锁。

    简单来说,CAS算法有3个三个操作数:

    需要读写的内存值 V。

    进行比较的值 A。

    要写入的新值 B。

    当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则返回V。这是一种乐观锁的思路,它相信在它修改之前,没有其它线程去修改它;而Synchronized是一种悲观锁,它认为在它修改之前,一定会有其它线程去修改它,悲观锁效率很低。

    (2)悲观锁

    总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。

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

    悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。

    乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。

    2、公平锁 VS 非公平锁

    (1)公平锁

    就是很公平,在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照FIFO的规则从队列中取到自己。

    公平锁的优点是等待锁的线程不会饿死。缺点是整体吞吐效率相对非公平锁要低,等待队列中除第一个线程以外的所有线程都会阻塞,CPU唤醒阻塞线程的开销比非公平锁大。

    (2)非公平锁

    上来就直接尝试占有锁,如果尝试失败,就再采用类似公平锁那种方式。

    非公平锁的优点是可以减少唤起线程的开销,整体的吞吐效率高,因为线程有几率不阻塞直接获得锁,CPU不必唤醒所有线程。缺点是处于等待队列中的线程可能会饿死,或者等很久才会获得锁。

    (3)典型应用

    java jdk并发包中的ReentrantLock可以指定构造函数的boolean类型来创建公平锁和非公平锁(默认),比如:公平锁可以使用new ReentrantLock(true)实现。

    3、独享锁 VS 共享锁

    (1)独享锁

    是指该锁一次只能被一个线程所持有。

    (2)共享锁

    是指该锁可被多个线程所持有。

    对于Java ReentrantLock而言,其是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。

    读锁的共享锁可保证并发读是非常高效的,读写,写读 ,写写的过程是互斥的。

    独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。

    (3)AQS

    抽象队列同步器(AbstractQueuedSynchronizer,简称AQS)是用来构建锁或者其他同步组件的基础框架,它使用一个整型的volatile变量(命名为state)来维护同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。

    concurrent包的实现结构如上图所示,AQS、非阻塞数据结构和原子变量类等基础类都是基于volatile变量的读/写和CAS实现,而像Lock、同步器、阻塞队列、Executor和并发容器等高层类又是基于基础类实现。

    4、互斥锁 VS 读写锁

    相交进程之间的关系主要有两种,同步与互斥。所谓互斥,是指散布在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。所谓同步,是指散布在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。

    显然,同步是一种更为复杂的互斥,而互斥是一种特殊的同步。

    也就是说互斥是两个线程之间不可以同时运行,他们会相互排斥,必须等待一个线程运行完毕,另一个才能运行,而同步也是不能同时运行,但他是必须要安照某种次序来运行相应的线程(也是一种互斥)!

    总结:互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

    同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。

    (1)互斥锁

    在访问共享资源之前对进行加锁操作,在访问完成之后进行解锁操作。 加锁后,任何其他试图再次加锁的线程会被阻塞,直到当前进程解锁。

    如果解锁时有一个以上的线程阻塞,那么所有该锁上的线程都被编程就绪状态, 第一个变为就绪状态的线程又执行加锁操作,那么其他的线程又会进入等待。 在这种方式下,只有一个线程能够访问被互斥锁保护的资源

    (2)读写锁

    这个时候读写锁就应运而生了,读写锁是一种通用技术,并不是Java特有的。

    读写锁特点:

    多个读者可以同时进行读

    写者必须互斥(只允许一个写者写,也不能读者写者同时进行)

    写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)

    互斥锁特点:

    一次只能一个线程拥有互斥锁,其他线程只有等待

    (3)Linux的读写锁

    Linux内核也支持读写锁。

    互斥锁

    pthread_mutex_init()

    pthread_mutex_lock()

    pthread_mutex_unlock()

    读写锁

    pthread_rwlock_init()

    pthread_rwlock_rdlock()

    pthread_rwlock_wrlock()

    pthread_rwlock_unlock()

    条件变量

    pthread_cond_init()

    pthread_cond_wait()

    pthread_cond_signal()

    5、自旋锁

    自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

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

    典型的自旋锁实现的例子,可以参考自旋锁的实现

    它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。

    但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,”自旋”一词就是因此而得名。

    (1)Java如何实现自旋锁?

    下面是个简单的例子:

    public class SpinLock {

    private AtomicReference cas = new AtomicReference();

    public void lock() {

    Thread current = Thread.currentThread();

    // 利用CAS

    while (!cas.compareAndSet(null, current)) {

    // DO nothing

    }

    }

    public void unlock() {

    Thread current = Thread.currentThread();

    cas.compareAndSet(current, null);

    }

    }

    lock()方法利用的CAS,当第一个线程A获取锁的时候,能够成功获取到,不会进入while循环,如果此时线程A没有释放锁,另一个线程B又来获取锁,此时由于不满足CAS,所以就会进入while循环,不断判断是否满足CAS,直到A线程调用unlock方法释放了该锁。

    (2)自旋锁存在的问题

    如果某个线程持有锁的时间过长,就会导致其它等待获取锁的线程进入循环等待,消耗CPU。使用不当会造成CPU使用率极高。

    上面Java实现的自旋锁不是公平的,即无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题。

    (3)自旋锁的优点

    自旋锁不会使线程状态发生切换,一直处于用户态,即线程一直都是active的;不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快

    非自旋锁在获取不到锁的时候会进入阻塞状态,从而进入内核态,当获取到锁的时候需要从内核态恢复,需要线程上下文切换。 (线程被阻塞后便进入内核(Linux)调度状态,这个会导致系统在用户态与内核态之间来回切换,严重影响锁的性能)

    二、并发锁的不同设计方式

    根据所锁的设计方式和应用,有分段锁,读写锁等。

    1、分段锁技术,并发锁的一种设计方案

    分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。

    以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。

    当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在那一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。

    但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。

    分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。

    2、锁消除和锁膨胀(粗化)

    锁消除,如无必要,不要使用锁。Java 虚拟机也可以根据逃逸分析判断出加锁的代码是否线程安全,如果确认线程安全虚拟机会进行锁消除提高效率。

    锁粗化。如果一段代码需要使用多个锁,建议使用一把范围更大的锁来提高执行效率。Java 虚拟机也会进行优化,如果发现同一个对象锁有一系列的加锁解锁操作,虚拟机会进行锁粗化来降低锁的耗时。

    3、轮询锁与定时锁

    轮询锁是通过线程不断尝试获取锁来实现的,可以避免发生死锁,可以更好地处理错误场景。Java 中可以通过调用锁的 tryLock 方法来进行轮询。tryLock 方法还提供了一种支持定时的实现,可以通过参数指定获取锁的等待时间。如果可以立即获取锁那就立即返回,否则等待一段时间后返回。

    4、读写锁

    读写锁 ReadWriteLock 可以优雅地实现对资源的访问控制,具体实现为 ReentrantReadWriteLock。读写锁提供了读锁和写锁两把锁,在读数据时使用读锁,在写数据时使用写锁。

    读写锁允许有多个读操作同时进行,但只允许有一个写操作执行。如果写锁没有加锁,则读锁不会阻塞,否则需要等待写入完成。

    ReadWriteLock lock = new ReentrantReadWriteLock();

    Lock readLock = lock.readLock();

    Lock writeLock = lock.writeLock();

    三、synchronized中的锁

    synchronized 代码块是由一对儿 monitorenter/monitorexit 指令实现的,Monitor 对象是同步的基本实现单元。

    在 Java 6 之前,Monitor 的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作。

    现代的(Oracle)JDK 中,JVM 对此进行了大刀阔斧地改进,提供了三种不同的 Monitor 实现,也就是常说的三种不同的锁:偏斜锁(Biased Locking)、轻量级锁和重量级锁,大大改进了其性能。

    1、synchronized中锁的状态

    锁的状态是通过对象监视器在对象头中的字段来表明的。

    四种状态会随着竞争的情况逐渐升级,而且是不可逆的过程,即不可降级。

    这四种状态都不是Java语言中的锁,而是Jvm为了提高锁的获取与释放效率而做的优化(使用synchronized时)。

    无锁状态

    偏向锁状态

    轻量级锁状态

    重量级锁状态

    2、偏向锁、轻量级锁、重量级锁

    这三种锁是指锁的状态,并且是针对Synchronized。在Java 5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。

    偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。

    轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。

    重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。

    3、synchronized的锁升级

    所谓锁的升级、降级,就是 JVM 优化 synchronized 运行的机制,当 JVM 检测到不同的竞争状况时,会自动切换到适合的锁实现,这种切换就是锁的升级、降级。

    当没有竞争出现时,默认会使用偏斜锁。JVM 会利用 CAS 操作(compare and swap),在对象头上的 Mark Word 部分设置线程 ID,以表示这个对象偏向于当前线程,所以并不涉及真正的互斥锁。这样做的假设是基于在很多应用场景中,大部分对象生命周期中最多会被一个线程锁定,使用偏斜锁可以降低无竞争开销。

    如果有另外的线程试图锁定某个已经被偏斜过的对象,JVM 就需要撤销(revoke)偏斜锁,并切换到轻量级锁实现。轻量级锁依赖 CAS 操作 Mark Word 来试图获取锁,如果重试成功,就使用普通的轻量级锁;否则,进一步升级为重量级锁。

    四、看下ReentrantLock

    ReentrantLock,一个可重入的互斥锁,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。

    1、基本用法

    public class LockTest {

    private Lock lock = new ReentrantLock();

    public void testMethod() {

    lock.lock();

    for (int i = 0; i < 5; i++) {

    System.out.println("ThreadName=" + Thread.currentThread().getName()

    + (" " + (i + 1)));

    }

    lock.unlock();

    }

    }

    2、Condition应用

    synchronized与wait()和nitofy()/notifyAll()方法相结合可以实现等待/通知模型,ReentrantLock同样可以,但是需要借助Condition,且Condition有更好的灵活性,具体体现在:

    一个Lock里面可以创建多个Condition实例,实现多路通知

    notify()方法进行通知时,被通知的线程时Java虚拟机随机选择的,但是ReentrantLock结合Condition可以实现有选择性地通知,这是非常重要的

    3、Condition类和Object类

    Condition类的awiat方法和Object类的wait方法等效

    Condition类的signal方法和Object类的notify方法等效

    Condition类的signalAll方法和Object类的notifyAll方法等效

    五、再看下ReadWriteLock

    在并发场景中用于解决线程安全的问题,我们几乎会高频率的使用到独占式锁,通常使用java提供的关键字synchronized(关于synchronized可以看这篇文章)或者concurrents包中实现了Lock接口的ReentrantLock。

    它们都是独占式获取锁,也就是在同一时刻只有一个线程能够获取锁。而在一些业务场景中,大部分只是读数据,写数据很少,如果仅仅是读数据的话并不会影响数据正确性(出现脏读),而如果在这种业务场景下,依然使用独占锁的话,很显然这将是出现性能瓶颈的地方。

    针对这种读多写少的情况,java还提供了另外一个实现Lock接口的ReentrantReadWriteLock(读写锁)。读写所允许同一时刻被多个读线程访问,但是在写线程访问时,所有的读线程和其他的写线程都会被阻塞。

    1、ReadWriteLock接口

    ReadWriteLock,顾明思义,读写锁在读的时候,上读锁,在写的时候,上写锁,这样就很巧妙的解决synchronized的一个性能问题:读与读之间互斥。

    ReadWriteLock也是一个接口,原型如下:

    public interface ReadWriteLock {

    Lock readLock();

    Lock writeLock();

    }

    该接口只有两个方法,读锁和写锁。

    也就是说,我们在写文件的时候,可以将读和写分开,分成2个锁来分配给线程,从而可以做到读和读互不影响,读和写互斥,写和写互斥,提高读写文件的效率。

    2、ReentrantReadWriteLock应用

    下面的实例参考《Java并发编程的艺术》,使用读写锁实现一个缓存。

    import java.util.HashMap;

    import java.util.Map;

    import java.util.concurrent.locks.Lock;

    import java.util.concurrent.locks.ReentrantReadWriteLock;

    public class Cache {

    static Map map = new HashMap();

    static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    static Lock readLock = readWriteLock.readLock();

    static Lock writeLock = readWriteLock.writeLock();

    public static final Object getByKey(String key){

    readLock.lock();

    try{

    return map.get(key);

    }finally{

    readLock.unlock();

    }

    }

    public static final Object getMap(){

    readLock.lock();

    try{

    return map;

    }finally{

    readLock.unlock();

    }

    }

    public static final Object put(String key,Object value){

    writeLock.lock();

    try{

    return map.put(key, value);

    }finally{

    writeLock.unlock();

    }

    }

    public static final Object remove(String key){

    writeLock.lock();

    try{

    return map.remove(key);

    }finally{

    writeLock.unlock();

    }

    }

    public static final void clear(){

    writeLock.lock();

    try{

    map.clear();

    }finally{

    writeLock.unlock();

    }

    }

    public static void main(String[] args) {

    List threadList = new ArrayList();

    for(int i =0;i<6;i++){

    Thread thread = new PutThread();

    threadList.add(thread);

    }

    for(Thread thread : threadList){

    thread.start();

    }

    put("ji","ji");

    System.out.println(getMap());

    }

    private static class PutThread extends Thread{

    public void run(){

    put(Thread.currentThread().getName(),Thread.currentThread().getName());

    }

    }

    }

    3、读写锁的锁降级

    读写锁支持锁降级,遵循按照获取写锁,获取读锁再释放写锁的次序,写锁能够降级成为读锁,不支持锁升级,关于锁降级下面的示例代码摘自ReentrantWriteReadLock源码中:

    void processCachedData() {

    rwl.readLock().lock();

    if (!cacheValid) {

    // Must release read lock before acquiring write lock

    rwl.readLock().unlock();

    rwl.writeLock().lock();

    try {

    // Recheck state because another thread might have

    // acquired write lock and changed state before we did.

    if (!cacheValid) {

    data = ...

    cacheValid = true;

    }

    // Downgrade by acquiring read lock before releasing write lock

    rwl.readLock().lock();

    } finally {

    rwl.writeLock().unlock(); // Unlock write, still hold read

    }

    }

    try {

    use(data);

    } finally {

    rwl.readLock().unlock();

    }

    }

    }

    展开全文
  • 我使用MySQL 5.5。我注意到在并发情况下发生了特殊的僵局,我不认为这种僵局应该发生。重现这样,使用两个同时运行的mysql客户端会话:mysql session 1:create table parent (id int(11) primary key);insert into ...

    我使用MySQL 5.5。我注意到在并发情况下发生了特殊的僵局,我不认为这种僵局应该发生。

    重现这样,使用两个同时运行的mysql客户端会话:

    mysql session 1:

    create table parent (id int(11) primary key);

    insert into parent values (1);

    create table child (id int(11) primary key, parent_id int(11), foreign key (parent_id) references parent(id));

    begin;

    insert into child (id, parent_id) values (10, 1);

    -- this will create shared lock on parent(1)

    mysql session 2:

    begin;

    -- try and get exclusive lock on parent row

    select id from parent where id = 1 for update;

    -- this will block because of shared lock in session 1

    mysql session 1:

    -- try and get exclusive lock on parent row

    select id from parent where id = 1 for update;

    -- observe that mysql session 2 transaction has been rolled back

    mysql会话2:

    ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

    从显示引擎innodb状态报告的信息是:

    ------------------------

    LATEST DETECTED DEADLOCK

    ------------------------

    161207 10:48:56

    *** (1) TRANSACTION:

    TRANSACTION 107E67, ACTIVE 43 sec starting index read

    mysql tables in use 1, locked 1

    LOCK WAIT 2 lock struct(s), heap size 376, 1 row lock(s)

    MySQL thread id 13074, OS thread handle 0x7f68eccfe700, query id 5530424 localhost root statistics

    select id from parent where id = 1 for update

    *** (1) WAITING FOR THIS LOCK TO BE GRANTED:

    RECORD LOCKS space id 0 page no 3714 n bits 72 index `PRIMARY` of table `foo`.`parent` trx id 107E67 lock_mode X locks rec but not gap waiting

    Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0

    0: len 4; hex 80000001; asc ;;

    1: len 6; hex 000000107e65; asc ~e;;

    2: len 7; hex 86000001320110; asc 2 ;;

    *** (2) TRANSACTION:

    TRANSACTION 107E66, ACTIVE 52 sec starting index read

    mysql tables in use 1, locked 1

    5 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1

    MySQL thread id 12411, OS thread handle 0x7f68ecfac700, query id 5530425 localhost root statistics

    select id from parent where id = 1 for update

    *** (2) HOLDS THE LOCK(S):

    RECORD LOCKS space id 0 page no 3714 n bits 72 index `PRIMARY` of table `foo`.`parent` trx id 107E66 lock mode S locks rec but not gap

    Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0

    0: len 4; hex 80000001; asc ;;

    1: len 6; hex 000000107e65; asc ~e;;

    2: len 7; hex 86000001320110; asc 2 ;;

    *** (2) WAITING FOR THIS LOCK TO BE GRANTED:

    RECORD LOCKS space id 0 page no 3714 n bits 72 index `PRIMARY` of table `foo`.`parent` trx id 107E66 lock_mode X locks rec but not gap waiting

    Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0

    0: len 4; hex 80000001; asc ;;

    1: len 6; hex 000000107e65; asc ~e;;

    2: len 7; hex 86000001320110; asc 2 ;;

    *** WE ROLL BACK TRANSACTION (1)

    您可以看到交易(1)没有显示任何已经获得的S或X锁;它只是阻止了尝试获得排他锁。由于没有循环,在这种情况下不应该有僵局,据我所知。

    这是一个已知的MySQL bug吗?有其他人遇到过吗?使用了哪些解决方法?

    这些是我们可以采取的可能步骤:

    >减少我们对外键的使用(在我们的生产场景中,我们只是软删除引用的表中的行,但是是icky)

    >获取排他锁,而不是隐式共享锁(将减少我们的并发吞吐量)

    >改变我们的逻辑,所以我们不再需要在添加子行的同一个事务中对父进行排他锁(有风险和难度)

    >将我们的MySQL版本更改为不显示此行为的版本

    还有其他选择我们没有考虑?

    展开全文
  • MySQL innoDB 中的锁升级

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

    什么是锁升级?

    • 锁升级是指将当前锁的粒度降低,如一把行锁升级唯一把页锁,或者将页锁升级为表锁,如果在数据库设计中认为锁是一中稀有资源,哪么就会频繁有锁升级的现象

    发生锁升级的现象

    • 当一条SQL语句对一个对象上持有的锁数量超锁了阈值,默认这个阈值为5000,但是对于不同对象不会发生锁升级
    • 锁资源占用的内存超过激活内存的百分之40 就会发生锁升级

    但是!!!!!

    innoDB 引擎不存在锁升级的问题,因为其不是根据每个记录来产生啊行锁的,是根据每个事务访问的每个页对锁进行管理的。
    在这里插入图片描述
    其实吧,这个根据页进行加锁我没搞懂,X,S锁作何解释,难道不是当一条SQL语句加的锁范围大了 在next-keys-locks 的加锁算法下导致全页被锁住 或全表被锁住。 我感觉这玩意也是锁升级啊。 要是哪位大神看到帮我指点指点

    展开全文
  • mysql 锁_MYSQL锁机制

    2020-11-22 00:15:46
    前言本文讲述mysql锁定义,lock与latch区别,Innodb中的锁,强制开启S/X锁,自增长锁,行锁的3中算法,锁升级等,主要参照来自《MYSQL技术内幕 第2版》这本书,以及参杂了自己的一些理解。锁定义/作用为支持对 共享...
  • 其次测试行锁的升级,测试索引项操作的等待;最后,减少操作索引项数据,查看行锁升级的临界值。 测试前,首先建立两个mysql连接,并且将mysql的autocommit设置为0,然后进行一下各个测试。执行的sql语句为: set ...
  • mysql innodb 行级锁升级

    2019-09-24 14:07:50
    由此可见,mysql在操作时的确是使用了行级。  验证结束之后,回滚操作,分别在两个客户端上执行回滚操作: rollback;   2 、行锁升级测试  在mysql客户端1上执行以下操作: delete from test...
  • mysql 与sqlserver的锁升级

    千次阅读 2019-06-18 11:26:42
    锁升级(Lock Escalation)是指将当前锁的粒度降低(有点Java锁粗化的味道),是一种优化技术,数据库设计者们认为锁是一种稀有资源,为了避免锁的开销(占用内存),数据库中会频繁的出现锁升级的现象。比如sql...
  • 前言本文讲述mysql锁定义,lock与latch区别,Innodb中的锁,强制开启S/X锁,自增长锁,行锁的3中算法,锁升级等,主要参照来自《MYSQL技术内幕 第2版》这本书,以及参杂了自己的一些理解。锁定义/作用为支持对 共享...
  • 1. 系统版本MySQL 5.7.25 ubuntu 16.042. 全局全局即对整个数据库实例加锁,使得整个库处于只读状态,会阻塞DML和DDL语句。使用如下命令(简称FTWRL)可为数据库加全局:flush tables with read lock;释放全局...
  • 文章目录lock与latch锁的类型MVCC一致性非锁定读(快照读)一致性锁定读(当前读)锁算法死锁锁升级 lock与latch 在了解数据库锁之前,首先就要区分开lock和latch。在数据库中,lock和latch虽然都是锁,却有着截然...
  • MySql的写语句中,给表列赋值与表类型不符合时,MySql底层的优化器发挥作用,会做一个强制类型转化,此时能正常操作,但会导致行锁升级为表锁。示例如下以student表为例,表字段类型:表内容如下: 打开两个...
  • InnoDB存储引擎是基于行锁的设计,当然可以升级为表锁。InnoDB引擎内部也会使用用来控制对多种不同的资源提供并发访问。如,操作缓存池中的LRU列表,删除、添加、移动LRU列表中的元素。SQL Server低版本是基于页...
  • Mysql

    2019-06-28 10:55:56
    Innodb 支持行锁,有时会升级为表锁。 MyIsam:支持表锁 × 表锁的特点: 开销小,加锁块;不会出现死锁,出现冲突的概率高,并发度相对低。 × 行锁特点: 开销大,加锁慢,会出现死锁现象;力度小,发生...
  • mysql 关闭_Mysql

    2021-01-27 20:05:30
    分类行锁间隙表锁行锁(原理是基于索引的,如果不命中索引就会升级为表锁,注意Innodb存储引擎)1、查自动提交是否关闭命令行打开两个窗口,窗口1关闭自动提交show variables like 'autocommit';--关闭自动提交...
  • MySQL

    2019-08-28 14:01:19
    一、什么是 是数据库系统区别于文件系统的一个关键...页、表锁、行锁,是一种稀有的资源越多开销就越大,因此会有锁升级,在这种情况下,行锁会升级到表锁。 1、Lock 与 Latch 数据库中Lock 与 Latc...
  • MySQL锁问题

    2020-04-11 16:14:39
    MySQL锁问题1 锁概述2 锁分类3 Mysql 锁4 MyISAM 表锁4.1 获得表的读锁4.2 获得tb_book 表的写锁4.3 查看锁的争用情况5 InnoDB 行锁5.1 行锁介绍5.2 背景知识5.3 InnoDB 的行锁模式5.4 无索引行锁升级为表锁5.5 间隙...
  • MYSQL锁机制

    2020-06-29 22:27:36
    MYSQL锁机制死锁什么情况下会产生死锁死锁演示死锁处理方式MVCC-乐观锁什么是MVCC悲观锁间隙锁行锁升级为表锁 死锁 什么情况下会产生死锁 死锁是指两个或者两个以上进程在执行过程中因争夺资源而造成一种互相等待的...
  • 锁升级(Lock Escalation)是指将当前锁的粒度降低。举例来说,数据库可以把一个表的1000个行锁升级为一个页锁,或者将页锁升级为表锁。如果在数据库的设计中认为锁是一种稀有资源,而且想避免锁的开销,那数据库中会频繁...
  • Mysql 死锁

    2021-02-22 22:25:50
    Mysql 表锁 意向:当表锁事务去访问行锁资源,那么行锁会升级成意向 自增:当自增列存在插入事务时,自增列会获取自增,其他事务都要等待。 行锁 Record lock: 对索引加锁(主键范围存在) Gap ...
  • InnoDB默认是行锁(row lock)InnoDB是通过在索引记录上加锁,实现行锁因此没有索引时就无法实现行锁,从而升级成全表记录,等同于表锁。索引效率很低时也会升级。需要加锁的数据量过多,也会直接升级锁范围。 ...
  • MySQL锁机制

    2019-09-24 20:49:18
    MySQL锁机制最显著的特点是不同的存储引擎支持不同的锁机制。InnoDB支持行锁,有时也会升级为表锁,MyISAM只支持表锁。 表锁的特点是开销小,加锁快;不会出现死锁;锁粒度大,发生锁冲突的概率高,并发度相对较...
  • MyISAM引擎已经不再推荐使用,主是要因为MyISAM引擎不支持事务虽然查询性能略微高点,但InnoDB经过几个版本的升级后各方面已经有很大的提升,其中在MySQL5.6版本后InnoDB开始支持全文索引,到5.7后可以使用全文索引...
  • mysql锁机制 08

    2020-10-28 09:28:59
    mysql锁机制锁的基础与行锁的特点1、概念2、锁的类型3、innodb锁4、innodb锁类型5、锁对于语句的加锁5.1 排它锁5.2 共享锁死锁的产生于处理1、死锁产生的原因2、死锁的现象乐观锁与悲观锁的解释1、MVCC-多版本并发...
  • mysql锁详细解析

    2021-03-05 14:26:59
    1.mysql锁基础 2乐观锁于悲观锁的解释 3.死锁的产生和处理 4.间隙锁于行锁升级为表锁 1.0所谓表级锁,它直接锁住的是一个表,开销小,加锁快,不会出现死锁的情况,锁定粒度大,发生锁 冲突的概率更高,并发度最低 ...
  • Mysql锁机制

    2020-11-20 13:51:29
    文章目录定义的分类从对数据操作的类型(读\写)分从对数据操作的粒度分表(偏读)特点案例分析加读加写结论行锁(偏写)特点案例分析行锁的基本演示无索引行锁升级为表锁间隙的危害说明危害案例演示案例结论...
  • 间隙:当我们用范围条件而不是相等条件检索数据,并请求共享或排他时,InnoDB会给符合条件的已有数据记 录的索引项加锁;对于键值在条件范围内但不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加 ...

空空如也

空空如也

1 2 3 4 5 ... 19
收藏数 365
精华内容 146
关键字:

mysql锁升级

mysql 订阅