Heim > Fragen und Antworten > Hauptteil
说说我的看法,不对的地方请指正。
乐观锁的实现原理是cas操作,java中轻量级锁也是基于cas实现的。
悲观锁最大的问题就是阻塞问题。
在深入理解java虚拟机中提到,轻量级锁一般情况下是优于重量级锁(互斥锁)的;如果在高并发锁竞争比较激烈的情况下轻量级锁会由于长时间自旋消耗cpu 从而使得轻量级锁的性能比传统的重量级锁更慢。那么乐观锁中也有自旋和cas,所以高并发下乐观锁好像不是一种好的解决方案。
但是有些博文中提到 高并发下使用乐观锁更合适
比如这篇文章中就提到高并发数据库访问使用乐观锁
http://blog.csdn.net/amqvje/a...
问题1,高并发下如何选择?
问题2 乐观锁有什么缺点? 为什不都使用乐观锁。高并发下都是使用乐观锁,如果并发量不高,使用乐观锁感觉更不是问题
怪我咯2017-05-16 13:07:01
简单的来说,一般情况下,乐观锁适合只有读没有写的操作,悲观锁适合于读写混合的操作。如果写操作非常简单短小,比如增加访问人数,也可以用乐观锁,或者不需要确保读到的数据是最新的时候。80%的情况下使用乐观锁确实是比较好的选择。
CAS依靠硬件CPU指令支持去实现原子级操作,所以高并发的情况下一般会更快,但是这个快不是没有缺点的,缺点就是有读也有写的时候,CPU缓存失效率可能会增加,除非你的CPU是单核的(非Intel平台的嵌入式)。
总而言之,要提升高并发性能,还是要实际测量出的数据说明问题,我上面提到的都是理论。希望对你有帮助。
阿神2017-05-16 13:07:01
无论是悲观锁还是乐观锁,其实都是并发控制的一种思想,并不仅仅局限于数据库,具体如何选择乐观锁和悲观锁是根据业务场景来的。
悲观锁:
一般情况下,我们使用的悲观锁就是在数据库层面增加一个排它锁,加锁成功就可以修改数据然后提交事务,事务提交成功解锁,失败就说明数据正在被修改。如果你使用mysql的innodb的话,要注意 set autocommit=0
关闭mysql自动提交属性,因为mysql默认使用autocommit模式,就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交,另外还要注意锁的级别,默认innodb是使用行级锁,但是行级锁是基于索引的,如果你这条sql没用索引,那么mysql就会使用表级锁锁表了。
优缺点:
悲观锁是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会。另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载。还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数据。
乐观锁:
乐观锁其实就是假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。
一般直接写在代码逻辑层就可以了,相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制,通常采用一个version版本号或时间戳来实现。使用版本号时,可以在数据初始化时指定一个版本号,每次对数据的更新操作都对版本号执行+1操作。并判断当前版本号是不是该数据的最新的版本号。如:
1.查询出商品信息
select (status,version) from t_goods where id=#{id}
2.根据商品信息生成订单
3.修改商品status为2
update t_goods
set status=2,version=version+1
where id=#{id} and version=#{version};
优缺点:
乐观锁认为数据竞争的概率是很小的,因此,尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题。
乐观锁存在失效的情况,属小概率事件,需要多个条件共同配合才会出现。如:
应用采用自己的策略管理主键ID。如,常见的取当前ID字段的最大值+1作为新ID。
版本号字段 version 默认值为 0 。
用户A读取了某个记录准备修改它。该记录正好是ID最大的记录,且之前没被修改过,version 为默认值 0。
在用户A读取完成后,用户B恰好删除了该记录。之后,用户C又插入了一个新记录。
此时,阴差阳错的,新插入的记录的ID与用户A读取的记录的ID是一致的, 而版本号两者又都是默认值 0。
用户A在用户C操作完成后,修改完成记录并保存。由于ID、version均可以匹配上,因此用户A成功保存。但是,却把用户C插入的记录覆盖掉了。
乐观锁此时的失效,根本原因在于应用所使用的主键ID管理策略, 正好与乐观锁存在极小程度上的不兼容。
PHPz2017-05-16 13:07:01
没工作之前我也是和题主相同的想法,实际工作中,其实两者差别不大,真正高并发下系统耗时的地方永远是网络连接,数据库查询以及线程的主动睡眠,锁带来开销基本可以忽略不计
我想大声告诉你2017-05-16 13:07:01
关键问题在于,事实是悲观的还是乐观的?
假如你的资源竞争很激烈,并且无法共享的话,乐观锁不过是让大量请求的希望落空罢了。
假如你的资源没什么竞争(这个和并发高低没必然的关联,业务的影响更大),那悲观锁意味着不必要地加锁。如果原本是可共享的资源(比如资源支持多个只读方),那么悲观锁意味着失去原本的可以使用的时间。
在深入理解java虚拟机中提到,轻量级锁一般情况下是优于重量级锁(互斥锁)的;如果在高并发锁竞争比较激烈的情况下轻量级锁会由于长时间自旋消耗cpu 从而使得轻量级锁的性能比传统的重量级锁更慢。那么乐观锁中也有自旋和cas,所以高并发下悲观锁好像不是一种好的解决方案。
我并不了解 JVM。不过「乐观锁中也有自旋和cas」是什么意思?cas 就是自旋锁的一种实现方式,为什么要并列呢?「也」是什么意思?悲观锁是个阻塞操作,没有自旋,也不会持续消耗 CPU 的。