MySQL(五)事务和三大范式

  MySQL是一个关系型数据库管理系统。

一、SQL语句分类

  • 数据定义语言DDL:create,drop,alter主要为这几个操作,即对逻辑结构等有操作的,其中包括表结构、视图和索引。
  • 数据查询语言DQL:select,即查询操作,以select关键字,各种简单查询、连接查询都属于DQL。
  • 数据操纵语言DML:insert,update,delete主要为这几个操作,即对数据进行操作的,
  • 数据控制功能DCL:grant,revoke,commit,rollback主要为这几个操作,即对数据库安全性、完整性等有操作的,可以理解为权限控制。

二、MySQL事务

  MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!

四个特性,ACID:

  • A(原子性Atomicity):原子性指的是事务是一个不可分割的,要么都执行要么都不执行
  • C(一致性Consistency):事务必须使得数据库从一个一致性状态,到另一个一致性状态
  • I(隔离性Isolation):指的是一个事务的执行,不能被其他的事务所干扰
  • D(持久性Durability):持久性指的是一个事务一旦提交了之后,对数据库的改变就是永久的

并发事务处理带来的问题

  更新丢失(Lost Update):事务A和事务B选择同一行,然后基于最初选定的值更新该行时,由于两个事务都不知道彼此的存在,就会发生丢失更新问题。

  脏读(Dirty Reads):事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。

  不可重复读(Non-Repeatable Reads):事务 A 多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。

  幻读(Phantom Reads):幻读与不可重复读类似。它发生在一个事务A读取了几行数据,接着另一个并发事务B插入了一些数据时。在随后的查询中,事务A就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

幻读和不可重复读的区别:

  • 不可重复读的重点是修改:在同一事务中,同样的条件,第一次读的数据和第二次读的数据不一样。(因为中间有其他事务提交了修改)
  • 幻读的重点在于新增或者删除:在同一事务中,同样的条件,,第一次和第二次读出来的记录数不一样。(因为中间有其他事务提交了插入/删除)

创建一个事务

  事务可以划分为两大类,显示事务和隐式事务。隐式的事务很简单,比如insert、delete、update和select;显示事务就是很明显的开始和结束标记。接着来创建一个显示事务。

  1. 禁用步骤提交功能 set autocommit = 0
  2. 开启一个事务 start transaction
  3. SQL语句:update table user set money=500 where name = "张三";update table user set money=1500 where name = "李四";
  4. 结束事务,commit或者rollback。

并发事务处理带来的问题解决办法

  “更新丢失”通常是应该完全避免的。但防止更新丢失,并不能单靠数据库事务控制器来解决,需要应用程序对要更新的数据加必要的锁来解决,因此,防止更新丢失应该是应用的责任。

  “脏读” 、 “不可重复读”和“幻读” ,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决:一种是加锁:在读取数据前,对其加锁,阻止其他事务对数据进行修改。

  另一种是数据多版本并发控制(MultiVersion Concurrency Control,简称 MVCC 或 MCC),也称为多版本数据库:不用加任何锁, 通过一定机制生成一个数据请求时间点的一致性数据快照 (Snapshot), 并用这个快照来提供一定级别 (语句级或事务级) 的一致性读取。从用户的角度来看,好象是数据库可以提供同一数据的多个版本。

事务的隔离级别

事务隔离级别 脏读 不可重复读 幻读                          
读未提交(read-uncommitted) 三种现象都可能发生
不可重复读(read-committed) 只允许读取已提交的数据
可重复读(repeatable-read) 读取期间,其他事务不能更新

 

 

  • 未提交读(Read Uncommitted):最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。

  • 提交读(Read Committed):允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
  • 可重复读(Repeated Read):对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • SERIALIZABLE(可串行化):最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

查看当前数据库的事务隔离级别:show variables like 'tx_isolation'

小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

  数据库的事务隔离越严格,并发副作用越小,但付出的代价就越大,因为事务隔离实质上就是使事务在一定程度上“串行化”进行,这显然与“并发”是矛盾的。同时,不同的应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用对“不可重复读”和“幻读”并不敏感,可能更关心数据并发访问的能力。

Read uncommitted:读未提交,就是一个事务可以读取另一个未提交事务的数据。

事例:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。
分析:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。那怎么解决脏读呢?Read committed!读提交,能解决脏读问题。

Read committed:读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数据。

事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…
分析:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。那怎么解决可能的不可重复读问题?Repeatable read !
Repeatable read:重复读,就是在开始读取数据(事务开启)时,不再允许修改操作。MySQL的默认事务隔离级别
事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。
分析:重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。
什么时候会出现幻读?
事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。
那怎么解决幻读问题?Serializable!
Serializable 序列化
Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。简单来说,Serializable会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用问题。这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。

  事务隔离级别和数据访问的并发性是对立的,事务隔离级别越高并发性就越差。所以要根据具体的应用来确定合适的事务隔离级别,这个地方没有万能的原则。MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。

  这里需要注意的是:与 SQL 标准不同的地方在于InnoDB 存储引擎在 REPEATABLE-READ(可重读)事务隔离级别下使用的是Next-Key Lock 算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server)是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)已经可以完全保证事务的隔离性要求,即达到了 SQL标准的 SERIALIZABLE(可串行化)隔离级别,而且保留了比较好的并发性能。

三、MySQL三大范式

第一范式

1NF,强调的是列的原子性,也就是列不能再被分割。如电话列可进行拆分---家庭电话、公司电话

第二范式

2NF,首先是 1NF,另外包含两部分内容,一是表必须有主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。例如:订单表(订单编号、产品编号、定购日期、价格、……),"订单编号"为主键,"产品编号"和主键列没有直接的关系,即"产品编号"列不依赖于主键列,应删除该列。

第三范式

3NF,首先是 2NF,目标是确保每列都和主键列直接相关,而不是间接相关(另外非主键列必须直接依赖于主键,不能存在传递依赖).

为了理解第三范式,需要根据Armstrong公里之一定义传递依赖。假设A、B和C是关系R的三个属性,如果A-〉B且B-〉C,则从这些函数依赖中,可以得出A-〉C,如上所述,
依赖A-〉C是传递依赖。
例如:订单表(订单编号,定购日期,顾客编号,顾客姓名,……),初看该表没有问题,满足第二范式,每列都和主键列"订单编号"相关,再细看你会发现"顾客姓名"和"顾客
编号"相关,"顾客编号"和"订单编号"又相关,最后经过传递依赖,"顾客姓名"也和"订单编号"相关。为了满足第三范式,应去掉"顾客姓名"列,放入客户表中。

  

原文地址:https://www.cnblogs.com/yfstudy/p/13549721.html