做项目时由于业务逻辑的需要,必须对数据表的一行或多行加入行锁,举个最简单的例子,图书借阅系统。假设 id=1 的这本书库存为 1 ,但是有 2 个人同时来借这本书,此处的逻辑为

Select restnum from book where id =1 ;

-- 如果 restnum 大于 0 ,执行 update

Update book set restnum=restnum-1 where id=1 ;

问题就来了,当 2 个人同时来借的时候,有可能第一个人执行 select 语句的时候,第二个人插了进来,在第一个人没来得及更新 book 表的时候,第二个人查到数据了,其实是脏数据,因为第一个人会把 restnum 值减 1 ,因此第二个人本来应该是查到 id=1 的书 restnum 为 0 了,因此不会执行 update ,而会告诉它 id=1 的书没有库存 了,可是数据库哪懂这些,数据库只负责执行一条条 SQL 语句,它才不管中间有没有其他 sql 语句插进来,它也不知道要把一个 session 的 sql 语句执行完再执行另一个 session的。因此会导致并发的时候 restnum 最后的结果为 -1 ,显然这是不合理的,所以,才出现锁的概念, Mysql 使用 innodb 引擎可以通过索引对数据行加锁。以上借书的语句变为:Begin;

Select restnum from book where id =1 for update ;

-- 给 id=1 的行加上排它锁且 id 有索引

Update book set restnum=restnum-1 where id=1 ;

Commit;

这样,第二个人执行到 select 语句的时候就会处于等待状态直到第一个人执行 commit 。从而保证了第二个人不会读到第一个人修改前的数据。

那这样是不是万无一失了呢,答案是否定的。看下面的例子。

跟我一步一步来,先建立表CREATE TABLE `book` (

`id` int(11) NOT NULL auto_increment,

`num` int(11) default NULL,

`name` varchar(0) default NULL,

PRIMARY KEY (`id`),

KEY `asd` (`num`)

) ENGINE=InnoDB DEFAULT CHARSET=gbk

其中num字段加了索引

然后插入数据,运行,

insert into book(num) values(11),(11),(11),(11),(11);

insert into book(num) values(22),(22),(22),(22),(22);

然后打开 2 个 mysql 控制台窗口,其实就是建立2个session做并发操作

********************************************************************

在第一个 session 里运行:

begin;

select * from book where num=11 for update;

出现结果:

+----+-----+------+

| id | num | name |

+----+-----+------+

| 11 | 11 | NULL |

| 12 | 11 | NULL |

| 13 | 11 | NULL |

| 14 | 11 | NULL |

| 15 | 11 | NULL |

+----+-----+------+

5 rows in set

然后在第二个 session 里运行:

begin;

select * from book where num=22 for update;

出现结果:

+----+-----+------+

| id | num | name |

+----+-----+------+

| 16 | 22 | NULL |

| 17 | 22 | NULL |

| 18 | 22 | NULL |

| 19 | 22 | NULL |

| 20 | 22 | NULL |

+----+-----+------+

5 rows in set

好了,到这里什么问题都没有,是吧,可是接下来问题就来了,大家请看:

回到第一个 session ,运行:

update book set name=’abc’ where num=11;

********************************************************************************************

问题来了,session竟然处于等待状态,可是 num=11 的行不是被第一个 session 自己锁住的么,为什么不能更新呢?好了,打这里大家也许有自己的答案,先别急,再请看一下操作。

把 2 个 session 都关闭,然后运行:

delete from book where num=11 limit 3;

delete from book where num=22 limit 3;

其实就是把 num=11 和 22 的记录各删去 3 行,

然后重复 “***********************” 之间的操作

竟然发现,运行 update book set name=’abc’ where num=11; 后,有结果出现了,说明没有被锁住,

这是为什么呢,难道 2 行数据和 5 行数据,对MySQL来说,会产生锁行和锁表两种情况吗。经过跟网友讨论和翻阅资料,仔细分析后发现:

在以上实验数据作为测试数据的情况下,由于 num 字段重复率太高,只有 2 个值,分别是 11 和 12. 而数据量相对于这两个值来说却是比较大的,是 10 条, 5 倍的关系。

那么 mysql 在解释 sql 的时候,会忽略索引,因为它的优化器发现:即使使用了索引,还是要做全表扫描,故而放弃了索引,也就没有使用行锁,却使用了表锁。 简单的讲,就是 MYSQL 无视了你的索引,它觉得与其行锁,还不如直接表锁,毕竟它觉得表锁所花的代价比行锁来的小。以上问题即便你使用了 force index 强制索引,结果还是一样,永远都是表锁。

所以 mysql 的行锁用起来并不是那么随心所欲的,必须要考虑索引。再看下面的例子。select id from items where id in (select id from items where id <6) for update;

--id字段加了索引

select id from items where id in (1,2,3,4,5) for update;

大部分会认为结果一样没什么区别,其实差别大了,区别就是第一条 sql 语句会产生表锁,而第二个 sql 语句是行锁,为什么呢?因为第一个 sql 语句用了子查询外围查询故而没使用索引,导致表锁。

好了,回到借书的例子,由于 id 是唯一的,所以没什么问题,但是如果有些表出现了索引有重复值,并且 mysql 会强制使用表锁的情况,那怎么办呢?一般来说只有重新设计表结构和用新的 SQL 语句实现业务逻辑,但是其实上面借书的例子还有一种办法。请看下面代码:Set sql_mode=

'STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';

Begin;

Select restnum from book where id =1 ; -- 取消排它锁 , 设置 restnum 为 unsigned

Update book set restnum=restnum-1 where id=1 ;

If(update 执行成功 ) commit;

Else rollback;

上面是个小技巧,通过把数据库模式临时设置为严格模式,当 restnum 被更新为 -1 的时候,由于 restnum 是 unsigned 类型的,因此 update 会执行失败,无论第二个session 做了什么数据库操作,都会被回滚,从而确保了数据的正确性,这个目的只是为了防止并发的时候极小概率出现的 2 个 session 的 sql 语句嵌套执行导致数据脏读。当然最好的办法还是修改表结构和 sql 语句,让 MYSQL 通过索引来加行锁, MySQL 测试版本为 5.0.75-log 和 5.1.36-community

文章来源:http://blog.csdn.net/liyongqiao/article/details/52415516

本文由【waitig】发表在等英博客 本文固定链接:mysql 行级锁 索引唯一值 欢迎关注本站官方公众号,每日都有干货分享!

点赞 (0)赏分享 (0)

mysql 行级锁 索引_mysql 行级锁 索引唯一值相关推荐

  1. mysql uuid 索引_Mysql使用Java UUID作为唯一值时使用前缀索引测试

    Mysql可以使用字符串前缀 作为索引 以节约空间. 下面我们以 Java的UUID 生成的 32位(移除UUID中的 中划线)字符串 来做一下 测试. 表结构: CREATE TABLE `test ...

  2. mysql 页级锁写法_MYSQL中表级锁、行级锁、页级锁介绍

    一.MYSQL数据库锁的种类 在数据库系统中,可以按照锁的粒度把数据库锁分为行级锁(INNODB引擎).表级锁(MYISAM引擎)和页级锁(BDB引擎 ). 1.行级锁 行级锁是Mysql中锁定粒度最 ...

  3. mysql某个表被行锁了_MySQL中的锁(表锁、行锁)

    锁是计算机协调多个进程或纯线程并发访问某一资源的机制.在数据库中,除传统的计算资源(CPU.RAM.I/O)的争用以外,数据也是一种供许多用户共享的资源.如何保证数据并发访问的一致性.有效性是所在有数 ...

  4. mysql 并发 锁表_MySQL中的锁(表锁、行锁) 并发控制锁

    https://github.com/MrLining/mysql/wiki/MySQL%E4%B8%AD%E7%9A%84%E9%94%81%EF%BC%88%E8%A1%A8%E9%94%81%E ...

  5. mysql行锁还需要乐观锁吗_mysql行锁、表锁。乐观锁,悲观锁

    锁定用于确保事务完整性和数据库一致性. 锁定可以防止用户读取其他用户正在更改的数据,并防止多个用户同时更改相同的数据. 如果不使用锁定,数据库中的数据可能在逻辑上变得不正确,而针对这些数据进行查询可能 ...

  6. mysql某个表被行锁了_MySQL 行锁和表锁的含义及区别详解

    一.前言 对于行锁和表锁的含义区别,在面试中应该是高频出现的,我们应该对MySQL中的锁有一个系统的认识,更详细的需要自行查阅资料,本篇为概括性的总结回答. MySQL常用引擎有MyISAM和Inno ...

  7. mysql记录锁与互斥锁区别_MySQL的各种锁认知

    一.相关名词 |--表级锁(锁定整个表) |--页级锁(锁定一页) |--行级锁(锁定一行) |--共享锁(S锁,MyISAM 叫做读锁) |--排他锁(X锁,MyISAM 叫做写锁) |--悲观锁( ...

  8. mysql innodb 的锁机制_Mysql之Innodb锁机制详解

    InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION):二是采用了行级锁.关于事务我们之前有专题介绍,这里就着重介绍下它的锁机制. 总的来说,InnoDB按照不同的分类共有 ...

  9. mysql中写锁定实例_MySQL中的锁

    我学习MySQL是半路出家,刚开始接触的时候,只知道数据库的增删改查和事务,直到有一天数据库突然爆出(1205, 'Lock wait timeout exceeded; try restarting ...

最新文章

  1. html旋转代码_用CSS实现一个抽奖转盘(附详细代码+思路)
  2. Spring学习笔记之Design of DispatcherServlet
  3. UI之常用通过颜色值和透明度怎么得到最后的颜色值
  4. c++输出方块_【Blender】方块波浪动画音乐效果
  5. 京东联合vivo针对vivo X Note推出先行者计划
  6. 【报告分享】2019年中国互联网企业战投发展白皮书.pdf(附154页pdf下载链接)...
  7. 【贪心School】机器学习课程笔记
  8. 湖北湖南广州计算机科学大学排行,校友会2018中国大学计算机类专业排名
  9. 是时候关注邮件安全了
  10. 电影《阿凡达》观后感
  11. Windows凭据管理器
  12. NX二次开发-UFUN读取属性的值UF_ATTR_read_value
  13. 3款大数据bi工具,让企业数据分析更简单
  14. 网络编程——epoll
  15. 会考计算机考试查询成绩查询,会考成绩(学业水平考试成绩查询系统)
  16. Jieba库实现词性标注及小说人物角色抽取
  17. MFC 右键菜单呼出
  18. 平流式沉淀池流量计算_沉淀池设计计算(平流式
  19. C# 中2个问号的作用
  20. 再严重的感冒,马上就好【转】

热门文章

  1. python自动化框架pytest pdf_Python 自动化测试框架 unittest 和 pytest 对比
  2. 论文公式编号右对齐_论文不会写?最详细的论文排版技巧
  3. observable_Java Observable deleteObserver()方法与示例
  4. 面试题:聊一聊设计模式的基本原则
  5. 一个好的技术团队应该怎么选择开发语言
  6. VMware ubuntu20.04 server随win10自动启动与关闭
  7. IDEA中Spring Boot项目报错:There was an unexpected error (type=Not Found, status=404)
  8. Windows 10 搭建Python3 安装使用 protobuf
  9. debian apache php mysql,Debian下配置APACHE2+MYSQL5+PHP5
  10. C#中利用Linq.Dynamic实现简单的动态表达式构建查询