大侠帮忙看看,我的理解对吗?
牵着你的手 2014-11-06 12:42:14 看到这篇文章http://www.cnblogs.com/chenwenbiao/archive/2012/06/06/2537508.html
对SELECT ... FOR UPDATE 介绍,我看懂了如下:
举个例子: 假设商品表单products 内有一个存放商品数量的quantity ,在订单成立之前必须先确定quantity 商品数量是否足够(quantity>0) ,然后才把数量更新为1。
不安全的做法:
SELECT quantity FROM products WHERE id=3; UPDATE products SET quantity = 1 WHERE id=3;
为什么不安全呢?
少量的状况下或许不会有问题,但是大量的数据存取「铁定」会出问题。
如果我们需要在quantity>0 的情况下才能扣库存,假设程序在第一行SELECT 读到的quantity 是2 ,看起来数字没有错,但是当MySQL 正准备要UPDATE 的时候,可能已经有人把库存扣成0 了,但是程序却浑然不知,将错就错的UPDATE 下去了。
因此必须透过的事务机制来确保读取及提交的数据都是正确的。
于是我们在MySQL 就可以这样测试: (注1)
SET AUTOCOMMIT=0; BEGIN WORK; SELECT quantity FROM products WHERE id=3 FOR UPDATE; ===========================================
此时products 数据中id=3 的数据被锁住(注3),其它事务必须等待此次事务 提交后才能执行
SELECT * FROM products WHERE id=3 FOR UPDATE (注2) 如此可以确保quantity 在别的事务读到的数字是正确的。 ===========================================
UPDATE products SET quantity = '1' WHERE id=3 ; COMMIT WORK;
===========================================
提交(Commit)写入数据库,products 解锁。
注1: BEGIN/COMMIT 为事务的起始及结束点,可使用二个以上的MySQL Command 视窗来交互观察锁定的状况。
注2: 在事务进行当中,只有SELECT ... FOR UPDATE 或LOCK IN SHARE MODE 同一笔数据时会等待其它事务结束后才执行,一般SELECT ... 则不受此影响。
注3: 由于InnoDB 预设为Row-level Lock,数据列的锁定可参考这篇。
注4: InnoDB 表单尽量不要使用LOCK TABLES 指令,若情非得已要使用,请先看官方对于InnoDB 使用LOCK TABLES 的说明,以免造成系统经常发生死锁。
但有个疑问,既然quantity>0 的情况下才能update,那直接UPDATE products SET quantity = '1' WHERE id=3 where quantity >0
这样不就好了,后面跟个quantity >0就能保证了啊,何必要用select for update
还有这个介绍乐观锁的:
乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本
( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于
数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来
实现。
读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提
交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据
版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
对于上面修改用户帐户信息的例子而言,假设数据库中帐户信息表中有一个
version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $100 。
1 操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 $50
( $100-$50 )。
2 在操作员 A 操作的过程中,操作员 B 也读入此用户信息( version=1 ),并
从其帐户余额中扣除 $20 ( $100-$20 )。
3 操作员 A 完成了修改工作,将数据版本号加一( version=2 ),连同帐户扣
除后余额( balance=$50 ),提交至数据库更新,此时由于提交数据版本大
于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。
4 操作员 B 完成了操作,也将版本号加一( version=2 )试图向数据库提交数
据( balance=$80 ),但此时比对数据库记录版本时发现,操作员 B 提交的
数据版本号为 2 ,数据库记录当前版本也为 2 ,不满足 “ 提交版本必须大于记
录当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。
这样,就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作
员 A 的操作结果的可能。
从上面的例子可以看出,乐观锁机制避免了长事务中的数据库加锁开销(操作员 A
和操作员 B 操作过程中,都没有对数据库数据加锁),大大提升了大并发量下的系
统整体性能表现。
我不明白这个弄什么version有必要吗,直接update 表 set balance=balance-20 where id=1;
update 表 set balance=balance-80 where id=1;
这样两句话不就解决了,因为update 本来就会加锁,不可能出现同时减去的问题
求大侠指点,我理解的对吗?