-
mysql手册12_锁机制
2020-08-28 21:18:11mysql手册12_锁机制 锁是协调多个线程并发访问某一资源的机制(避免资源的争抢) 锁的分类 以对数据库的操作粒度划分:表锁(操作时锁定整个表)、行锁(锁定当前行) 以对数据库的操作类型划分:读锁(共享锁)、...mysql手册12_锁机制
锁是协调多个线程并发访问某一资源的机制(避免资源的争抢)
锁的分类
- 以对数据库的操作粒度划分:表锁(操作时锁定整个表)、行锁(锁定当前行)
- 以对数据库的操作类型划分:读锁(共享锁)、写锁(排他锁)
共享锁:多个事务可并发读取数据,但任何事务都不可获取数据的排它锁(写),除非已释放所有共享锁
排它锁:某个事务获得了排它锁,其他事务不能读取也不能修改
各存储引擎对锁的支持情况:
存储引擎 表级锁 行级锁 页面锁 MyISAM 支持 不支持 不支持 InnoDB 支持 支持 不支持 MEMORY 支持 不支持 不支持 BDB 支持 不支持 支持 3种锁的特性:
锁类型 特点 表级锁 偏向MyISAM存储引擎,开销小,加锁快,不会出现死锁,锁定粒度大,发生锁冲突的概率最高,并发度最低 行级锁 偏向InnoDB存储引擎,开销大,加锁慢,会出现死锁,锁定粒度最小,发生锁冲突的概率最低,并发度最高 页面锁 开销和加锁时间中等,会出现死锁,锁定粒度中等,并发度一般
MyISAM存储引擎的表锁
在执行查询语句前,MyISAM会 自动 给涉及的表加上 读锁
在执行更新操作前,MyISAM会 自动 给涉及的表加上 写锁对 MyISAM 表的读操作,不会阻塞其他事务对同一个表的读操作,但会阻塞其他事务对该表的写操作
对 MyISAM 表的写操作,则会阻塞其他事务对同一表的读和写操作所以 MyISAM 不适合做以写为主的表的存储引擎
列举当前状态在表缓存中当前被打开的表,In_use不等于0表示该表正在被锁定 show open tables +--------------------+---------------------------+--------+-------------+ | Database | Table | In_use | Name_locked | +--------------------+---------------------------+--------+-------------+ | mysql | default_roles | 0 | 0 | | mysql | check_constraints | 0 | 0 | | mysql | slave_master_info | 0 | 0 | ................ ................ ................
查看表锁的情况 show status like 'Table_locks%'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Table_locks_immediate | 9 | | Table_locks_waited | 0 | +-----------------------+-------+ Table_locks_immediate 表示能够立即获得表级锁的次数 Table_locks_waited 表示需要等待获取表锁的次数,该值较高说明存在严重的表级锁的争用情况
InnoDB存储引擎的行锁
InnoDB 与 MyISAM 的最大不同有两点:支持事务、采用行级锁
并发事务带来的问题
问题 含义 丢失更新 多个事务操作同一行数据时,后提交的事务修改的值会覆盖前面提交的事务修改的值 脏读 一个事务访问到了另一个事务还没有提交的数据 不可重复读 同一事务中执行了两次相同的查询,得到的结果不一致 幻读 事务在插入已经检查过不存在的记录时,惊奇的发现这些数据已经存在了 使用事务的隔离级别来解决以上问题
隔离级别 丢失更新 脏读 不可重复读 幻读 Read uncommitted X Y Y Y Read committed X X Y Y Repeatable read (默认) X X X Y Serializable X X X X 注:当 Where 查询条件中的字段没有索引,或者索引失效时,InnoDB 默认的行锁更新操作将变为表锁!应该尽量避免!
间隙锁
间隙锁(Gap Lock)是Innodb在可重复读提交下为了解决幻读问题时引入的锁机制,在更新操作中如果进行了范围查询,此时的行级锁将无法满足要求,需要对一定范围的行数据进行加锁举例:数据库中存在id为1,3,4,5,6 的数据,缺少id为2的数据
A事务执行以下更新操作,此时范围 id<5 的行数据将被加锁
update t_user set name = "zhangsan" where id < 5
同时B事务执行以下 insert 语句,此时B事务将进入阻塞状态,直至A事务提交
insert into t_user values(2,'lisi');
注:尽量缩小范围区间以避免间隙锁
查看InnoDB的行锁争用情况:
show status like 'innodb_row_lock%'; +-------------------------------+-------+ | Variable_name | Value | +-------------------------------+-------+ | Innodb_row_lock_current_waits | 0 | | Innodb_row_lock_time | 0 | | Innodb_row_lock_time_avg | 0 | | Innodb_row_lock_time_max | 0 | | Innodb_row_lock_waits | 0 | +-------------------------------+-------+ Innodb_row_lock_current_waits 当前正在等待行锁的数量 Innodb_row_lock_time 锁定的总时长 Innodb_row_lock_time_avg 锁定的平均时长 Innodb_row_lock_time_max 锁定的最大时长 Innodb_row_lock_waits 系统启动至今总共等待的次数
总结:
虽然 InnoDB 在锁机制上的性能损耗比 MyISAM 高,但在整体并发处理能力方面远远优于 MyISAM
优化建议:
- 尽可能让所有数据的检索通过索引完成,以避免行锁升级为表锁
- 合理涉及索引,尽量缩小锁的范围
- 尽可能减少索引条件和索引范围,避免间隙锁
- 尽量控制事务大小,减少锁定的资源量和锁定的时间长度
- 尽可能使用低级别的事务隔离(在满足业务需求的前提下)
-
锁机制
2018-03-02 14:56:58概述:在java多线程中,有synchronized关键字来实现线程间的同步互斥工作,那么其实还有一个更优秀的机制去完成这个“同步互斥”工作,他就是Lock对象,用得最多的是重入锁ReentrantLock和读写锁...概述:
在java多线程中,有synchronized关键字来实现线程间的同步互斥工作,
那么其实还有一个更优秀的机制去完成这个“同步互斥”工作,他就是Lock对象,
用得最多的是重入锁ReentrantLock和读写锁ReentrantReadWriteLock。他们具有比synchronized更为强大的功能,
并且有嗅探锁定、多路分支等功能。重入锁ReentrantLock:
在需要进行同步的代码部分加上锁定,但不要忘记最后一定要释放锁定,不然会造成锁永远无法释放,其他线程永远进不来的结果。
public class UseReentrantLock { private Lock lock = new ReentrantLock(); public void method1(){ try { lock.lock(); System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method1.."); Thread.sleep(1000); System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method1.."); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void method2(){ try { lock.lock(); System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method2.."); Thread.sleep(2000); System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method2.."); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public static void main(String[] args) { final UseReentrantLock ur = new UseReentrantLock(); Thread t1 = new Thread(new Runnable() { @Override public void run() { ur.method1(); ur.method2(); } }, "t1"); t1.start(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }
读写锁ReentrantReadWriteLock:
其核心就是实现读写分离的锁。在高并发访问下,尤其是读多写少的情况下,性能要远高于重入锁。
synchronized、ReentrantLock,同一时间内,只能有一个线程进行访问被锁定的代码,
那么读写锁则不同,其本质是分成两个锁,即读锁、写锁。在读锁下,多个线程可以并发的进行访问,但是在写锁的时候,只能一个一个的顺序访问。
口诀:读读共享(只有所有线程都是读才是共享),写写互斥,读写互斥(重要:这个一定得互斥)。
package com.bjsxt.height.lock021; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; public class UseReentrantReadWriteLock { private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); private ReadLock readLock = rwLock.readLock(); private WriteLock writeLock = rwLock.writeLock(); public void read(){ try { readLock.lock(); System.out.println("当前线程:" + Thread.currentThread().getName() + "进入..."); Thread.sleep(3000); System.out.println("当前线程:" + Thread.currentThread().getName() + "退出..."); } catch (Exception e) { e.printStackTrace(); } finally { readLock.unlock(); } } public void write(){ try { writeLock.lock(); System.out.println("当前线程:" + Thread.currentThread().getName() + "进入..."); Thread.sleep(3000); System.out.println("当前线程:" + Thread.currentThread().getName() + "退出..."); } catch (Exception e) { e.printStackTrace(); } finally { writeLock.unlock(); } } public static void main(String[] args) { final UseReentrantReadWriteLock urrw = new UseReentrantReadWriteLock(); Thread t1 = new Thread(new Runnable() { @Override public void run() { urrw.read(); } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { urrw.read(); } }, "t2"); Thread t3 = new Thread(new Runnable() { @Override public void run() { urrw.write(); } }, "t3"); Thread t4 = new Thread(new Runnable() { @Override public void run() { urrw.write(); } }, "t4"); /** * 两个读锁: * 当前线程:t2进入... * 当前线程:t1进入... * 当前线程:t2退出... * 当前线程:t1退出... * 结论:两个都是读锁,可以同时进入 */ // t1.start(); // t2.start(); /** * 读写两个锁: * 当前线程:t1进入... * 当前线程:t1退出... * 当前线程:t3进入... * 当前线程:t3退出... * 结论:读写两个锁:互斥进入,谁先抢到锁,谁先进入,下个线程只有等前一个锁释放了才能进 */ // t1.start(); // R // t3.start(); // W /** * 两个写锁: * 当前线程:t3进入... * 当前线程:t3退出... * 当前线程:t4进入... * 当前线程:t4退出... * 结论:互斥 */ t3.start(); t4.start(); } }
锁与等待/通知。(Lock替代synchronize,Condition代替wait,notify)
使用synchronized的时候,如果需要多线程间进行协作工作则需要Object的wait()和notify()、notifyAll()方法进行配合工作。那么同样,我们在使用Lock的时候,可以使用一个新的等待/通知的类,它就是Condition。
这个Condition一定是针对具体某一把锁的。也就是在只有锁的基础之上才会产生Condition。
public class UseCondition { private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void method1(){ try { lock.lock(); System.out.println("当前线程:" + Thread.currentThread().getName() + "进入等待状态.."); Thread.sleep(3000); System.out.println("当前线程:" + Thread.currentThread().getName() + "释放锁.."); condition.await(); //相当于 Object wait System.out.println("当前线程:" + Thread.currentThread().getName() +"继续执行..."); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void method2(){ try { lock.lock(); System.out.println("当前线程:" + Thread.currentThread().getName() + "进入.."); Thread.sleep(3000); System.out.println("当前线程:" + Thread.currentThread().getName() + "发出唤醒.."); condition.signal(); //相当于 Object notify } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public static void main(String[] args) { final UseCondition uc = new UseCondition(); Thread t1 = new Thread(new Runnable() { @Override public void run() { uc.method1(); } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { uc.method2(); } }, "t2"); t1.start(); t2.start(); } }
当前线程:t1进入等待状态.. 当前线程:t1释放锁.. 当前线程:t2进入.. 当前线程:t2发出唤醒.. 当前线程:t1继续执行...
我们可以通过一个Lock对象产生多个Condition进行多线程间的交互,非常的灵活。可以使得部分需要唤醒的线程唤醒,其他线程则继续等待通知。package com.bjsxt.height.lock020; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class UseManyCondition { private ReentrantLock lock = new ReentrantLock(); private Condition c1 = lock.newCondition(); private Condition c2 = lock.newCondition(); public void m1(){ try { lock.lock(); System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m1等待.."); c1.await(); //await能释放锁,但是会阻塞在这里 System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m1继续.."); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void m2(){ try { lock.lock(); System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m2等待.."); c1.await(); //await能释放锁,但是会阻塞在这里 System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m2继续.."); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void m3(){ try { lock.lock(); System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m3等待.."); c2.await(); //await会释放锁,但是会阻塞在这里 System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m3继续.."); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void m4(){ try { lock.lock(); System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒.."); c1.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void m5(){ try { lock.lock(); System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒.."); c2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public static void main(String[] args) { final UseManyCondition umc = new UseManyCondition(); Thread t1 = new Thread(new Runnable() { @Override public void run() { umc.m1(); } },"t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { umc.m2(); } },"t2"); Thread t3 = new Thread(new Runnable() { @Override public void run() { umc.m3(); } },"t3"); Thread t4 = new Thread(new Runnable() { @Override public void run() { umc.m4(); } },"t4"); Thread t5 = new Thread(new Runnable() { @Override public void run() { umc.m5(); } },"t5"); t1.start(); // c1 t2.start(); // c1 t3.start(); // c2 //过一会再唤醒线程 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } t4.start(); // c1 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } t5.start(); // c2 } }
公平锁和非公平锁:当前线程:t1进入方法m1等待.. 当前线程:t3进入方法m3等待.. 当前线程:t2进入方法m2等待.. 当前线程:t4唤醒.. 当前线程:t1方法m1继续.. 当前线程:t2方法m2继续.. 当前线程:t5唤醒.. 当前线程:t3方法m3继续..
lock用法:Lock lock = new ReentrantLock(boolean isFair); //不公平锁,顺序由CPU定,公平锁,要维护顺序,性能不及不公平锁
tryLock(): 尝试获得锁,获得结果用true/false返回。 tryLock():在给定的时间内尝试获得锁,获得结果用true/false返回。 isFair():是否是公平锁。 isLocked():是否锁定。 getHoldCount(): 查询当前线程保持此锁的个数,也就是调用lock()次数。 lockInterruptibly():优先响应中断的锁。 getQueueLength():返回正在等待获取此锁定的线程数。 getWaitQueueLength():返回等待与锁定相关的给定条件Condition的线程数。 hasQueuedThread(Thread thread): 查询指定的线程是否正在等待此锁。 hasQueuedThreads(): //查询是否有线程正在等待此锁。 hasWaiters():查询是否有线程正在等待与此锁定有关的condition条件。
优化:
1 避免死锁
2 减小锁的持有时间
3 减小锁的粒度
4 锁的分离
5 尽量使用无锁的操作,如原子操作(Atomic系列类),volatile关键字 -
Java - 锁机制有什么用?简述Hibernate的悲观锁和乐观锁机制。
2019-03-20 10:38:29有些业务逻辑在执行过程中要求对数据进行排他性的访问,于是需要通过一些机制保证在此过程中数据被锁住不会被外界修改,这就是所谓的锁机制。 Hibernate支持悲观锁和乐观锁两种锁机制。悲观锁,顾名思义悲观的认为在...分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请点击http://www.captainbed.net
有些业务逻辑在执行过程中要求对数据进行排他性的访问,于是需要通过一些机制保证在此过程中数据被锁住不会被外界修改,这就是所谓的锁机制。
Hibernate支持悲观锁和乐观锁两种锁机制。悲观锁,顾名思义悲观的认为在数据处理过程中极有可能存在修改数据的并发事务(包括本系统的其他事务或来自外部系统的事务),于是将处理的数据设置为锁定状态。悲观锁必须依赖数据库本身的锁机制才能真正保证数据访问的排他性。乐观锁,顾名思义,对并发事务持乐观态度(认为对数据的并发操作不会经常性的发生),通过更加宽松的锁机制来解决由于悲观锁排他性的数据访问对系统性能造成的严重影响。最常见的乐观锁是通过数据版本标识来实现的,读取数据时获得数据的版本号,更新数据时将此版本号加1,然后和数据库表对应记录的当前版本号进行比较,如果提交的数据版本号大于数据库中此记录的当前版本号则更新数据,否则认为是过期数据无法更新。Hibernate中通过Session的get()和load()方法从数据库中加载对象时可以通过参数指定使用悲观锁;而乐观锁可以通过给实体类加整型的版本字段再通过XML或@Version注解进行配置。提示:使用乐观锁会增加一个版本字段,很明显这需要额外的空间来存储这个版本字段,浪费了空间,但是乐观锁会让系统具有更好的并发性,这是对时间的节省。因此乐观锁也是典型的空间换时间的策略。
-
innodb引擎锁机制和myisam引擎锁机制的区别
2018-04-28 17:42:01innodb引擎锁机制和myisam引擎锁机制的区别 1、锁机制(sql操作是需要锁的,select是读锁,update、insert、delete是写锁)的最大区别及用法: (1)myisam只支持表锁: ● 共享锁(读锁、s锁):其他线程操作...innodb引擎锁机制和myisam引擎锁机制的区别
1、锁机制(sql操作是需要锁的,select是读锁,update、insert、delete是写锁)的最大区别及用法:
(1)myisam只支持表锁:
● 共享锁(读锁、s锁):其他线程操作可以读,但不能写。
● 排他锁(写锁、x锁) :其他线程操作不能读取,也不能写。(2)InnoDB 支持行锁和表锁,默认行锁(基于索引实现,需用事务操作):
● 共享锁(读锁、s锁):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
● 排他锁(写锁、x锁):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁,同时也会阻止同一行的update操作(update操作会自动加锁)。
(3)查询表级锁争用情况:
可以通过检查 table_locks_waited 和 table_locks_immediate 状态变量来分析系统上的表锁的争夺,如果 Table_locks_waited 的值比较高,则说明存在着较严重的表级锁争用情况:
mysql> SHOW STATUS LIKE ‘Table%’;
+———————–+———+
| Variable_name | Value |
+———————–+———+
| Table_locks_immediate | 1151552 |
| Table_locks_waited | 15324 |
+———————–+———+(4)获取 InnoDB 行锁争用情况:
可以通过检查 InnoDB_row_lock 状态变量来分析系统上的行锁的争夺情况:
mysql> show status like ‘innodb_row_lock%’;
+——————————-+——-+
| Variable_name | Value |
+——————————-+——-+
| InnoDB_row_lock_current_waits | 0 |
| InnoDB_row_lock_time | 0 |
| InnoDB_row_lock_time_avg | 0 |
| InnoDB_row_lock_time_max | 0 |
| InnoDB_row_lock_waits | 0 |
+——————————-+——-+
5 rows in set (0.01 sec)(5)LOCK TABLES语法:
● 在用 LOCK TABLES 对 InnoDB 表加锁时要注意,要将 AUTOCOMMIT 设为 0,否则MySQL 不会给表加锁;
● 事务结束前,不要用 UNLOCK TABLES 释放表锁,因为 UNLOCK TABLES会隐含地提交事务;
● COMMIT 或 ROLLBACK 并不能释放用 LOCK TABLES 加的表级锁,必须用UNLOCK TABLES 释放表锁。
正确的方式见如下语句:
例如,如果需要写表 t1 并从表 t 读,可以按如下做:
SET AUTOCOMMIT=0;
LOCK TABLES t1 WRITE, t2 READ, …;
[do something with tables t1 and t2 here];
COMMIT;
UNLOCK TABLES;(6)myisam表锁用法:LOCK TABLE ‘表名’ READ(读锁)**********LOCK TABLE ‘表名’ READ LOCAL(可并发读写的读锁)**********LOCK TABLE ‘表名’ WRITE(写锁)
(7)innodb表锁用法和myisam表锁用法一致,但是基于事务,需要将AUTOCOMMIT设置为0,innodb中默认每条语句都是一个事务
(8)innodb行锁用法:
(读锁)SET autocommit=0; //设置事务不自动提交,事务默认是自动提交
START TRANSACTION;
SELECT * FROM test1 WHEREname
=’1234’ LOCK IN SHARE MODE;
COMMIT;
(写锁)SET autocommit=0; //设置事务不自动提交,事务默认是自动提交
START TRANSACTION;
SELECT * FROM test1 WHEREname
=’1234’ FOR UPDATE;
COMMIT;
2、不同粒度锁的比较:
● 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
○ 这些存储引擎通过总是一次性同时获取所有需要的锁以及总是按相同的顺序获取表锁来避免死锁。
○ 表级锁更适合于以查询为主,并发用户少,只有少量按索引条件更新数据的应用,如Web 应用
● 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
○ 最大程度的支持并发,同时也带来了最大的锁开销。
○ 在 InnoDB 中,除单个 SQL 组成的事务外,
锁是逐步获得的,这就决定了在 InnoDB 中发生死锁是可能的。
○ 行级锁只在存储引擎层实现,而Mysql服务器层没有实现。 行级锁更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统
● 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。3、myisam锁表模式:
MyISAM 表的读操作与写操作之间,以及写操作之间是串行的。当一个线程获得对一个表的写锁后, 只有持有锁的线程可以对表进行更新操作。 其他线程的读、 写操作都会等待,直到锁被释放为止。
默认情况下,写锁比读锁具有更高的优先级:当一个锁释放时,这个锁会优先给写锁队列中等候的获取锁请求,然后再给读锁队列中等候的获取锁请求。 (This ensures that updates to a table are not “starved” even when there is heavy SELECT activity for the table. However, if there are many updates for a table, SELECT statements wait until there are no more updates.)。
这也正是 MyISAM 表不太适合于有大量更新操作和查询操作应用的原因,因为,大量的更新操作会造成查询操作很难获得读锁,从而可能永远阻塞。同时,一些需要长时间运行的查询操作,也会使写线程“饿死” ,应用中应尽量避免出现长时间运行的查询操作(在可能的情况下可以通过使用中间表等措施对SQL语句做一定的“分解” ,使每一步查询都能在较短时间完成,从而减少锁冲突。如果复杂查询不可避免,应尽量安排在数据库空闲时段执行,比如一些定期统计可以安排在夜间执行)。
可以设置改变读锁和写锁的优先级:
● 通过指定启动参数low-priority-updates,使MyISAM引擎默认给予读请求以优先的权利。
● 通过执行命令SET LOW_PRIORITY_UPDATES=1,使该连接发出的更新请求优先级降低。
● 通过指定INSERT、UPDATE、DELETE语句的LOW_PRIORITY属性,降低该语句的优先级。
● 给系统参数max_write_lock_count设置一个合适的值,当一个表的读锁达到这个值后,MySQL就暂时将写请求的优先级降低,给读进程一定获得锁的机会。4、myisam加锁表方法:
MyISAM存储引擎支持并发插入,以减少给定表的读和写操作之间的争用:
如果MyISAM表在数据文件中间没有空闲块,则行始终插入数据文件的末尾。 在这种情况下,你可以自由混合并发使用MyISAM表的INSERT和SELECT语句而不需要加锁——你可以在其他线程进行读操作的时候,同时将行插入到MyISAM表中。 文件中间的空闲块可能是从表格中间删除或更新的行而产生的。 如果文件中间有空闲快,则并发插入会被禁用,但是当所有空闲块都填充有新数据时,它又会自动重新启用。 要控制此行为,可以使用MySQL的concurrent_insert系统变量。
如果你使用LOCK TABLES显式获取表锁,则可以请求READ LOCAL锁而不是READ锁,以便在锁定表时,其他会话可以使用并发插入。
● 当concurrent_insert设置为0时,不允许并发插入。
● 当concurrent_insert设置为1时,如果MyISAM表中没有空洞(即表的中间没有被删除的行),MyISAM允许在一个线程读表的同时,另一个线程从表尾插入记录。这也是MySQL的默认设置。
● 当concurrent_insert设置为2时,无论MyISAM表中有没有空洞,都允许在表尾并发插入记录。5、InnoDB加锁方法:
● 意向锁是 InnoDB 自动加的, 不需用户干预。
● 对于 UPDATE、 DELETE 和 INSERT 语句, InnoDB
会自动给涉及数据集加排他锁(X);
● 对于普通 SELECT 语句,InnoDB 不会加任何锁;
事务可以通过以下语句显式给记录集加共享锁或排他锁:
○ 共享锁(S):SELECT * FROM table_name WHERE … LOCK IN SHARE MODE。 其他 session 仍然可以查询记录,并也可以对该记录加 share mode 的共享锁。但是如果当前事务需要对该记录进行更新操作,则很有可能造成死锁。
○ 排他锁(X):SELECT * FROM table_name WHERE … FOR UPDATE。其他 session 可以查询该记录,但是不能对该记录加共享锁或排他锁,而是等待获得锁● 隐式锁定:
InnoDB在事务执行过程中,使用两阶段锁协议:
随时都可以执行锁定,InnoDB会根据隔离级别在需要的时候自动加锁;
锁只有在执行commit或者rollback的时候才会释放,并且所有的锁都是在同一时刻被释放。
● 显式锁定 :
select … lock in share mode //共享锁
select … for update //排他锁select for update:
在执行这个 select 查询语句的时候,会将对应的索引访问条目进行上排他锁(X 锁),也就是说这个语句对应的锁就相当于update带来的效果。
select * for update 的使用场景:为了让自己查到的数据确保是最新数据,并且查到后的数据只允许自己来修改的时候,需要用到 for update 子句。
select lock in share mode :in share mode 子句的作用就是将查找到的数据加上一个 share 锁,这个就是表示其他的事务只能对这些数据进行简单的select 操作,并不能够进行 DML 操作。select * lock in share mode 使用场景:为了确保自己查到的数据没有被其他的事务正在修改,也就是说确保查到的数据是最新的数据,并且不允许其他人来修改数据。但是自己不一定能够修改数据,因为有可能其他的事务也对这些数据 使用了 in share mode 的方式上了 S 锁。
性能影响:
select for update 语句,相当于一个 update 语句。在业务繁忙的情况下,如果事务没有及时的commit或者rollback 可能会造成其他事务长时间的等待,从而影响数据库的并发使用效率。
select lock in share mode 语句是一个给查找的数据上一个共享锁(S 锁)的功能,它允许其他的事务也对该数据上S锁,但是不能够允许对该数据进行修改。如果不及时的commit 或者rollback 也可能会造成大量的事务等待。
for update 和 lock in share mode 的区别:
前一个上的是排他锁(X 锁),一旦一个事务获取了这个锁,其他的事务是没法在这些数据上执行 for update ;后一个是共享锁,多个事务可以同时的对相同数据执行 lock in share mode。6、InnoDB 行锁实现方式:
● InnoDB 行锁是通过给索引上的索引项加锁来实现的,这一点 MySQL 与 Oracle 不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB 这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB 才使用行级锁,否则,InnoDB 将使用表锁!
● 不论是使用主键索引、唯一索引或普通索引,InnoDB 都会使用行锁来对数据加锁。
● 只有执行计划真正使用了索引,才能使用行锁:即便在条件中使用了索引字段,但是否使用索引来检索数据是由 MySQL 通过判断不同执行计划的代价来决定的,如果 MySQL 认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下 InnoDB 将使用表锁,而不是行锁。因此,在分析锁冲突时,
别忘了检查 SQL 的执行计划(可以通过 explain 检查 SQL 的执行计划),以确认是否真正使用了索引。(更多阅读:MySQL索引总结)
● 由于 MySQL 的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然多个session是访问不同行的记录, 但是如果是使用相同的索引键, 是会出现锁冲突的(后使用这些索引的session需要等待先使用索引的session释放锁后,才能获取锁)。 应用设计的时候要注意这一点。7、InnoDB的间隙锁:
当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。
很显然,在使用范围条件检索并锁定记录时,InnoDB这种加锁机制会阻塞符合条件范围内键值的并发插入,这往往会造成严重的锁等待。因此,在实际应用开发中,尤其是并发插入比较多的应用,我们要尽量优化业务逻辑,尽量使用相等条件来访问更新数据,避免使用范围条件。
InnoDB使用间隙锁的目的:
1. 防止幻读,以满足相关隔离级别的要求;
2. 满足恢复和复制的需要:
MySQL 通过 BINLOG 录入执行成功的 INSERT、UPDATE、DELETE 等更新数据的 SQL 语句,并由此实现 MySQL 数据库的恢复和主从复制。MySQL 的恢复机制(复制其实就是在 Slave Mysql 不断做基于 BINLOG 的恢复)有以下特点:
一是 MySQL 的恢复是 SQL 语句级的,也就是重新执行 BINLOG 中的 SQL 语句。
二是 MySQL 的 Binlog 是按照事务提交的先后顺序记录的, 恢复也是按这个顺序进行的。
由此可见,MySQL 的恢复机制要求:在一个事务未提交前,其他并发事务不能插入满足其锁定条件的任何记录,也就是不允许出现幻读。 -
锁机制有什么用?简述Hibernate的悲观锁和乐观锁机制
2018-07-25 21:35:00有些业务逻辑在执行过程中要求对数据进行排他性的访问,于是需要通过一些机制保证在此过程中数据被锁住不会被外界修改,这就是所谓的锁机制。 Hibernate支持悲观锁和乐观锁两种锁机制。悲观锁,顾名思义悲观的认为在... -
MySQL锁机制
2018-06-16 15:32:22MySQL锁机制及优化 MySQL锁机制及优化 MySQL锁概述 MySQL锁级别 MyISAM 表锁 表锁介绍 表锁兼容性 MyISAM并发插入 MyISAM锁调度 MyISAM优化建议 InnoDB 行锁 死锁 优化建议 MySQL锁概述 ... -
Mysql锁机制
2019-10-23 17:05:32文章原创于公众号:程序猿周先森。本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号。 锁是计算机协调多个进程或线程并发访问某一资源...Mysql的锁机制包含多种:行锁,表锁,读锁,写锁等,其实就是使用不... -
MySql锁机制
2020-02-05 20:44:32MYSQL共有几种锁机制? 两种 共享锁(读锁):其他事务可以读,但不可以写 排它锁(写锁):其他事务不可以读也不可以写 MYSQL按锁粒度共分几种,有什么特点? 由小到大分三种 行锁:开销大,加锁慢;会出现死锁;锁定粒度... -
MySQL的锁机制和加锁原理
2019-03-09 10:35:01MySQL的锁机制和加锁原理 文章目录MySQL的锁机制和加锁原理1.行锁2.表锁3.页锁4.乐观锁和悲观锁4.1悲观锁4.2乐观锁5.MySQL/InnoDB中的行锁和表锁问题5.1InnoDB锁的特性6.Record Lock、Gap Lock、Next-key Lock锁6.1... -
PostgreSQL锁机制
2018-12-18 18:14:19下面对PostgreSQL数据库锁机制的理解,大部分来自与《PostgreSQL修炼之道 从小工到专家》-唐成书中,以及网络上的博客的总结。通过实际测试发现,还是存在一些不合理的点,后面实际的案列中,会有一些说明。 1.表级... -
Hibernate锁机制
2018-05-29 16:42:39Hibernate支持两种锁机制: 悲观锁(Pessimistic Locking):当数据被外界修改时保持原始状态,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁大多数情况下依靠数据库的锁机制实现。 乐观锁(Optimistic ... -
mysql锁机制详解
2020-05-25 23:50:40mysql锁机制详解 -
Innodb 锁机制
2018-03-31 22:46:04不同数据库和不同搜索引擎都可能有不同的锁机制,MyISAM 引擎的锁是表锁设计,并发读没有问题,但是并发写入可能就存在一定的问题。 Microsoft SQL Server 在2005版本之前是页锁设计,相对于 MyISAM 并... -
DB2锁机制
2017-06-21 17:46:21原始连接:DB2锁机制 相比较Oracle来说,DB2的锁机制麻烦了很多,而且这个麻烦带来的不是性能的上升而是下降,不过如果细致了解的话,只能感慨不愧是数据库理论诞生的公司,在实现数据库理论上比Oracle全面得多... -
mysql锁机制之行级锁
2018-05-31 00:48:22锁是在执行多线程时用于强行限定资源访问的同步机制,数据库锁根据锁的粒度可分为行级锁,表级锁和页级锁行级锁行级锁是mysql中粒度最细的一种锁机制,表示只对当前所操作的行进行加锁,行级锁发生冲突的概率很低,... -
Mysql锁机制简单了解一下
2018-06-07 20:52:11Mysql为了解决并发、数据安全的问题,使用了锁机制。 可以按照锁的粒度把数据库锁分为表级锁和行级锁。 表级锁: Mysql中锁定 粒度最大 的一种锁,对当前操作的整张表加锁,实现简单 ,资源消耗也比较少,加锁快... -
Java锁机制
2018-05-16 00:03:00对于熟悉java多线程并发的人来说,java锁机制是不可逃避的话题。那么什么是java锁机制,以及什么时候使用java的锁呢?让我们看看以下几种场景吧!一、同步锁案例:假设现在我们现在有很多人去商店买衣服,因为我们每... -
mongo锁机制简介
2019-11-20 09:12:01mongo锁机制简介 文章目录mongo锁机制简介背景锁的类型锁的粒度锁的调度和让渡常用操作与锁相关文档 背景 锁机制要解决的是并发请求下资源分配的问题,对于数据库来说,就是并发的读写。锁机制要处理两个问题,一个... -
数据库的锁机制
2017-10-02 13:54:03为什么需要锁机制事务是并发控制的基本单位,保证事务ACID原则是事务处理的重要任务,但是当多个事务对数据库进行并发操作时,就有可能破坏事务的ACID特性。为了保证事务的隔离性与一致性,就有了数据库的锁机制。在... -
MySQL锁机制以及锁的粒度
2019-09-04 19:04:25文章目录MySQL锁机制概述什么是锁,为什么使用锁锁的运作锁定机制分类并发控制锁粒度两种重要的锁策略表锁(table lock)行级锁(row lock) MySQL锁机制概述 什么是锁,为什么使用锁 锁是计算机协调多个进程或纯... -
Oracle锁机制
2018-05-09 11:08:30一、Oracle锁机制 1、什么是锁 锁是控制“共享资源”并发存取的一种机制。注意这里说“共享资源”而不仅指“数据行”,数据库的却在行一级对表的数据加锁,但是数据库也在其它地方对各种资源的并发存取使用锁。... -
数据库 锁机制 详解
2019-03-07 17:26:00乐观锁是指用户实现的锁机制,悲观锁一般就是我们通常说的数据库锁机制。 悲观锁: 顾名思义,就是很悲观的意思,认为数据随时都可能会被修改,所以每次操作之前先将数据加锁。悲观锁一般都是依靠关系数据库提供的锁... -
Mysql锁机制解析
2019-05-10 18:32:33mysql的锁是由具体的存储引擎实现的。所以像Mysql的默认引擎MyISAM和第三方插件引擎 InnoDB的锁实现机制是有区别的。 Mysql有三种级别的锁定:表级锁定、页级...表级锁:每次锁定的是一张表的锁机制就是表级别锁定(... -
MySQL的锁机制
2020-04-21 12:58:39但是,为了保证并发访问数据的一致性和完整性,MySQL服务端内部有它特有的锁机制。 MySQL支持插件式的存储引擎,不同的存储引擎内部锁实现也大不相同。 例如:MyISAM只支持表锁,而InnoDB则支持更细粒度的行锁和间隙... -
互斥锁机制
2015-06-21 13:44:53锁机制,可以说是linux整个系统的精髓所在,linux内核都是围绕着同步在运转。在多进程和多线程编程中,锁起着极其重要的作用。我这里说的是互斥锁,其实是泛指linux中所有的锁机制。我在这里不讲如果创建锁,关于锁... -
Mongodb锁机制
2017-03-10 10:03:15Mongodb锁机制Mongodb使用读写锁来允许很多用户同时去读一个资源,比如数据库或者集合。读采用的是共享锁,写采用的是排它锁。对于大部分的读写操作,WiredTiger使用的都是乐观锁,在全局、数据库、集合级别,... -
mysql锁机制
2017-10-25 11:00:21最近看了mysql的锁机制: 锁是计算机协调多个进程或纯线程并发访问某一资源的机制。在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致... -
Redis实现分布式锁机制
2018-11-18 11:17:22Redis实现分布式锁思路 常用的是redis函数是setnx(),这个应该是实现分布式锁最主要的函数。首先是将某一业务标识名作为键存到redis里,并为其设个过期时间,如果是还有加锁请求过来,先是通过setnx()看看是否能将... -
悲观锁和乐观锁机制 - Java基础
2020-08-22 15:15:17知识的广度来自知识的深度,学习如果不成体系...悲观锁和乐观锁机制 - Java基础一、资源和加锁1、场景描述2、演示案例二、锁的概念简介1、锁机制简介2、悲观锁3、乐观锁4、机制对比三、Lock基础案例1、Lock方法说明2、.