mysql--->mysql的事务和锁

mysql 事务和锁

  • 什么是事务?及其特性?
    • 答:事务:是一系列的数据库操作,是数据库应用的基本逻辑单位。
    • 或者这样理解:
      事务就是被绑定在一起作为一个逻辑工作单元的SQL语句分组,如果任何一个语句操作失败那么整个操作就被失败,以后操作就会回滚到操作前状态,或者是上有个节点。为了确保要么执行,要么不执行,就可以使用事务。要将有组语句作为事务考虑,就需要通过ACID测试,即原子性,一致性,隔离性和持久性。
  • 事务特性(ACID):
    • (1)原子性:即不可分割性,事务要么全部被执行,要么就全部不被执行。
      (2)一致性或可串性。事务的执行使得数据库从一种正确状态转换成另一种正确状态
      (3)隔离性。在事务正确提交之前,不允许把该事务对数据的任何改变提供给任何其他事务,
      (4) 持久性。事务正确提交后,其结果将永久保存在数据库中,即使在事务提交后有了其他故障,事务的处理结果也会得到保存。
  • MySQL 中的事务
    • 只有使用了 Innodb 数据库引擎的数据库或表才支持事务。
    • 事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
    • 事务用来管理 insert,update,delete 语句
  • mysql事务使用
    • BEGIN或START TRANSACTION;显式地开启一个事务;
    • COMMIT会提交事务
    • ROLLBACK回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;
  • 事务隔离性及在MySQL中实践
    • 脏读(Dirty Read):
      一个事务处理过程里读取了另一个未提交的事务中的数据
    • 不可重复读(NonRepeatable Read)
      对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询的间隔期间,另外一个事务修改并提交了该数据
    • 幻读(Phantom Read)
      幻读的意思是,当某个事务在读取某个范围内的值的时候,另外一个事务在这个范围内插入了新记录,那么之前的事务再次读取这个范围的值,会读取到新插入的数据。
  • 事务的四种隔离级别
    • READ UNCOMMITTED(未提交读)。在RU的隔离级别下,事务A对数据做的修改,即使没有提交,对于事务B来说也是可见的,这种问题叫脏读。这是隔离程度较低的一种隔离级别,在实际运用中会引起很多问题,因此一般不常用。
    • READ COMMITTED(提交读)。在RC的隔离级别下,不会出现脏读的问题。事务A对数据做的修改,提交之后会对事务B可见,举例,事务B开启时读到数据1,接下来事务A开启,把这个数据改成2,提交,B再次读取这个数据,会读到最新的数据2。在RC的隔离级别下,会出现不可重复读的问题。这个隔离级别是许多数据库的默认隔离级别。
    • REPEATABLE READ(可重复读)。在RR的隔离级别下,不会出现不可重复读的问题。事务A对数据做的修改,提交之后,对于先于事务A开启的事务是不可见的。举例,事务B开启时读到数据1,接下来事务A开启,把这个数据改成2,提交,B再次读取这个数据,仍然只能读到1。在RR的隔离级别下,会出现幻读的问题。Mysql默认的隔离级别是RR,然而mysql的innoDB引擎间隙锁成功解决了幻读的问题。
    • SERIALIZABLE(可串行化)。可串行化是最高的隔离级别。这种隔离级别强制要求所有事物串行执行,在这种隔离级别下,读取的每行数据都加锁,会导致大量的锁征用问题,性能最差。
    • (隔离级别越高,它所带来的资源消耗也就越大(锁),因此它的并发性能越低)
  • 隔离级别的查看和修改
    • 查看:select @@tx_isolation;
    • 修改:set tx_isolation=’隔离级别名称;’
    • Mysql默认的隔离级别是RR
  • innoDB是mysql默认的存储引擎,默认的隔离级别是RR,并且在RR的隔离级别下更进一步,通过多版本并发控制(MVCC,Multiversion Concurrency Control )解决不可重复读问题,加上间隙锁(也就是并发控制)解决幻读问题。因此innoDB的RR隔离级别其实实现了串行化级别的效果,而且保留了比较好的并发性能。
  • 使用select…for update会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认Row-Level Lock,所以只有「明确」地指定主键,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住)。

  • mysql中的锁
    • 表锁
      对一整张表加锁,并发能力低下(即使有分读锁、写锁),一般在DDL处理时使用
    • 行锁
      只锁住特定行的数据,并发能力强,MySQL一般都是用行锁来处理并发事务。
    • GAP锁(间隙锁)
      是MySQL使用索引对行锁两边的区间进行加锁,避免其他事务在这两个区间insert的一种锁。
    • Next-Key锁
      Next-Key锁是行锁和GAP锁的合并(MySQL使用它来避免幻读)
  • MVVC(多版本并发控制)
    • Innodb中的乐观锁实现。通过它提高MySQL的读取操作的性能。并能解决MySQL的重复读问题。
    • mysql通过MVVC实现事务的可重复读
  • 事务的隔离性是通过锁实现,而事务的原子性、一致性和持久性则是通过事务日志实现。说到事务日志,不得不说的就是redo和undo。
    • redo log
      在innoDB的存储引擎中,事务日志通过重做(redo)日志和innoDB存储引擎的日志缓冲(InnoDB Log Buffer)实现。事务开启时,事务中的操作,都会先写入存储引擎的日志缓冲中,在事务提交之前,这些缓冲的日志都需要提前刷新到磁盘上持久化,这就是DBA们口中常说的“日志先行”(Write-Ahead Logging)
    • undo log主要为事务的回滚服务。在事务执行的过程中,除了记录redo log,还会记录一定量的undo log。undo log记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据undo log进行回滚操作。
  • 乐观锁
    • 乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。
    • 乐观锁在数据库上的实现完全是逻辑的,不需要数据库提供特殊的支持。一般的做法是在需要锁的数据上增加一个版本号,或者时间戳,然后按照如下方式实现:
      • 数据版本,为数据增加的一个版本标识。当读取数据时,将版本标识的值一同读出,数据每更新一次,同时对版本标识进行更新。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的版本标识进行比对,如果数据库表当前版本号与第一次取出来的版本标识值相等,则予以更新,否则认为是过期数据。
  • 悲观锁
    • 悲观锁的特点是先获取锁,再进行业务操作,即“悲观”的认为获取锁是非常有可能失败的,因此要先确保获取锁成功再进行业务操作。通常所说的“一锁二查三更新”即指的是使用悲观锁。
    • 通常来讲在数据库上的悲观锁需要数据库本身提供支持,即通过常用的select … for update(排他锁)操作来实现悲观锁。select for update获取的行锁会在当前事务结束时自动释放,因此必须在事务中使用。
    • 在数据库中,悲观锁的流程如下:
      • 在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。
        如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。 具体响应方式由开发者根据实际需要决定。
        如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。
        其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。
    • 要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。set autocommit=0;
    • 上面我们提到,使用select…for update会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认行级锁。行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住,这点需要注意。
  • 排他锁和共享锁
    • 在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。当数据对象被加上排它锁时,其他的事务不能对它读取和修改。加了共享锁的数据对象可以被其他事务读取,但不能修改。数据库利用这两 种基本的锁类型来对数据库的事务进行并发控制。
    • 排他锁:SELECT ... FOR UPDATE
    • 共享锁:SELECT ... LOCK IN SHARE MODE 
  • myisam应用表锁实现伪事务
    • 通过使用表锁定对MyISAM表进行锁定操作,以此过程来代替事务型表InnoDB,即应用表锁定来实现伪事务。实现伪事务的一般步骤如下:
      (1)对数据库中的数据表进行锁定操作,可以对多个表做不同的方式锁定,其代码格式如下:
      LOCK TABLE table_name1 lock_type1,table_name2 lock_type2,……
      (2)执行数据库操作,向锁定的数据表中执行添加、删除、修改操等操作。
      如前面提到的INSERT、UPDATE、DELETE等操作。用户可以对锁定的数据表执行上述操作,在执行过程中,该伪事务所产生的结果是不会被其他用户更改的。
  • 死锁的概念与避免
    • 即当两个或者多个处于不同序列的用户打算同时更新某相同的数据库时,因互相等待对方释放权限而导致双方一直处于等待状态。在实际应用中,两个不同序列的客户打算同时对数据执行操作,极有可能产生死锁。更具体地讲,当两个事务相互等待操作对方释放所持有的资源,而导致两个事务都无法操作对方持有的资源,这样无限期的等待被称作死锁。
    • MySQL的InnoDB表处理程序具有检查死锁这一功能,如果该处理程序发现用户在操作过程中产生死锁,该处理程序立刻通过撤销操作来撤销其中一个事务,以便使死锁消失。这样就可以使另一个事务获取对方所占有的资源而执行逻辑操作。
  • mysql 解除正在死锁的状态
    • 1.查询是否锁表
      show OPEN TABLES where In_use > 0;
      2.查询进程(如果您有SUPER权限,您可以看到所有线程。否则,您只能看到您自己的线程)
      show processlist
      3.杀死进程id(就是上面命令的id列)
      kill id
    • 查看下在锁的事务
      SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
      2.杀死进程id(就是上面命令的trx_mysql_thread_id列)
      kill 线程ID
  • 如何尽可能避免死锁
    • 1)以固定的顺序访问表和行。比如对第2节两个job批量更新的情形,简单方法是对id列表先排序,后执行,这样就避免了交叉等待锁的情形;又比如对于3.1节的情形,将两个事务的sql顺序调整为一致,也能避免死锁。
      2)大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。
      3)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。
      4)降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。
      5)为表添加合理的索引。可以看到如果不走索引将会为表的每一行记录添加上锁,死锁的概率大大增大。

猜你喜欢

转载自www.cnblogs.com/frankltf/p/8996826.html