数据库 乐观锁,mysql数据库怎么设置乐观锁
大家好,关于数据库 乐观锁很多朋友都还不太明白,今天小编就来为大家分享关于mysql数据库怎么设置乐观锁的知识,希望对各位有所帮助!
如何给oracle数据库添加乐观锁
乐观锁一开始也说了,就是一开始假设不会造成数据冲突,在最后提交的时候再进行数据冲突检测。在乐观锁中,我们有3种
常用的做法来实现。
[1]第一种就是在数据取得的时候把整个数据都copy到应用中,在进行提交的时候比对当前数据库中的数据和开始的时候更新前取得的数据。当发现两个数据一模一样以后,就表示没有冲突可以提交,否则则是并发冲突,需要去用业务逻辑进行解决。
[2]第二种乐观锁的做法就是采用版本戳,这个在Hibernate中得到了使用。采用版本戳的话,首先需要在你有乐观锁的数据库table上建立一个新的column,比如为number型,当你数据每更新一次的时候,版本数就会往上增加1。比如同样有2个session同样对某条数据进行操作。两者都取到当前的数据的版本号为1,当第一个session进行数据更新后,在提交的时候查看到当前数据的版本还为1,和自己一开始取到的版本相同。就正式提交,然后把版本号增加1,这个时候当前数据的版本为2。当第二个session也更新了数据提交的时候,发现数据库中版本为2,和一开始这个session取到的版本号不一致,就知道别人更新过此条数据,这个
时候再进行业务处理,比如整个Transaction都Rollback等等操作。在用版本戳的时候,可以在应用程序侧使用版本戳的验证,也可以在数据库侧采用Trigger(触发器)来进行验证。不过数据库的Trigger的性能开销还是比较的大,所以能在应用侧进行验证的话还是推荐不用Trigger。
[3]第三种做法和第二种做法有点类似,就是也新增一个Table的Column,不过这次这个column是采用timestamp型,存储数据最后更新的时间。在Oracle9i以后可以采用新的数据类型,也就是timestamp with time zone类型来做时间戳。这种Timestamp的数据精度在Oracle的时间类型中是最高的,精确到微秒(还没与到纳秒的级别),一般来说,加上数据库处理时间和人的思考动作时间,微秒级别是非常非常够了,其实只要精确到毫秒甚至秒都应该没有什么问题。和刚才的版本戳类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。如果不想把代码写在程序中或者由于别的原因无法把代码写在现有的程序中,也可以把这个时间戳乐观锁逻辑写在Trigger或者存储过程中。
数据库update默认的是悲观锁定还是乐观锁
是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想。其实不仅仅是数据库系统中有乐观锁和悲观锁的概念,像memcache、hibernate、tair等都有类似的概念。
针对于不同的业务场景,应该选用不同的并发控制方式。所以,不要把乐观并发控制和悲观并发控制狭义的理解为DBMS中的概念,更不要把他们和数据中提供的锁机制(行锁、表锁、排他锁、共享锁)混为一谈。其实,在DBMS中,悲观锁正是利用数据库本身提供的锁机制来实现的。
mysql数据库怎么设置乐观锁
乐观锁与悲观锁不同的是,它是一种逻辑上的锁,而不需要数据库提供锁机制来支持
当数据很重要,回滚或重试一次需要很大的开销时,需要保证操作的ACID性质,此时应该采用悲观锁
而当数据对即时的一致性要求不高,重试一次不太影响整体性能时,可以采用乐观锁来保证最终一致性,同时有利于提高并发性
通常,乐观锁采用版本号/时间戳的形式实现:给数据额外增加一个版本号字段进行控制;更新时,若提交的数据所带的版本号与当前记录的版本号一致,则允许变更执行并更新版本号;若不一致,则意味着产生冲突,根据业务需求直接丢弃并返回失败,或者尝试合并
在MySQL的实践中,常见的一种使用乐观锁的方法,是在需要使用乐观锁的表中,新增一个version字段
例如:
create table product_amount(
id int not null primary key auto_increment,
product_name varchar(64) not null,
selling_amount int not null,
storing_amount int not null,
version int not null
);
当需要更新销售中的商品数量(selling_amount)时,使用如下的SQL语句:
update product_amount set selling_amount=#{selling_amount}, version=#{new_version} where id=#{id} and version=#{old_version};
若该语句返回1,则表示更新成功;若返回0,则表示前后的version不一致,产生冲突,更新失败
对于更新仓库中的商品数据(storing_amount)时,也是同理
不过,这样为每行记录都统一设置一个version字段的乐观锁方式,存在一个问题:上例中,如果同时需要单独对selling_amount及storing_amount进行update(两条SQL语句分别单独执行),那么后执行的一条会因为先执行的一条更新了version字段而失败,而这种失败显然是没有必要的,白白浪费了开销
一种比较好的方式是为每个需要乐观锁的字段单独设置版本号,例如对上例的改造:
create table product_amount(
id int not null primary key auto_increment,
product_name varchar(64) not null,
selling_amount int not null,
selling_version int not null,
storing_amount int not null,
storing_version int not null
);
selling_amount和storing_amount分别拥有自己的乐观锁版本号(selling_version和storing_version),更新时分别只关注自己的版本号,这样就不会因为版本号被其它字段修改而失败,提高了并发性
数据库高并发下乐观锁的原理
在高并发下,经常需要处理SELECT之后,在业务层处理逻辑,再执行UPDATE的情况。
若两个连接并发查询同一条数据,然后在执行一些逻辑判断或业务操作后,执行UPDATE,可能出现与预期不相符的结果。
在不使用悲观锁与复杂SQL的前提下,可以使用乐观锁处理该问题,同时兼顾性能。
场景模拟:
假设一张表两个字段,一个id,一个use_count。
表里存了100个id,每个id对应自己的use_count。
当id每使用一次,use_count要加1。当use_count大于1000时,这个id就不能在被使用了(换句话说无法从数据库中查出)。
在高并发情况下,会遇到一种问题:假设数据表中有一条记录为:id=123456, use_count=999
A与B两个连接并发查询这个id=123456,都执行下列SQL:
SELECT*FROMtableWHEREid=123456anduse_count<1000;
A先执行,得到id=123456的use_count是999,之后在程序里做了一些逻辑判断或业务操作后执行SQL:
UPDATEtableSETuse_count+1WHEREid=123456;
在A做判断且没有update之前,B也执行了查询SQL,发现use_count是999,之后它也会执行SQL:
UPDATEtableSETuse_count+1WHEREid=123456;
但是,事实上B不应该取得这个id,因为A已经是第1000个使用者。
处理步骤如下:
1、添加第3个字段version,int类型,default值为0。version值每次update时作加1处理。
ALTERTABLEtableADDCOLUMNversionINTDEFAULT'0'NOTNULLAFTERuse_count;
2、SELECT时同时获取version值(例如为3)。
SELECTuse_count,versionFROMtableWHEREid=123456ANDuse_count<1000;
3、UPDATE时检查version值是否为第2步获取到的值。
UPDATEtableSETversion=4,use_count=use_count+1WHEREid=123456ANDversion=3;
如果UPDATE的记录数为1,则表示成功。
如果UPDATE的记录数为0,则表示已经被其他连接UPDATE过了,需作异常处理。
参考地址:http://latrell.me/post-358.html
OK,关于数据库 乐观锁和mysql数据库怎么设置乐观锁的内容到此结束了,希望对大家有所帮助。