二、mysql中Innodb的锁介绍

介绍:

mysql中常说的锁分为:乐观锁、悲观锁、排他锁、共享锁、表锁、行锁,这些锁并不是平行关系,其实是包含关系,如悲观锁的实现方式是排他锁与共享锁,下面来一一介绍一下。

一、为什么需要锁(并发控制)?

在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题
典型的冲突有:
1.丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户A把值从6改为2,用户B把值从2改为6,则用户A丢失了他的更新。
2.脏读:当A事务读取其B事务为提交的记录时,B事物回滚了,就会发生脏读取。
例如:事务B执行过程中修改了数据X,在未提交前,事务A读取了X,而事务B却回滚了,这样事务A就形成了脏读。

为了解决这些并发带来的问题。 我们需要引入并发控制机制。

二、 并发控制机制

锁,即给我们选定的目标数据上锁,使其无法被其他程序修改。

  1. 悲观锁:指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态
  2. 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。乐观锁不能解决脏读的问题。

三、乐观锁

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

下面就从两个步骤来介绍乐观锁的实现:设计数据库与具体操作
  1. 设计数据库表task
    有三个字段,分别是id,value、version,主要就是靠version来做版本控制,假设出事话version值为0;

  2. 每次更新task表中的value字段时,为了防止发生冲突,需要这样操作,假设id为1
    2.1 获取version值。 select version from task where id=1,用oldVersion来保存。
    2.2 修改其值是不仅确定id,更需要确定其版本号是否发生变化。

    update task set value = 修改值, version = oldVersion + 1 where version = oldVersion;
    如果version发生变化,则表示在此期间被其他人做过修改。

假设a用户在获取version值 0 后准备修改的过程中,被b用户抢占了资源完成了修改并释放资源那么此时version的值是1,此时b用户获取资源进行更新时发现 1=0 不通过则修改失败。

四、悲观锁

  1. 与乐观锁相对应的就是悲观锁了。悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作,这点跟java中的synchronized很相似,所以悲观锁需要耗费较多的时间。

  2. 另外与乐观锁相对应的,悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了。

  3. 说到这里,由悲观锁涉及到的另外两个锁概念就出来了,它们就是共享锁与排它锁。共享锁和排它锁是悲观锁的不同的实现,它俩都属于悲观锁的范畴。

五、共享锁

  1. 共享锁又称读锁 read lock,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。
  2. 如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获得共享锁的事务只能读数据,不能修改数据。
  3. 加锁方式:select * from student where id =1 lock in share mode;
    1. 需要取消mysql 自动mysql数据库自动提交事物
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+

--取消自动提交事物
mysql> set autcommit=off;
ERROR 1193 (HY000): Unknown system variable 'autcommit'
mysql> set autocommit = off;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | OFF   |
+---------------+-------+
1 row in set (0.00 sec)
  1. 进行测试需要打开两个终端同时访问一条数据来共享锁,看能不能加载数据

    4.1打开终端1开测试,关闭自动提交的来测试共享锁。

--开始会话
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
--查询数据并加锁
mysql> select * from student where id =1 lock in share mode;
+----+----------+---------+----------------+------------+----------+
| id | stu_name | stu_age | stu_email      | stu_height | class_id |
+----+----------+---------+----------------+------------+----------+
|  1 | 张三     |      16 | zhangsan@email |        145 |        1 |
+----+----------+---------+----------------+------------+----------+
1 row in set (0.00 sec)
4.2 打开终端2查询数据并加共享锁
mysql> select * from student where id =1 lock in share mode;
+----+----------+---------+----------------+------------+----------+
| id | stu_name | stu_age | stu_email      | stu_height | class_id |
+----+----------+---------+----------------+------------+----------+
|  1 | 张三     |      16 | zhangsan@email |        145 |        1 |
+----+----------+---------+----------------+------------+----------+
1 row in set (0.00 sec)

有人可能会有疑问了,终端2并没有开启会话跟关闭自动提交,其实这个主要测试的是共享锁是可一同时加在一条数据上,因为终端1关闭了自动提交,它并没有结束事物,此时终端2可以进行加锁查询,已经达到了效果。即使终端2关闭了自动提交事物,也是一样可以查询数据的。

4.3 测试mysql共享锁状态下无法修改数据;
--终端1对数据加共享锁,并且尚未提交事物
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from student where id =1 lock in share mode;
+----+----------+---------+----------------+------------+----------+
| id | stu_name | stu_age | stu_email      | stu_height | class_id |
+----+----------+---------+----------------+------------+----------+
|  1 | 张三     |      16 | zhangsan@email |        145 |        1 |
+----+----------+---------+----------------+------------+----------+
1 row in set (5.61 sec)

--终端2此时来修改终端1锁定的数据时由于共享锁尚未释放所以无法之心。
mysql> update student set stu_age=11 where id =1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

--锁等待超时。

终结:共享锁状态下可以访问数据,但是不能修改数据。

六、排他锁

  1. 排他锁,顾名思义就是排除我之外的其他其他事物,也就是说只能我来操作我已经锁定的数据范围,可以查询修改,但是别人不行。假设:A对数据添加排他锁时,B无法对数据进行任何操作只能等待A释放资源。

  2. 这里就会疑问了修改加锁也就是了,为什查询也要加锁呢,因为防止我在读取数据是别人来添加写锁,这样的我就无法访问了。

  3. 加锁方式:InnoDB引擎默认对 update、insert、delete添加排他锁,查询是需要在末尾添加 foru pdate;例如: select * from student where id =1 for update;

  4. 下面来进入测试阶段,同样需要打开两个终端,取消自动提交事物。

    4.1 测试排他锁执行过程中未提交状态,另一个事物只能等待。

--终端1开启会话状态,并对数据添加排他锁
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
--查询数据并且添加锁
mysql> select * from student where id =1 for update;
+----+----------+---------+----------------+------------+----------+
| id | stu_name | stu_age | stu_email      | stu_height | class_id |
+----+----------+---------+----------------+------------+----------+
|  1 | 张三     |      16 | zhangsan@email |        145 |        1 |
+----+----------+---------+----------------+------------+----------+
1 row in set (0.00 sec)

--------------------------------------------------
--终端2执行查询数据并添加排他锁状态
mysql> select * from student where id =1 for update;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
--终端二执行锁等待超时。

---------终端2修改操作也是一样无法执行的,因为修改操作默认也是添加排他锁的
mysql> update student set stu_age=11 where id =1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

----顺便测试一下select 不加锁状态下是可以执行的,因为他跟谁都没有竞争
mysql> select * from student where id =1;
+----+----------+---------+----------------+------------+----------+
| id | stu_name | stu_age | stu_email      | stu_height | class_id |
+----+----------+---------+----------------+------------+----------+
|  1 | 张三     |      16 | zhangsan@email |        145 |        1 |
+----+----------+---------+----------------+------------+----------+
1 row in set (0.00 sec)

终结:添加排锁的情况下,别的锁无法进入包括共享锁也是一样需要等待排他所释放资源才行。不过默认的查询是可以的,因为他是没有锁的。

七、表锁与行锁

  1. 首先需要明确的是表锁与行锁这两个概念跟悲观锁、乐观锁、共享锁、排他锁是有取别的因为这个几个锁是表示他的效果,而表锁与行锁是指这些锁操作的效果,例如select * from student where id =1 for update;这句sql表示的是排他锁,在innodb下是行锁,表示至锁定这一行的数据,然而在myisam引擎下表示的表锁,在资源未释放之前其他事物对这张表的操作都只能等待。

  2. MYISAM引擎默认是表锁,也就是说不管你操作过程中是添加、修改、删除都是针对整张表加锁的。

  3. InnoDB引擎支持行锁,但是是建立在索引的基础上,也就是说它的where条件中的列必须是索引列。然而并不是所有的索引列都是添加的行锁,在特殊情况下普通索引列会导致行表变化为表锁。这个在后面会有介绍的。

  4. 表锁的优势:表锁适合查询操作。

1.我个人感觉首先表所不是建立索引的基础上进行的,因为索引是需要消耗物理空间的。如同一本书它的目录也是消耗纸张的增加书本的厚度。
2.单行查询还好,如果出现多行查询时,某一行数据添加上排他锁的情况下只能选择等待。

  1. 行锁的优势:行锁适合修改操作,只锁定我需要修改的部分,不对其他造成影响,适合并发操作。例如同时有两个事物:a操作123行,b操作456行之间是互不干扰的。

八、死锁

  1. 所谓死锁:是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。

  2. 产生死锁的必要条件

(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

  1. 下列方法有助于最大限度地降低死锁:

(1)按同一顺序访问对象。
(2)避免事务中的用户交互。
(3)保持事务简短并在一个批处理中。
(4)使用低隔离级别。
(5)使用绑定连接。

4.解决死锁的方式:
https://blog.csdn.net/qq_33352259/article/details/81582941

来源:
https://blog.csdn.net/qq_33352259/article/details/81582941
https://blog.csdn.net/weixin_38756990/article/details/72855927
https://blog.csdn.net/Somhu/article/details/78775198

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值