数据库、SQL相关

写在前面

  这篇博客来和大家一起回顾和总结一下SQL相关的知识,主要分为两大部分:数据库三大范式、事务的隔离级别。关于SQL的基本增删改查这里就不再赘述了,这些大家应该都很熟练。

三大范式

  我们先来看看什么是范式:以下关于范式的解释摘抄于百度百科:

  设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。

  目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),其余范式以次类推。一般说来,数据库只需满足第三范式(3NF)就行了。

  简而言之,范式就是设计关系型数据库时需要遵循的规范,通常情况下我们所说的范式就是前三种,这篇博文也就只说明前三种。

第一范式

  所谓第一范式(1NF)是指在关系模型中,对域添加的一个规范要求,所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。即实体中的某个属性有多个值时,必须拆分为不同的属性。

  其实在我们设计关系型数据库时,第一范式是默认遵循的,因为它是关系型数据库最基本的要求。

第二范式

  第二范式是在满足第一范式的条件下,要求每个实例或记录都能被唯一的区分,选取一个能区分每个实体的属性或属性组,作为实体的唯一标识。

  一般情况下我们会选取一个主键作为唯一标识。

第三范式

  第三范式是在满足第一范式的条件下,任何非主属性都不依赖于其它非主属性,

  这样说比较难理解,我们可以举一个例子:如S1(SNO,SNAME,DNO,DNAME,LOCATION) 各属性分别代表学号, 姓名,所在系,系名称,系地址。

  关键字SNO决定各个属性。由于是单个关键字,没有部分依赖的问题,肯定遵循第二范式。但这关系肯定有大量的冗余,有关学生所在的几个属性DNO,DNAME,LOCATION将重复存储,插入,删除和修改时也将产生类似以上例的情况。 这样就会造成数据冗余。

  发生这种情况的原因其实就是非主属性LOCATION和DNAME是依赖于非主属性DNO的,唯一标识SNO并不会对这两个属性造成实际的约束,这样一来就不符合第三范式了,并且会造成大量的数据冗余,

  解决方法:分为两个关系 S(SNO,SNAME,DNO),D(DNO,DNAME,LOCATION),在这两个关系中SNO作为关系S的唯一标识,关系D中的DNO作为关系D的唯一标识,关系S中的DNO作为关系S的一个外键,用来关联关系D。

事务的隔离级别

  关于什么是数据库的事务,这里就不多说了,事务要遵循四种特征:原子性、一致性、隔离性、持久性。具体各个特征的意义是什么不知道的朋友可以百度下。

  先来看看为什么要提出事务的隔离级别,很多时候我们的数据库会被大量的用户并发的访问,会有很多并发的事务访问,这时就可能出现以下问题:

更新丢失

  两个事务都同时更新一行数据,一个事务对数据的更新把另一个事务对数据的更新覆盖了。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。

脏读

  一个事务读取到了另一个事务未提交的数据操作结果。这是相当危险的,因为很可能所有的操作都被回滚。

  举例说明:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。

  这时程序员的工资其实还是3.6万/月,但是他看到的是3.9万/月,而老板看到的是3.6万/月,程序员看到的是还未被提交的事务,这就是脏读。

不可重复读

  不可重复读(Non-repeatable Reads):一个事务对同一行数据重复读取两次,但是却得到了不同的结果。

包括以下情况:

  (1) 虚读:事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。

  举例说明:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…

  这时一个事务范围内两个相同的查询却返回了不同数据,这就虚读。

  (2) 幻读(Phantom Reads):事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据(这里并不要求两次查询的SQL语句相同)。这是因为在两次查询过程中有另外一个事务插入数据造成的。

  举例说明:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。

四种隔离级别

  为了解决以上的问题,事务的隔离级别就诞生了,我们熟知的有四种隔离级别:Read uncommitted(读未提交)、Read committed(读提交)、Repeatable read(可重复读取)、Serializable(序列化),我们依次的解释下这四种隔离级别,和他们解决了什么问题。

Read uncommitted(读未提交)

  这是最低的隔离等级,允许其他事务看到没有提交的数据。这种等级会导致脏读(Dirty Read)。

Read committed(读提交)

  读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。

  该隔离级别避免了脏读,但是却可能出现不可重复读。

Repeatable read(可重复读取)

  读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。

  避免了虚读和脏读,但是有时可能出现幻读。这可以通过“共享读锁”和“排他写锁”实现。

Serializable(序列化)

  提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。

  序列化是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、虚读,还避免了幻读。

应用

  隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。

  大多数数据库的默认级别就是Read committed,比如Sql Server , Oracle。Mysql的默认隔离级别就是Repeatable read。


笔者水平有限,若有错漏,欢迎指正,如果转载以及CV操作,请务必注明出处,谢谢!
版权声明:本文为博主原创文章,未经博主允许不得转载。

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦