数据锁

需求: 有一个账户,两个人在同一时间要对此账户操作,A要对账户充值100块,B要从账户中取出100块.操作前都要先看一下账户的 余额然后再操作.

-- 窗口1 用户进行充值

-- 充值前 先查看余额
set @m=0; SELECT money into @m from account where id = 1; select @m; -- 看到余额后 充值100 块
update account set money = @m + 100 where id = 1; SELECT * from account; --------------------------------------------------------------
-- 窗口2 用户进行取款 -- 取款前 先查看余额
set @m=0; SELECT money into @m from account where id = 1; select @m; -- 看到余额后 取款100 块
update account set money = @m - 100 where id = 1; SELECT * from account;

示例

1. 锁的基本概念

  当并发事务同时访问一个资源时,有可能导致数据不一致,因此需要一种机制来将数据访问顺序化,以保证数据库数据的一致性。

2. 锁的基本类型

  多个事务同时读取一个对象的时候,是不会有冲突的。同时读和写,或者同时写才会产生冲突。因此为了提高数据库的并发性能,通常会定义两种锁:共享锁和排它锁。

  2.1 共享锁(Shared Lock,也叫S锁)

    共享锁(S)表示对数据进行读操作。因此多个事务可以同时为一个对象加共享锁。(如果试衣间的门还没被锁上,顾客都能够同时进去参观)

  2.2 排他锁(Exclusive Lock,也叫X)

    排他锁(X)表示对数据进行写操作。如果一个事务对 对象加了排他锁,其他事务就不能再给它加任何锁了。(某个顾客把试衣间从里面反锁了,其他顾客想要使用这个试衣间,就只有等待锁从里面给打开了).

3. 实际开发中常见的两种锁:

  3.1悲观锁 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block(阻塞)直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制.

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

设置完autocommit后,我们就可以执行我们的正常业务了。具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 0.开始事务
start transaction;
 
-- 1.查询账户余额
set @m = 0; -- 账户余额
select money into @m from account where id = 1 for update;
select @m;
 
-- 2.修改账户余额
update account set money = @m -100 where id = 1;
 
select FROM account where id = 1;
-- 3. 提交事务
commit;

在另外的查询页面执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 0.开始事务
start transaction;
 
-- 1.查询账户余额
set @m = 0; -- 账户余额
select money into @m from account where id = 1 for update;
select @m;
 
-- 2.修改账户余额
update account set money = @m +100 where id = 1;
 
select FROM account where id = 1;
-- 3. 提交事务
commit;

会发现当前查询会进入到等待状态,不会显示出数据,当上面的sql执行完毕提交事物后,当前sql才会显示结果.

注意1:在使用悲观锁时,如果表中没有指定主键,则会进行锁表操作.

注意2: 悲观锁的确保了数据的安全性,在数据被操作的时候锁定数据不被访问,但是这样会带来很大的性能问题。因此悲观锁在实际开发中使用是相对比较少的。  

   3.2 乐观锁, 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。 

使用乐观锁的两种方式:

  1.使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现 方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录 的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数 据。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
-- 1.查询账户余额
set @m = 0; -- 账户余额
select money into @m from account where id = 1 ;
select @m;
-- 2.查询版本号
set @version = 0; -- 版本号
select version into @version from account where id = 1 ;
select @version;
 
-- 3.修改账户余额
update account set money = @m -100,version=version+1 where id = 1 and version = @version;
 
select FROM account where id = 1;

  2.乐观锁定的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳 (datatime), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。

悲观锁与乐观锁的优缺点:

  两种锁各有其有点缺点,不能单纯的讲哪个更好.

    乐观锁适用于写入比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。

    但如果经常产生冲突,上层应用会不断的进行重试操作,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适.

最新文章

  1. CLR via C# 3rd - 08 - Methods
  2. linux---mysql远程访问
  3. 关于C转汇编(转自网上)
  4. 5.HBase In Action 第一章-HBase简介(1.1.3 HBase的兴起)
  5. [Nginx 1] Nginx简介
  6. C#链接远程SQL 服务器方法
  7. qemu核心机制分析-协程coroutine
  8. Day2------字符编码
  9. Redis批量导入数据的方法
  10. Vue.js 入门教程
  11. CSS实现div高度自适应
  12. 手动编译websocket-sharp项目使其支持.net core
  13. ssh远程登陆和MTR测试
  14. JS 遍历JSON中每个key值
  15. mysql的mysqli异步与php的携程
  16. 浅析libuv源码-编译启动
  17. Flex布局兼容知识点总结
  18. Bootstrap-Plugin:下拉菜单(Dropdown)插件
  19. Server 2008 r2 多用户远程桌面配置
  20. 在Ubuntu上开启MongoDB的IP Security

热门文章

  1. flask BytesIO() 多个文件打包下载 zipfile
  2. 公司HBase基准性能测试之结果篇
  3. Nginx设置网站维护页面
  4. ELKStack之消息队列
  5. POJ1797 Heavy Transpotation
  6. zookeeper源码导入
  7. 理解CopyOnWriteArrayList
  8. mysql 权限管理 针对某个库 某张表 授权 tables_priv表
  9. ubuntu系统下怎么安装gcc编译器
  10. Linux实验楼学习之一