ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

事务和锁机制

2022-09-15 00:01:29  阅读:203  来源: 互联网

标签:事务 并发 幻读 数据库 T1 索引 机制


事务的使用

1. 什么是事务
    (事务)transactions, 顾名思义就是要做或所做的事情, 数据库事务指的是作为单个工作单位执行的一系列操作(sql语句).
    这些操作要么全部执行. 要么全部都不执行


2. 为什么要有事务
    将一系列sql语句放到一个事务里面有两个目的:
        1. 为数据库操作提供一个从失败中恢复到正常状态的方法. 同时提供了数据库即使在异常状态下仍能保持一致性的方法
        2. 当多个应用该程序在并发访问数据库时. 可以在这些应用程序之间你提供一个隔离方法. 以防止彼此的操作互相干扰


事务有四大特性(ACID)

   1. **原子性(atomicity)**
      事务的一个整体被执行. 包含在其中的对数据库的操作要么全部被执行. 要么都不执行
   2. **一致性(consistency)**
      事务应该确保数据库的状态从一个一直状态到另一个一直状态.例如转账行为中.一个人减了50元. 另外一个人就应该加上50元 而不是40元
      其他一致状态的含义是数据库中的数据应该满足完整性约束. 例如字段约束不能为负数. 事务执行完毕后的该字段页同样同样不能是负数
   3. **隔离性(Isolation)**
      多个事务并发执行时. 一个事务的执行不应影响其他事务执行
   4. **持久性(Durability)**
      一个事务一旦提交. 他对数据库的修改应该永久保存在数据库中

mysql事务的三种运行模式

  1. 自动提交事务(隐式开启. 隐式提交)
  2. 隐式事务(隐式开启. 显示提交)
  3. 显示事务(显示开启. 显示提交)

开启事务:
start transaction; -- 开启事务
update test set age = 32312 where id = 1; -- 修改数据
commit; -- 提交
rollback; -- 回滚

commit 和 rollback事务都结束了

pymysql中隐式开启. 显示提交

try:
cursor.execute(sql_1)
cursor.execute(sql_2)
cursor.execute(sql_3)
except Exception as e:
connect.rollback() # 事务回滚
print("事务处理失败", e)
else:
connect.commit() # 事务提交
print('事务处理成功', cursor.rowcount) # 关闭连接
cursor.close()
connect.close()

事务的控制语句

start transaction(或 begin):显式开始一个新事务 #开启事务
savepoint:分配事务过程中的一个位置,以供将来引用 #临时存档
commit:永久记录当前事务所做的更改 #提交
rollback:取消当前事务所做的更改 #回滚
rollback to savepoint:取消在 savepoint 之后执行的更改 #回到存档点
release savepoint:删除 savepoint 标识符 #删除临时存档
set autocommit:为当前连接禁用或启用默认 autocommit 模式

事务的使用原则

  1. 保持事务短小
  2. 尽量避免事务中rollback
  3. 尽量避免savepoint
  4. 显式声明打开事务
  5. 默认情况下,依赖于悲观锁,为吞吐量要求苛刻的事务考虑乐观锁
  6. 锁的行越少越好,锁的时间越短越好

数据库读现象

数据库读现象: 多个事务并发执行的时候. 在读数据方面可能会遇到各种各样的问题: 脏读. 不可重复读. 幻读

脏读(dirty read)

​ 事务T2更新了一行记录的内容,但是并没有提交所做的修改。
事务T1读取更新后的行,然后T1执行回滚操作,取消了刚才所做的修改。此时T1所读取的行就无效了,称之为脏数据

不可重复读(nonrepeatable read)

​ 事务T1读取一行记录,紧接着事务T2修改了T1刚才读取的那一行记录并且提交了。
然后T1又再次读取这行记录,发现与刚才读取的结果不同。这就称为“不可重复”读,因为T1原来读取的那行记录已经发生了变化。

幻读(phantom read)

​ 事务T1读取或修改了指定的where子句所返回的结果集。
然后事务T2新插入一行记录,这行记录恰好可以满足T1所使用的查询条件中的WHERE 子句的条件。
然后T1又使用相同的查询再次对表进行检索,但是此时却看到了事务T2刚才插入的新行或者发现了处于where子句范围内,但却未曾修改过的记录。
就好像“幻觉”一样,因为对T1来说这一行就像突然出现的一样。
一般解决幻读的方法是增加范围锁RangeS,锁定检锁范围为只读,这样就避免了幻读。

解决方案

​ 其实,脏写、脏读、不可重复读、幻读,都是因为业务系统会多线程并发执行,每个线程可能都会开启一个事务,每个事务都会执行增删改查操作。
​ 然后数据库会并发执行多个事务,多个事务可能会并发地对缓存页里的同一批数据进行增删改查操作,于是这个并发增删改查同一批数据的问题,可能就会导致我们说的脏写、脏读、不可重复读、幻读这些问题。
​ 所以这些问题的本质,都是数据库的多事务并发问题,那么为了解决多事务并发带来的脏读、不可重复读、幻读等读等问题,
​ 数据库才设计了锁机制、事务隔离机制、MVCC 多版本隔离机制,用一整套机制来解决多事务并发问题

mysql锁机制即其他知识点

1. 什么是锁
    锁是计算机协调多个进程或线程并发访问某一资源的机制

2. 为什么要加入锁机制
    当并发事务访问一个共享资源的时. 有时候可能导致数据不一致.数据无效等问题
    而锁机制可以将并发的数据访问顺序化,以保证数据库中数据的一致性与有效性
    此外,锁冲突也是影响数据库并发访问性能的一个重要因素,锁对数据库而言显得尤其重要,也更加复杂。

3. 怎么加锁
    对于insert. update. delete语句. innodb会自动给涉及的数据加锁. 而且是排他锁.
    对于普通的select语句. innodb不会加任和锁. 需要我们手动自己加. 可以加两种类型的锁
    1. 共享锁: select ... lock in share mode;  -- 查出的记录行都会被锁住
    2. 排他锁: select ... for update;  -- 查出的记录行都会被锁住

锁介绍

共享锁(Share lock): 共享锁就是多个事务对于同一数据可以共享一把锁,获准共享锁的事务只能读数据,
不能修改数据直到已释放所有共享锁,所以共享锁可以支持并发读
如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁或不加锁(在其他事务里一定不能再加排他锁,但是在事务T自己里面是可以加的)
排它锁(eXclusive Lock): 排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,
其他事务就不能再对该行加任何类型的其他他锁(共享锁和排他锁),但是获取排他锁的事务是可以对数据就行读取和修改。

锁中出现的一些问题

innodb行锁的特性:

  1. 如果没有命中索引,InnoDB 会通过隐藏的聚簇索引来对记录加锁。
    也就是说:如果不通过索引条件检索数据,那么InnoDB将对表中所有数据加锁,实际效果跟表级锁一样。
    例如: id没有primary key主键索引. 锁行相当于锁表
  2. 如果一条sql语句操作了主键索引. mysql就会锁住这条语句就会命中这条主键索引
  3. 如果一条sql语句操作了非主键索引. mysql会先锁住该非主键索引. 在锁定相关的主键索引

在实际应用中要非常注意innodb行锁这个特性. 不然的话. 可能出现各种锁冲突. 从而影响并发性能
1、在不通过索引条件查询的时候,InnoDB 的效果就相当于表锁
2、当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论 是使用主键索引、唯一索引或普通索引,InnoDB 都会使用行锁来对数据加锁。
3、由于 MySQL 的行锁是针对索引加的锁,不是针对记录加的锁,所以即便你的sql语句访问的是不同的记录行,但如果命中的是相同的被锁住的索引键,也还是会出现锁冲突的。
4、即便在条件中使用了索引字段,但是否使用索引来检索数据是由 MySQL 通过判断不同 执行计划的代价来决定的,如果 MySQL 认为全表扫 效率更高,比如对一些很小的表,它 就不会使用索引,这种情况下 InnoDB 将锁住所有行,相当于表锁。因此,在分析锁冲突时, 别忘了检查 SQL 的执行计划,以确认是否真正使用了索引

死锁问题

  1. 简单情况
    事务一:
    begin;
    select * from employee where id = 1 for update; -- 第一步(加上了排它锁)

    update employee set name = "xxx" where id = 3; -- 第三步, 阻塞

    事务二:
    begin;
    delete from employee where id = 3; -- 第二步(默认排它锁)

    update employee set name = "yyy" where id = 1; -- 第四部, 阻塞

  2. 复杂情况: 有innodb行锁特性导致的 -> 如果命中辅助索引. 则先锁住辅助索引在锁住主键索引
    两个事务. 每个事务就一个sql语句. 但是每个事务都是命中辅助索引. 交叉将对方锁住. 导致出现了死锁现象

三种行锁算法

  1. Record lock : 单个行记录上的锁
  2. Gap lock: 间隙锁. 锁定一个范围. 但是不包括记录本身.gap锁的目的是为了防止同一事务的两次当前读. 出现幻读现象
  3. Next-lock: 等于Record Lock结合Gap Lock. 注意: Innodb储存引擎还会对辅助索引下一个键值加上GAP lock

​ innodb采用的都是next-key-lock. 主要目的是解决幻读的问题.

事务隔离机制

​ mysql默认使用的是Repeatable read机制 + next-lock解决脏读. 幻读等各种问题
​ django框架中采用的Read committed机制. 其自身框架会解决不可重复读和幻读问题

多版本并发控制MVCC

快照读: 不加锁读
当前读: 加锁读取

​ MVCC好处: 读不加锁,读写不冲突
​ MVCC原理分析: https://www.cnblogs.com/linhaifeng/articles/14424181.html

标签:事务,并发,幻读,数据库,T1,索引,机制
来源: https://www.cnblogs.com/Maxs-message/p/16695067.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有