`
1028826685
  • 浏览: 939074 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类

mySql数据库 forUpdate 实战总结

 
阅读更多

悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

 

使用场景举例:以MySQL InnoDB为例

商品goods表中有一个字段status,status为1代表商品未被下单,status为2代表商品已经被下单,那么我们对某个商品下单时必须确保该商品status为1。假设商品的id为1。

 

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次,使得数据不一致。所以说这种方式是不安全的。

 

2使用悲观锁来实现:

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

 

注:要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。

 

我们可以使用命令设置MySQL为非autocommit模式:

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为主键,数据库中记录如下;

Sql代码  收藏代码
  1. mysql> select * from t_goods;  
  2. +----+--------+------+  
  3. | id | status | name |  
  4. +----+--------+------+  
  5. |  1 |      1 | 道具 |  
  6. |  2 |      1 | 装备 |  
  7. +----+--------+------+  
  8. rows in set  
  9.   
  10. mysql>  

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

 

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

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

Sql代码  收藏代码
  1. mysql> select * from t_goods where id=1 for update;  
  2. +----+--------+------+  
  3. | id | status | name |  
  4. +----+--------+------+  
  5. |  1 |      1 | 道具 |  
  6. +----+--------+------+  
  7. 1 row in set  
  8.   
  9. mysql>  

console2:查询被阻塞

Sql代码  收藏代码
  1. mysql> select * from t_goods where id=1 for update;  

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

Sql代码  收藏代码
  1. mysql> select * from t_goods where id=1 for update;  
  2. ERROR 1205 : Lock wait timeout exceeded; try restarting transaction  

 

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

console1:查询结果为空

Sql代码  收藏代码
  1. mysql> select * from t_goods where id=3 for update;  
  2. Empty set  

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

Sql代码  收藏代码
  1. mysql> select * from t_goods where id=3 for update;  
  2. Empty set  

 

例3: (无主键,table lock)

console1:查询name=道具 的数据,查询正常

Sql代码  收藏代码
  1. mysql> select * from t_goods where name='道具' for update;  
  2. +----+--------+------+  
  3. | id | status | name |  
  4. +----+--------+------+  
  5. |  1 |      1 | 道具 |  
  6. +----+--------+------+  
  7. 1 row in set  
  8.   
  9. mysql>  

console2:查询name=装备 的数据,查询阻塞,说明console1把表给锁住了

Sql代码  收藏代码
  1. mysql> select * from t_goods where name='装备' for update;  

console2:若console1长时间未提交,则查询返回为空

Sql代码  收藏代码
  1. mysql> select * from t_goods where name='装备' for update;  
  2. Query OK, -1 rows affected  

 

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

console1:查询正常

Sql代码  收藏代码
  1. mysql> begin;  
  2. Query OK, 0 rows affected  
  3.   
  4. mysql> select * from t_goods where id>0 for update;  
  5. +----+--------+------+  
  6. | id | status | name |  
  7. +----+--------+------+  
  8. |  1 |      1 | 道具 |  
  9. |  2 |      1 | 装备 |  
  10. +----+--------+------+  
  11. rows in set  
  12.   
  13. mysql>  

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

Sql代码  收藏代码
  1. mysql> select * from t_goods where id>1 for update;  

 

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

console1:

Sql代码  收藏代码
  1. mysql> begin;  
  2. Query OK, 0 rows affected  
  3.   
  4. mysql> select * from t_goods where id<>1 for update;  
  5. +----+--------+------+  
  6. | id | status | name |  
  7. +----+--------+------+  
  8. |  2 |      1 | 装备 |  
  9. +----+--------+------+  
  10. 1 row in set  
  11.   
  12. mysql>  

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

Sql代码  收藏代码
  1. mysql> select * from t_goods where id<>2 for update;  

console1:提交事务

Sql代码  收藏代码
  1. mysql> commit;  
  2. Query OK, 0 rows affected  

console2:console1事务提交后,console2查询结果正常

Sql代码  收藏代码
  1. mysql> select * from t_goods where id<>2 for update;  
  2. +----+--------+------+  
  3. | id | status | name |  
  4. +----+--------+------+  
  5. |  1 |      1 | 道具 |  
  6. +----+--------+------+  
  7. 1 row in set  
  8.   
  9. mysql>  

 

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

 

举例:

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

修改id为2的数据的status为2,此时表中数据为:

Sql代码  收藏代码
  1. mysql> select * from t_goods;  
  2. +----+--------+------+  
  3. | id | status | name |  
  4. +----+--------+------+  
  5. |  1 |      1 | 道具 |  
  6. |  2 |      2 | 装备 |  
  7. +----+--------+------+  
  8. rows in set  
  9.   
  10. mysql>  

 

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

console1:

Sql代码  收藏代码
  1. mysql> select * from t_goods where status=1 for update;  
  2. +----+--------+------+  
  3. | id | status | name |  
  4. +----+--------+------+  
  5. |  1 |      1 | 道具 |  
  6. +----+--------+------+  
  7. 1 row in set  
  8.   
  9. mysql>  

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

Sql代码  收藏代码
  1. mysql> select * from t_goods where status=1 for update;  
  2. Query OK, -1 rows affected  

console2:查询status=2的数据,能正常查询,说明console1只锁住了行,未锁表

Sql代码  收藏代码
  1. mysql> select * from t_goods where status=2 for update;  
  2. +----+--------+------+  
  3. | id | status | name |  
  4. +----+--------+------+  
  5. |  2 |      2 | 装备 |  
  6. +----+--------+------+  
  7. 1 row in set  
  8.   
  9. mysql>  

 

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

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

Sql代码  收藏代码
  1. mysql> select * from t_goods where status=3 for update;  
  2. Empty set  

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

Sql代码  收藏代码
  1. mysql> select * from t_goods where status=3 for update;  
  2. Empty set  

    说明:这里更好的解决方案使用乐观锁。比如:使用version、timestemp 在更新数据时比较版本或时间戳不一致重试。
分享到:
评论

相关推荐

    Python + MySQL 0基础从入门到精通 MySQL数据库实战精讲教程(2021精华版)课件

    在本课程"Python + MySQL 0基础从入门到精通 MySQL数据库实战精讲教程(2021精华版)"中,我们将深入学习Python编程语言与MySQL数据库的集成应用,为初学者提供一条全面掌握这两项关键技术的道路。这个教程旨在帮助...

    MySQL入门+进阶资源合集.pdf

    通过以上内容的学习,你可以深入了解MySQL的核心概念、掌握基本和高级的数据库操作技能,并且能够运用这些知识来优化数据库性能和开发更高效的数据库应用。无论是对于初学者还是有一定经验的开发者来说,这些知识点...

    Python操作三大主流数据库 实战网易新闻客户端(完整15章全)

    - **pymysql模块**:用于连接MySQL数据库,学习如何配置连接参数、执行SQL命令、处理结果集等。 - **psycopg2模块**:与PostgreSQL数据库交互的Python库,包括连接数据库、执行SQL、处理结果集等操作。 4. **SQL...

    MySQL从入门到实战学习教程练习题及参考答案.docx

    本资源提供了一个完整的 MySQL 学习教程,涵盖了从入门到实战的所有方面,包括数据库入门、数据库设计、表操作、SQL 语句基础和提高、运算及逻辑语句等内容。 章节 1:数据库入门 * 数据库的基本概念:了解数据库...

    php MySQL 网络数据库指南(1)

    SQL(Structured Query Language)是用于管理MySQL数据库的语言,用于创建、查询、更新和删除数据。 3. PHP连接MySQL: 要在PHP中使用MySQL,首先需要连接到数据库。可以使用`mysqli_connect()`或`PDO_MySQL`扩展...

    php MySQL 网络数据库指南(7)

    《PHP MySQL 网络数据库指南》是一本专为初学者设计的教程,旨在帮助读者掌握如何使用PHP语言与MySQL数据库进行交互,从而构建强大的网络应用程序。PHP是一种广泛使用的开源脚本语言,尤其适用于Web开发,而MySQL则...

    php6 mysql5 for dynamic websites

    1. 数据库基础:数据库的设计、创建和管理,以及SQL语言的基本语法,如SELECT、INSERT、UPDATE和DELETE。 2. 数据类型和约束:了解各种数据类型(如INT、VARCHAR、DATE等)及其适用场景,以及如何设置字段的约束条件...

    数据库系列(:MySQL 视图、触发器的原理与实战

    ### 数据库系列:MySQL 视图、触发器的原理与实战 #### 视图原理与实战 ##### 一、视图的基本概念 视图(View)是基于一个或多个表的虚拟表,它本身并不存储数据,而是由一系列定义好的SQL查询构成。视图可以被...

    MySQL从入门到实战学习教程之11触发器.pptx

    MySQL触发器知识点 MySQL触发器是指在预定义的事件发生时自动执行的特殊存储过程。触发器可以与表事件相关,例如Insert、Update或Delete事件。触发器可以基于一个表创建,但是可以针对一个或多个表进行操作。触发器...

    PHP+MySQL动态网站开发全程实例

    接着,深入到MySQL数据库的管理。学习如何安装和配置MySQL服务器,创建数据库和表,以及运用SQL(结构化查询语言)来执行增删改查操作。SQL是与数据库交互的语言,掌握其基本语法,如SELECT、INSERT、UPDATE和DELETE...

    jdbc.zip_JDBC mysql java_mysql jdbc

    2. **JDBC驱动管理**:Java程序需要加载合适的JDBC驱动才能连接到MySQL数据库,这通常通过`Class.forName()`方法完成。 3. **数据库连接**:使用`DriverManager.getConnection()`方法创建数据库连接,需要提供...

    php+mysql网站开发入门及提高

    - **安装环境**:通常使用Apache或Nginx作为Web服务器,并结合MySQL数据库。 - **配置文件**:`php.ini` 文件包含了PHP的各种配置选项,如错误报告级别、上传文件大小限制等。 - **运行环境搭建**:可以通过XAMPP、...

    使用Java实现数据库编程.zip

    总的来说,这个课程将帮助学习者掌握Java与MySQL数据库的集成技术,理解JDBC的工作原理,熟悉SQL语句的编写,以及在实际项目中应用这些知识。通过系统的理论学习和实战练习,学习者将具备独立进行数据库编程的能力。

    C#开发实战1200例(第Ⅱ卷)源码+数据库

    《C#开发实战1200例(第Ⅱ卷)源码+数据库》是一部针对C#编程语言的实战教程,旨在帮助读者深入理解和掌握C#编程技术,并通过丰富的实例来提升开发技能。该资源包含了1200个具体的开发案例,涵盖了C#语言的各个方面...

    php与mysql基础教程

    在实际开发中,连接和操作MySQL数据库是PHP的主要任务。教程会讲解如何使用PHP的MySQLi或PDO扩展来建立数据库连接,执行SQL查询,处理结果集。这些技术能帮助开发者从数据库中获取数据,并将其展示在网页上,或者...

    PHP+MYSQL动态网站开发与全程实例电子课件

    2. MySQL数据库管理:课程将深入讲解MySQL的安装、配置、创建数据库、数据表、数据类型及约束。同时,还会涉及SQL查询语句,包括SELECT、INSERT、UPDATE、DELETE等操作,以及JOIN、子查询等高级用法。 3. PHP与...

    PHP+MySQL网络开发从入门到精通.pdf

    PHP是一种广泛使用的服务器端脚本语言,尤其适合于Web开发,而MySQL则是一款功能强大的关系型数据库管理系统,是许多网站和应用程序背后的数据库解决方案。 在PHP部分,书中可能涵盖以下知识点: 1. **PHP基础**:...

    mysql、python等面试题大全

    - PyMySQL或pymysql库:使用Python连接和操作MySQL数据库。 - ORM框架:如SQLAlchemy,提供面向对象的方式来操作数据库。 - DataFrame与数据库:Pandas库如何与MySQL交互,进行数据导入导出。 - 数据分析与可视...

Global site tag (gtag.js) - Google Analytics