零食网站怎么做,465端口 WordPress,如何建立自己的网站商城,网站ftp密码怎么修改在操作数据库的时候#xff0c;可能会由于并发问题而引起的数据的不一致性#xff08;数据冲突#xff09;。
MySQL事务隔离级别
一个事务的执行#xff0c;本质上就是一条工作线程在执行#xff0c;当出现多个事务同时执行时#xff0c;这种情况则被称之为并发事务可能会由于并发问题而引起的数据的不一致性数据冲突。
MySQL事务隔离级别
一个事务的执行本质上就是一条工作线程在执行当出现多个事务同时执行时这种情况则被称之为并发事务所谓的并发事务也就是指多条线程并发执行。 多线程并发执行自然就会出问题也就是脏写、脏读、不可重复读及幻读问题。而对于这些问题又可以通过调整事务的隔离级别来避免那为什么调整事务的隔离级别后能避免这些问题产生呢这是因为不同的隔离级别中工作线程执行SQL语句时用的锁粒度、类型不同。 并发事务会带来哪些问题 1、脏读事务A读取了事务B更新的数据然后B回滚操作那么A读取到的数据是脏数据 2、不可重复读事务 A 多次读取同一数据事务 B 在事务A多次读取的过程中对数据作了更新并提交导致事务A多次读取同一数据时结果 不一致。 3、幻读系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级但是系统管理员B就在这个时候插入了一条具体分数的记录当系统管理员A改结束后发现还有一条记录没有改过来就好像发生了幻觉一样这就叫幻读。
总结不可重复读的和幻读很容易混淆不可重复读侧重于修改幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行解决幻读需要锁表 在博客中间部分mysql事务隔离级别例子讲得很详细
用例子说明各个隔离级别的情况
1、读未提交
1打开一个客户端A并设置当前事务模式为read uncommitted未提交读查询表account的初始值2在客户端A的事务提交之前打开另一个客户端B更新表account400-503503这时虽然客户端B的事务还没提交但是客户端A就可以查询到B已经更新的数据4一旦客户端B的事务因为某种原因回滚所有的操作都将会被撤销那客户端A查询到的数据其实就是脏数据
2、读已提交
1打开一个客户端A并设置当前事务模式为read committed不可重复读查询表account的所有记录2在客户端A的事务提交之前打开另一个客户端B更新表account3这时客户端B的事务还没提交客户端A不能查询到B已经更新的数据解决了脏读问题4客户端B的事务提交commit5客户端A执行与上一步相同的查询结果 与上一步不一致即产生了不可重复读的问题
3、可重复读
1打开一个客户端A并设置当前事务模式为repeatable read查询表account的所有记录4002在客户端A的事务提交之前打开另一个客户端B更新表account400-50350并提交3在客户端A查询表account的所有记录与步骤1查询结果400一致没有出现不可重复读的问题4在客户端A接着执行update balance balance - 50 where id 1balance没有变成400-50350这里的balance值用的是步骤2中的350来算的所以是300数据的一致性倒是没有被破坏。可重复读的隔离级别下使用了MVCC机制select操作不会更新版本号是快照读历史版本insert、update和delete会更新版本号是当前读当前版本。5重新打开客户端B插入一条新数据后提交6在客户端A查询表account的所有记录没有 查出 新增数据所以没有出现幻读不是会幻读吗
4.串行化serializable
1打开一个客户端A并设置当前事务模式为serializable查询表account的初始值2打开一个客户端B并设置当前事务模式为serializable插入一条记录报错表被锁了插入失败mysql中事务隔离级别为serializable时会锁表因此不会出现幻读的情况这种隔离级别并发性极低开发中很少会用到。
补充
1、事务隔离级别为读提交时写数据只会锁住相应的行2、事务隔离级别为可重复读时如果检索条件有索引包括主键索引的时候默认加锁方式是next-key 锁如果检索条件没有索引更新数据时会锁住整张表。一个间隙被事务加了锁其他事务是不能在这个间隙插入记录的这样可以防止幻读。3、事务隔离级别为串行化时读写数据都会锁住整张表4、隔离级别越高越能保证数据的完整性和一致性但是对并发性能的影响也越大。
MySQL的锁体系
这篇讲得超详细——MySQL锁、加锁机制超详细 以锁粒度的维度划分 全局锁锁定数据库中的所有表。加上全局锁之后整个数据库只能允许读不允许做任何写操作 表级锁每次操作锁住整张表。主要分为三类 表锁分为表共享读锁 read lock、表独占写锁 write lock 元数据锁meta data lockMDL基于表的元数据加锁加锁后整张表不允许其他事务操作。这里的元数据可以简单理解为一张表的表结构 意向锁分为意向共享锁、意向排他锁这个是InnoDB中为了支持多粒度的锁为了兼容行锁、表锁而设计的使得表锁不用检查每行数据是否加锁使用意向锁来减少表锁的检查 行级锁每次操作锁住对应的行数据。主要分为三类 记录锁 / Record 锁也就是行锁一条记录和一行数据是同一个意思。防止其他事务对此行进行update和delete在 RC、RR隔离级别下都支持 间隙锁 / Gap 锁锁定索引记录间隙不含该记录确保索引记录间隙不变防止其他事务在这个间隙进行insert产生幻读。在RR隔离级别下都支持 临键锁 / Next-Key 锁间隙锁的升级版同时具备记录锁间隙锁的功能在RR隔离级别下支持 以互斥性的角度划分 共享锁 / S锁不同事务之间不会相互排斥、可以同时获取的锁 排他锁 / X锁不同事务之间会相互排斥、同时只能允许一个事务获取的锁 共享排他锁 / SX锁MySQL5.7版本中新引入的锁主要是解决SMO带来的问题 以操作类型的维度划分 读锁查询数据时使用的锁 写锁执行插入、删除、修改、DDL语句时使用的锁 以加锁方式的维度划分 显示锁编写SQL语句时手动指定加锁的粒度 隐式锁执行SQL语句时根据隔离级别自动为SQL操作加锁 以思想的维度划分 乐观锁每次执行前认为自己会成功因此先尝试执行失败时再获取锁 悲观锁每次执行前都认为自己无法成功因此会先获取锁然后再执行
放眼望下来是不是看着还蛮多的但总归说来说去其实就共享锁、排他锁两种只是加的方式不同、加的地方不同因此就演化出了这么多锁的称呼。
1.共享锁【S锁】
数据库自带的锁。 又称读锁若事务T对数据对象A加上S锁则事务T可以读A但不能修改A其他事务只能再对A加S锁而不能加X锁直到T释放A上的S锁。这保证了其他事务可以读A但在T释放A上的S锁之前不能对A做任何修改。
上共享锁的写法lock in share mode例如 select math from zje where math60 lock in share mode2.排他锁【X锁】
数据库自带的锁。 又称写锁。若事务T对数据对象A加上X锁事务T可以读A也可以修改A其他事务不能再对A加任何锁直到T释放A上的锁。 确保不会同时同一资源进行多重更新。 如果事务T对数据A加上排他锁后则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据又能修改数据。
上排它锁的写法for update例如select math from zje where math 60 for update对比
共享锁就是允许多个线程同时获取一个锁一个锁可以同时被多个线程拥有。排它锁也称作独占锁一个锁在某一时刻只能被一个线程占有其它线程必须等待锁被释放之后才可能获取到锁。
排它锁和共享锁实例
ReentrantLock就是一种排它锁。CountDownLatch是一种共享锁。这两类都是单纯的一类即要么是排它锁要么是共享锁。 ReentrantReadWriteLock是同时包含排它锁和共享锁特性的一种锁这里主要以ReentrantReadWriteLock为例来进行分析学习。我们使用ReentrantReadWriteLock的写锁时使用的便是排它锁的特性使用ReentrantReadWriteLock的读锁时使用的便是共享锁的特性。
3.乐观锁
乐观锁不是数据库自带的需要我们自己去实现。乐观锁是指操作数据库时(更新操作)想法很乐观认为这次的操作不会导致冲突在操作数据时并不进行任何其他的特殊处理也就是不加锁而在进行更新后再去判断是否有冲突。
使用乐观锁需修改数据库的事务隔离级别 使用乐观锁的时候如果一个事务修改了库存并提交了事务那其他的事务应该可以读取到修改后的数据值所以不能使用可重复读的隔离级别应该修改为读取已提交Read committed。
1版本号法实现乐观锁
在商品表中增加一个版本号字段每次更新库存时都会携带这个版本号。如果版本号没有发生变化说明在此期间没有其他线程修改过数据更新操作可以进行如果版本号发生了变化则说明有其他线程已经修改过数据此时需要重新获取最新的数据并尝试再次更新。
UPDATE goods SET stock stock - 1, version version 1 WHERE id #{id} AND stock 0 AND version #{version}
//当商品的库存大于0且版本号与传入的版本号相同时将库存减1并将版本号加1。2CAS法实现乐观锁
CAS用库存量代替版本号在执行操作时判断 where 用的库存量和在最初查询的时候是否相等。 加乐观锁先比较再更新更新的时候判断**此时的库存是否是之前查询出的库存**如果相同表示没人修改可以更新库存否则表示别人抢过资源不再执行库存更新。
CAS
CAS 的全称是 Compare And Swap比较与交换 用于实现乐观锁被广泛应用于各大框架中。CAS 的思想很简单就是用一个预期值和要更新的变量值进行比较两值相等才会进行更新。
CAS 是一个原子操作底层依赖于一条 CPU 的原子指令。 原子操作 即最小不可拆分的操作也就是说操作一旦开始就不能被打断直到操作完成。
CAS的自旋 自旋 就是不停的判断比较看能否将值交换 在CAS操作中自旋的概念指的是当线程发现无法立即完成操作时不会让出CPU时间片而是继续循环尝试直到成功为止。这种机制可以避免线程频繁地挂起和恢复减少了线程切换的开销提高了效率。自旋锁通常适用于锁被占用的时间较短的场景因为长时间的自旋会导致CPU资源的浪费。
总的来说CAS的自旋是通过不断循环尝试来实现的一种锁优化机制它在多线程编程中用于保证操作的原子性和提高性能。
CAS/乐观锁存在的问题(ABA) CAS 三大问题1ABA问题。CAS需要在操作值的时候检查内存值是否发生变化没有发生变化才会更新内存值。但是如果内存值原来是A后来变成了B然后又变成了A那么CAS进行检查时会发现值没有发生变化但是实际上是有变化的。ABA问题的解决思路就是在变量前面添加版本号每次变量更新的时候都把版本号加一这样变化过程就从ABA变成了1A2B3A。JDK从1.5开始提供了AtomicStampedReference类来解决ABA问题原子更新带有版本号的引用类型。2循环时间长开销大。CAS操作如果长时间不成功会导致其一直自旋给CPU带来非常大的开销。 只能保证一个共享变量的原子操作。对一个共享变量执行操作时CAS能够保证原子操作但是对多个共享变量操作时CAS是无法保证操作的原子性的。
乐观锁在高并发场景下的问题 乐观锁在高并发场景下的问题是严重的空自旋 自旋 就是不停的判断比较看能否将值交换
4.悲观锁
悲观锁就是在操作数据时认为此操作会出现数据冲突所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作这点跟java中的synchronized很相似所以悲观锁需要耗费较多的时间。另外与乐观锁相对应的悲观锁是由数据库自己实现了的要用的时候我们直接调用数据库的相关语句就可以了。
适用场景
悲观锁适合写操作多的场景。乐观锁适合读操作多的场景不加锁可以提升读操作的性能。
当查询某条记录时即让数据库为该记录加锁锁住记录后别人无法操作使用类似如下语法 select for update提前加上写锁
beginTranse(开启事务)
try{query(select amount from s_store where goodID 12345 for update);//加上写锁if(库存 0){//quantity为请求减掉的库存数量query(update s_store set amount amount - quantity where goodID 12345);}
}catch( Exception e ){rollBack(回滚)
}
commit(提交事务)5.行锁
行锁由字面意思理解就是给某一行加上锁也就是一条记录加上锁。
6.表锁
对表操作的所以自然锁住全表的表锁就不会出现死锁。但是表锁效率低。