banner
bladedragon

bladedragon

mysql事务初识

前言:#

本次将向大家科普一些数据库事务相关知识。为什么选择数据库作为本次介绍的主角?

数据库是现代网络的重要组成部分。在信息爆炸的大数据时代,各行各业都因为大幅爆发的数据而正变得蒸蒸日上;海量,多样且在高速产生的数据正迸发着前所未有的价值。可以说,数据改变了我们的生活,也在创造我们的未来。因此,我们越来越需要更加灵活,功能强大的数据库来帮助我们存储数据,处理数据。随着时代的发展,掌握数据库操作相关技术已经是从事 ICT 行业的相关人员的必备选择。

基础概念#

在正式介绍事务之前,让我们先充充电,大致了解一些基本概念,这些在之后的学习将会用到。

  1. 什么是数据库

“数据库” 是以一定方式储存在一起、能予多个用户共享、具有尽可能小的冗余度、与应用程序彼此独立的数据集合

  1. 什么是数据库管理系统

数据库管理系统(Data Base Management System,简称 DBMS)是位于用户应用程序与操作系统之间的一层数据管理软件,是数据库系统的核心组成部分。为用户或应用程序提供访问数据库的方法:包括数据库的建立、查询、更新以及各种数据控制

数据库管理系统不仅允许单个用户查询和修改数据,也可以支持多人操作。多人操作就会出现导致操作并发,出现拥塞。

  1. 什么是锁

锁是数据库服务器用来控制数据资源被并行使用的一种机制。被锁的对象只允许持有用户操作,只有等锁释放后,其他用户才能有拥有锁的机会。

大部分数据库使用下面两种锁策略:

  • 写操作需要申请写锁,读操作申请读锁,一个表一次只能分配一个写锁,并且拒绝读请求直到写锁释放
  • 写操作需要写锁,读操作不需要锁

其中常见的数据库如 mysql 可以根据不同的存储引擎选择不同的锁策略

  1. 锁的粒度

即锁的范围,在数据库中,服务器可以在 3 个不同级别应用锁

  • 表锁

    阻止多用户同时修改同一张表的数据

  • 页锁

    阻止多用户同时修改某表中的一页

  • 行锁

    阻止多用户同时修改某表中的某一行

  1. 什么是存储引擎

数据库存储引擎是数据库底层软件组织,数据库管理系统 (DBMS) 使用数据引擎进行创建、查询、更新和删除数据,不同的存储引擎提供不同的存储机制、锁定水平等功能。

锁的行为是由存储引擎决定的,存储引擎使用不当会引发死锁现象

例如 mysql 有三种存储引擎:InnoDB MyISAM MEMORY

什么是事务#

我们从一个场景开始说起。假设一个人在银行办理转账业务,现在他要将 1 万元转账到另一个人,按一般情况,转账的过程几乎是秒级完成,但是,当我们将过程拆解,转账步骤可分为 3 步:1. 查询账户余额是否大于 1 万元,2. 从原账户上减去 1 万元,3. 在对方账户上增加 1 万元。我们可以发现,其实只要其中任意一个环节出现偏差,都有可能造成无法挽回的重大损失。因此,为了确保银行里金额不会损失,我们可以这么做:将这一万元暂时缓存下来,只有确认对方账户已完成扣除款项的操作,才将这一万元增加到对方账户;否则,所有操作都失效,原账户取消扣除款项,对方账户也不会无故增加数量,我们把这种操作称之为事务回滚,转账的操作就是一个事务。

事务的特性#

世界上总是充满各种各样的意外,很多都是我们无法左右的,例如服务器的损坏,突然的断电,系统的崩溃等等,如果没有事务的存在,数据库的操作可靠性将无法得到保证。但这些的前提是事务本身应当有完备的标准来确保事务本身的可靠性。因此,那些前辈们给事务指定了 ACID 四大特性,只有严格通过了 ACID 测试,事务才能发挥其作用。

  1. 原子性

    一个事务必须被视为不可分割的最小工作单元,整个事务种的所有操作要么全部提交成功,要么全部失败回滚,对于一个 事务来说,不可能 执行其中一部分操作。

  2. 一致性

    数据库总是从一个一致性的状态转移到另一个一致性的状态,事务不能破坏数据库的完整性以及业务逻辑的一致性。比如无论转账成功或失败,都不可能会多出或减少 1 万元,金额总数是不变的。>

  3. 隔离性

    一个事务在提交前的修改对其他事务通常是不可见的。一个事务不应影响其他事务的运行效果。

  4. 持久性

    一旦事务提交,其修改是不可逆的。

事务的 ACID 特性保证了数据库操作的安全性和可靠性,但实际操作总没有想象中那么简单。同时,添加事务也需要数据库系统进行更多额外的工作,这对数据库系统的性能提出了一定的要求。

隔离级别#

实际上,要让事务保证完全隔离依然是一件十分困难的事情 ,完全的隔离要求数据库同一时间只能执行一个事务,这样会严重影响性能。现实中,往往是多个事务并发执行。

一旦隔离性无法得到保证,数据库的读写就会面临如下情况

  1. 脏读:事务读取了未提交的数据。例如事务 A 读取了事务 B 的更新的数据,但是事务 B 回滚了,导致 A 读取的为脏数据
  2. 不可重复读:事务 A 读取同一数据两次,但是在两次之间事务 B 对该数据进行了修改并提交,导致事务 A 读取两次读取不一致
  3. 幻读:事务 A 修改全表的数据,在未提交时,事务 B 向表中插入或删除数据,导致事务 A 读取的数据与需要修改的数据不一致

对此,sql 标准中指定了四种隔离级别:

  1. 未提交读(READ UNCOMMITTED)

    事务可以读取为提交的数据,不做隔离控制

  2. 提交读(READ COMMITTED)

    不允许未提交读,一个事务开始前,只能 “看见” 已提交的事务修改,是大部分数据库的默认隔离级别(mysql 除外)

  3. 可重复读(REPEATED READ)

    保证同一事务中多次读取同一记录的结果是一致的,一般方法是事务中对符合条件的记录上排他锁,这样其他事务不能对该事务操作的数据进行修改,是 mysql 的默认隔离级别

  4. 可串行化 (SERIALIZABLE)

    是最高隔离级别,通过强制事务串行执行,在读取的每一行数据上加锁,导致其他事务不能对数据进行操作(包括增加、删除和修改),但是此级别也要注意大量锁的超时会极大地影响性能。

隔离级别脏读不可重复读幻读
未提交读(READ UNCOMMITTED)
提交读(READ COMMITTED)
可重复读(REPEATED READ)
可串行化 (SERIALIZABLE)

阻塞和死锁#

谈到并发就大致谈一下阻塞的概念

当多个事务对某一资源进行锁定时,其他没有分配到锁的事务势必等待锁的释放,这就造成了阻塞。

image

当阻塞时间达到永久,就形成了死锁。

在事务中,一旦两个及以上的事务在同一资源上相互占用,并请求锁定对方占用的资源,就会引发死锁现象。

解决思路

  1. 查询时间达到锁的超时时间后放弃请求
  2. 使用较低的隔离级别,让持有锁的时间减短,减少锁竞争
  3. 避免事务中用户交互,同时尽量顺序访问对象
  4. …….

不同的存储引擎对死锁实现了不同的死锁检测和死锁超时机制,因此大家在考虑死锁的解决方案的时候一定要结合数据库存储引擎的实现方案,并且做好事务日志,以便在问题发生时进行有效排查和高效解决。

事务的阻塞和死锁的出现实际上是多个进程并发的必然结果,大家如果想了解更多关于有关并发和锁,可以自行了解相关知识。

总结#

本篇笔者带大家初识数据库事务,初步了解了:

  1. 什么是数据库、什么是锁和事务;
  2. 事务的四个特性:原子性、隔离性、一致性、持久性;
  3. 数据库读写出现的情况:脏读、幻读和不可重复读;
  4. 事务的隔离级别;
  5. 死锁的出现和解决思路

因为篇幅有限,这里只能浅尝辄止。数据库是一门庞大复杂又极其重要的学科,如果大家有兴趣,鼓励大家自己凿渠引水,因笔者知识有限,文章内容所述难免存在谬误,恳请广大读者斧正。

知识链接#

数据库学习《SQL 学习指南》,《高性能 MYSQL》

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。