事务的ACID属性

  • 原子性(Atomicity):事务是一个原子操作,对数据的修改,要么全部成功,要么全部失败。
    • 比如在一个方法中,开启了事务,方法里面有多个修改或删除数据的操作,方法里面的所有操作要么同时成功,要么同时失败。
    • 主要是操作层面
  • 一致性(Consistent) ,在事务开始和完成时,数据都必须保持一致状态。
    • 比如在方法中,有多个修改或删除数据库的操作,在事务提交后,要求所有的操作过的数据都是成功的,状态都是一致的。
    • 主要是数据层面
  • 隔离性(Isolation),保证事务在不受外部并发操作影响的“独立”环境执行。
    • 例如线程1开启了事务A,线程2开始了事务B,两个事务都对库存做操作。事务A查询了库存的值是10,然后事务B把库存修改为5,事务A再次去查询库存得到的值是5,读到了其他事务修改的值,但我们希望的是,在一个事务里面,同一个查询得到一样的结果而不是会随着外部的修改而改变,所以有了隔离性。
    • 事务处理过程中的中间状态对外部是不可见的。
  • 持久性(Durable) ,事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。
    • MySql会把数据保存到磁盘。

并发事务处理带来的问题

  • 更新丢失或者脏写,当有多个事务去修改同一行,因为事务有隔离性,每个事务都不知道其他事务的存在,就会发生丢失更新的问题,最后的更新覆盖了其他事务所做的更新。
    • 例如有2个事务去修改库存,事务A查询库存是10,然后库存减去5。此时事务B也去查询库存,也是10,然后把库存减1。事务A提交,然后提交事务B,数据库最后的库存是9,我们想要的最后结果是4,所以出现了覆盖前面的更新。
  • 脏读,事务A读取到了事务B已经修改但尚未提交的数据。如果B事务回滚,A读取的数据无效,不符合一致性要求
  • 不可重读,事务A内部的相同查询语句在不同时刻读出的结果不一致,不符合隔离性。
  • 幻读,事务A读取到了事务B提交的新增数据,不符合隔离性

事务隔离级别

“脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。

隔离级别 脏读 不可重复读 幻读
读未提交(Read Uncommitted) 可能 可能 可能
读已提交(Read Committed) 不可能 可能 可能
可重复读(Repeatable Read) 不可能 不可能 可能
可串行化(Serializable) 不可能 不可能 不可能

数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上“串行化”进行。
Mysql默认的事务隔离级别是可重复读,用Spring开发程序时,如果不设置隔离级别默认用Mysql设置的隔离级别,如果Spring设置了就用已经设置的隔离级别

锁详解

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

锁分类

  • 从性能上分为乐观锁(用版本对比来实现)和悲观锁
  • 从对数据库操作的类型分,分为读锁和写锁(都属于悲观锁)
    • 读锁(共享锁):多个读操作可以同时进行而不会相互影响,读读共享
    • 写锁(排它锁):当写操作没有完成前,会阻塞其他的读和写操作。
  • 从对数据操作的粒度分,分为表锁和行锁
    • 表锁:每次操作锁住整张表。开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;一般用在整表数据迁移的场景。
    • 行锁:每次操作锁住一行数据。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高。

      表锁(MyIsAM)

      加读锁

      客户端A:
      截屏2022-05-01 03.37.17.png
      客户端B,能正常读。
      截屏2022-05-01 03.38.03.png
      客户端B进行写入会阻塞:
      截屏2022-05-01 03.48.50.png

加写锁

客户端A:
截屏2022-05-01 03.39.13.png
客户端B再次去查询,会阻塞:
截屏2022-05-01 03.40.27.png
但在客户端A支持增删改查都没问题
截屏2022-05-01 03.45.11.png

  • MyISAM表的读操作(加读锁) ,不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作
  • MylSAM表的写操作(加写锁) ,会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作

行锁(InnoDB)

每次操作锁住一行数据。
客户端A开启事务更新不提交
截屏2022-05-01 03.56.11.png
客户端B去更新同一条记录,会阻塞
截屏2022-05-01 03.57.37.png
客户端B去更新其他行的记录,正常更新:
截屏2022-05-01 03.58.40.png

InnoDB与MYISAM的最大不同有两点

  • InnoDB支持事务(TRANSACTION)
  • InnoDB支持行级锁


锁总结

  • MyISAM在执行查询语句SELECT前,会自动给涉及的所有表加读锁,在执行update、insert、delete操作会自动给涉及的表加写锁。
  • InnoDB在执行查询语句SELECT时(非串行隔离级别),不会加锁。但是update、insert、delete操作会加行锁。
  • 就是读锁会阻塞写,但是不会阻塞读。而写锁则会把读和写都阻塞