MySQL的SELECT …for update

MySQL的SELECT …for update

大家好,又见面了,我是全栈君。

  最近的项目中,因为涉及到Mysql数据中乐观锁和悲观锁的使用,所以结合项目和网上的知识点对乐观锁和悲观锁的知识进行总结。

悲观锁介绍

  悲观锁是对数据被的修改持悲观态度(认为数据在被修改的时候一定会存在并发问题),因此在整个数据处理过程中将数据锁定。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在应用层中实现了加锁机制,也无法保证外部系统不会修改数据)。

使用场景举例

  商品goods表中有一个字段status,status为1代表商品未被下单,status为2代表商品已经被下单,那么我们对某个商品下单时必须确保该商品status为1。假设商品的id为1。如果不采用锁,那么操作方法如下:

//1.查询出商品信息
select status from t_goods where id=1;
//2.根据商品信息生成订单
insert into t_orders (id,goods_id) values (null,1);
//3.修改商品status为2
update t_goods set status=2;

  上面这种场景在高并发访问的情况下很可能会出现问题。前面已经提到,只有当goods status为1时才能对该商品下单,上面第一步操作中,查询出来的商品status为1。但是当我们执行第三步Update操作的时候,有可能出现其他人先一步对商品下单把goods status修改为2了,但是我们并不知道数据已经被修改了,这样就可能造成同一个商品被下单2次,使得数据不一致。所以说这种方式是不安全的。

使用悲观锁来实现

  在上面的场景中,商品信息从查询出来到修改,中间有一个处理订单的过程,使用悲观锁的原理就是,当我们在查询出goods信息后就把当前的数据锁定,直到我们修改完毕后再解锁。那么在这个过程中,因为goods被锁定了,就不会出现有第三者来对其进行修改了。要使用悲观锁,我们必须关闭mysql数据库的自动提交属性。

set autocommit=0;  
//设置完autocommit后,我们就可以执行我们的正常业务了。具体如下:
//0.开始事务
begin;/begin work;/start transaction; (三者选一就可以)
//1.查询出商品信息
select status from t_goods where id=1 for update;
//2.根据商品信息生成订单
insert into t_orders (id,goods_id) values (null,1);
//3.修改商品status为2
update t_goods set status=2;
//4.提交事务
commit;/commit work;

  注:上面的begin/commit为事务的开始和结束,因为在前一步我们关闭了mysql的autocommit,所以需要手动控制事务的提交,在这里就不细表了。

  上面的第一步我们执行了一次查询操作:select status from t_goods where id=1 for update;与普通查询不一样的是,我们使用了select…for update的方式,这样就通过数据库实现了悲观锁。此时在t_goods表中,id为1的那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。

  注:需要注意的是,在事务中,只有SELECT … FOR UPDATE 或LOCK IN SHARE MODE 相同数据时会等待其它事务结束后才执行,一般SELECT … 则不受此影响。拿上面的实例来说,当我执行select status from t_goods where id=1 for update;后。我在另外的事务中如果再次执行select status from t_goods where id=1 for update;则第二个事务会一直等待第一个事务的提交,此时第二个查询处于阻塞的状态,但是如果我是在第二个事务中执行select status from t_goods where id=1;则能正常查询出数据,不会受第一个事务的影响。

补充:MySQL select…for update的Row Lock与Table Lock

  上面我们提到,使用select…for update会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认Row-Level Lock,所以只有「明确」地指定主键,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住)。

举例说明:

  数据库表t_goods,包括id,status,name三个字段,id为主键,数据库中记录如下;

MySQL的SELECT ...for update 

  注:为了测试数据库锁,我使用两个console来模拟不同的事务操作,分别用console1、console2来表示。

例1: (明确指定主键,并且有此数据,row lock)

  console1:查询出结果,但是把该条数据锁定了

set autocommit=0;
SELECT * from t_goods where id=1 for update;

MySQL的SELECT ...for update 

  console2:查询被阻塞

  console2:如果console1长时间未提交,则会报错

MySQL的SELECT ...for update

例2: (明确指定主键,若查无此数据,无lock)

  console1:查询结果为空

set autocommit=0;
SELECT * from t_goods where id=4 for update;

  console2:查询结果为空,查询无阻塞,说明console1没有对数据执行锁定

set autocommit=0;
SELECT * from t_goods where id=4 for update;

例3: (无主键,table lock)

  console1:

set autocommit=0;
SELECT * from t_goods where status=1 for update;

MySQL的SELECT ...for update

  console2:

select * from person where state=2 for UPDATE 

  查询阻塞,说明console1把表给锁住了.若console1长时间未提交,则返回锁超时。

MySQL的SELECT ...for update

例4: (主键不明确,table lock)

  console1:查询正常

set autocommit=0;
SELECT * from t_goods where id>1 for update;

  console2:查询被阻塞,说明console1把表给锁住了。

select * from person where id>=2 for UPDATE

  以上就是关于数据库主键对MySQL锁级别的影响实例,需要注意的是,除了主键外,使用索引也会影响数据库的锁定级别。

  举例:我们修改t_goods表,给status字段创建一个索引。

例5: (明确指定索引,并且有此数据,row lock)

  console1:

set autocommit=0;
SELECT * from t_goods where status=1 for update;

  console2:查询status=1的数据时阻塞,超时后返回为空,说明数据被console1锁定了

例7: (明确指定索引,若查无此数据,无lock)

  console1:查询status=3的数据,返回空数据

set autocommit=0;
SELECT * from t_goods where status=3 for update;

  console2:查询status=3的数据,返回空数据

SELECT * from t_goods where status=3 for update;

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/108500.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • fsync函数

    fsync函数fsync函数,将文件数据同步到硬盘功能描述: 系统调用fsync将所有已写入文件描述符fd的数据真正的写道磁盘或者其他下层设备上。用法:#includeintfsync(intfd);#ifdef_POSIX_SYNCHRONIZ

    2022年5月31日
  • Ubuntu卸载软件:3种卸载方式

    Ubuntu卸载软件:3种卸载方式1.使用Synaptic软件包管理器进行卸载打开软件包管理器。Ubuntu自带了一个GUI(GraphicalUserInterface,图形化用户界面)软件包管理器,它可以让你在一个可视化窗口中卸载程序。如果你不习惯使用命令行,这一工具将非常有用。点击系统,然后选择管理。在管理菜单中,选择Synaptic软件包管理器。某些较新版本的Ubuntu没有预装Synaptic。要安装它,打…

    2022年5月7日
  • 我的世界服务器指令大全电脑版_我的世界服务器专用指令

    我的世界服务器指令大全电脑版_我的世界服务器专用指令要成为一个合格的服主,熟悉我的世界服务器指令是必须的,服务器内指令的各种功能不仅是OP需要使用,还有部分是玩家也需要知道的,下面就看看小编为大家准备的我的世界服务器指令大全吧。【大全】我的世界服务器指令大全:首先/manuaddxxgm?如果要使用这个命令,需要自己先有权限在控制台输入manuaddxxadmin然后添加sethome权限manselect世界名字(默认world)输入m…

    2022年9月23日
  • 160亿数据点图表控件LightningChart振动分析可以检测什么?

    160亿数据点图表控件LightningChart振动分析可以检测什么?LightningChart.NET完全由GPU加速,并且性能经过优化,可用于实时显示海量数据-超过10亿个数据点。LightningChart包括广泛的2D,高级3D,Polar,Smith,3D饼/甜甜圈,地理地图和GIS图表以及适用于科学,工程,医学,航空,贸易,能源和其他领域的体绘制功能。点击下载LightningChart.NET最新试用版当您想到振动分析时,您会想到什么?它正在成为结构工程中一种非常常见的识别方法,用于识别潜在的结构完整性问题,例如隐藏的物体或空隙。这是一种识别机械问题的

    2022年10月15日
  • phpstome 激活码【注册码】

    phpstome 激活码【注册码】,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月20日
  • 最新tracker服务器网站_服务器网速测试

    最新tracker服务器网站_服务器网速测试Tracker服务器是对于BT下载必须的,网上随便搜索一下就有很多Tracker服务器列表,一个服务器文件少则几十个,多则上百个,但”杂乱无章”,要不就是用不了,要不就是在中不可用,要不就是重复,真正能用的就比较少了。提供的服务器在中国都ping得通,每24小时自动更新,自动检测,从几个百服务器筛选出在中国可通的服务器列表。[下载中国可用Tracker服务器列表-每24小时更新]提供的服务…

    2022年10月1日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号