数据库问答类题目1

部分转载自:
http://www.51testing.com/html/53/15150753-3722362.html
https://www.zhihu.com/question/55231277/answer/143456464
https://www.cnblogs.com/frankielf0921/p/5923946.html
http://blog.csdn.net/MiracleWW/article/details/53352738
http://blog.csdn.net/dinglang_2009/article/details/5951428
http://blog.csdn.net/u010648555/article/details/54235087
https://www.cnblogs.com/ismallboy/p/5574006.html
http://blog.csdn.net/hexieshangwang/article/details/47189213
http://blog.csdn.net/qq_33290787/article/details/51924963

1、存储过程是什么?有什么用?有什么优缺点?

存储过程是一个预编译的SQL语句集,优点是允许模块化的设计,就是说只需要创建一次,以后在该程序中就可以调用多次。如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语句执行要快。可以一个命令对象来调用存储过程。

存储过程(Stored Procedure)是一组为了完成特定功能的 SQL 语句集,经编译后存储在数据库中。用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是 SQL 语句和可选控制流语句的预编译集合,以一个名称存储并作为一个单元处理。存储过程存储在数据库内,可由应用程序通过一个调用执行,而且允许用户声明变量、有条件执行以及其它强大的编程功能。存储过程在创建时即在服务器上进行编译,所以执行起来比单个SQL语句快。

存储过程的优点:
(1)存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译, 而一般SQL语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度
(2)当对数据库进行复杂操作时(如对多个表进行 Update, Insert, Query, Delete 时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用;
(3)存储过程可以重复使用,可减少数据库开发人员的工作量;
(4)安全性高,可设定只有某此用户才具有对指定存储过程的使用权。

存储过程的缺点:
(1)如果更改范围大到需要对输入存储过程的参数进行更改,或者要更改由其返回的数据,则您仍需要更新程序集中的代码以添加参数、更新 GetValue() 调用, 等等, 这时候估计比较繁琐了。
(2)可移植性差。 由于存储过程将应用程序绑定到 SQL Server,因此使用存储过程封装业务逻辑将限制应用程序的可移植性。
(3)大量采用存储过程进行业务逻辑的开发致命的缺点是很多存储过程不支持面向对象的设计,无法采用面向对象的方式将业务逻辑进行封装,从而无法形成通用的可支持复用的业务逻辑框架。
(4)代码可读性差,相当难维护。区别一,存储过程保存在数据库里面,存储过程可以被连接此数据库的所有程序设计语言和程序使用,自定义函数不能。


2、触发器(trigger)的作用?

触发器是一种用来保障参照完整性的特殊的存储过程,它维护不同表中数据间关系的有关规则
当对指定的表进行某种特定操作(如:Insert,Delete或Update)时,触发器产生作用。
触发器可以调用存储过程,常常用于加强数据的完整性约束和业务规则等

触发器是一类特殊的存储过程,主要是通过事件来触发而被执行的。它可以强化约束,来维护数据的完整性和一致性,可以跟踪数据库内的操作从而不允许未经许可的更新和变化。可以联级运算。如,某表上的触发器上包含对另一个表的数据操作,而该操作又会导致该表触发器被触发。

创建触发器:

Create Trigger 触发器名
On 表名
For {insert,update,delete}
As
Begin

SQL语句(块) 

End

触发器的限制:

●一个表最多只能有三个触发器,insert,update,delete
●每个触发器只能用于一个表
不能对视图、临时表创建触发器
Truncate table能删除表,但不能触发触发器
能将触发器用于系统表

合理地使用触发器对性能的影响是正面的。在设计和使用触发器时,经常地用sp_depends命令了解对象所关联的触发器是有好处的,该命令能列出触发器影响的所有对象、表和视图等。

在定义几类数据库对象的时候,对存储过程、索引和触发器要给予特别的注意,尤其存储过程,它设计的好坏对数据库性能的影响很大。

说明:Sybase触发器使用的两个测试表:Deleted表和Inserted表,它们都是临时表,其结构与触发器的基表结构相同,用来存放与修改相关的数据行。

常见的触发器有三种:分别应用于Insert,Update,Delete事件

使用触发器的优点

(1)触发器是自动的:它们在对表的数据作了任何修改(比如手工输入或者应用程序采取的操作)之后立即被激活。

(2)触发器可以通过数据库中的相关表进行层叠更改。例如,可以在 titles 表的 title_id 列上写入一个删除触发器,以使其它表中的各匹配行采取删除操作。该触发器用 title_id 列作为唯一键,在 titleauthor、sales 及 roysched 表中对各匹配行进行定位。

(3)触发器可以强制限制,这些限制比用 CHECK 约束所定义的更复杂。与 CHECK 约束不同的是,触发器可以引用其它表中的列。例如,触发器可以回滚试图对价格低于 10 美元的书(存储在 titles 表中)应用折扣。


3、索引的作用?它的优点缺点是什么?

索引就是一种特殊的查询表,数据库的搜索引擎可以利用它加速对数据的检索。它很类似与现实生活中书的目录,不需要查询整本书内容就可以找到想要的数据。索引可以是唯一的,创建索引允许指定单个列或者是多个列。缺点是它减慢了数据录入的速度,同时也增加了数据库的尺寸大小。

1.什么是索引:

索引就像是书的目录,是与表或视图关联的磁盘上结构,可以加快从表或视图中检索行的速度。索引中包含由表或视图中的一列或多列生成的键。这些键存储在一个结构(BTree)中,使SQL可以快速有效地查找与键值关联的行

2.为什么要建立索引,即索引的优点:

① 通过创建唯一性索引,保证数据库表中每一行的唯一性,生成唯一的rowId
② 建立索引可以有效缩短数据的检索时间,加快检索速度
③ 建立索引可以加快表与表之间的连接,对实现数据的参考完整性有重要意义
④ 为用来排序或者是分组的字段添加索引可以加快分组和排序顺序
⑤ 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能

3.索引的缺点:

① 创建索引和维护索引需要时间成本,这个成本随着数据量的增加而加大
② 创建索引和维护索引需要空间成本,每一条索引都要占据数据库的物理存储空间,数据量越大,占用空间也越大(数据表占据的是数据库的数据空间)
③ 会降低表的增删改的效率,因为每次增删改索引需要进行动态维护,导致时间变长

4.创建索引的准则

  • 应该在这些列上创建索引:

①在经常需要搜索的列上,可以加快搜索的速度;
②在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
③在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
④ 在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;
⑤ 在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
⑥在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。

  • 不应该创建索引的的这些列具有下列特点:

① 对于那些在查询中很少使用或者参考的列不应该创建索引。这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。
② 对于那些只有很少数据值的列也不应该增加索引。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。
③ 对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。
当修改性能远远大于检索性能时,不应该创建索引。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因此,当修改性能远远大于检索性能时,不应该创建索引。

5.创建索引的方法

直接创建索引的方法和间接创建索引的方法。

①直接创建索引,例如使用CREATE INDEX语句或者使用创建索引向导。
②间接创建索引,例如在表中定义主键约束或者唯一性键约束时,同时也创建了索引。

使用CREATE INDEX语句或者使用创建索引向导来创建索引,这是最基本的索引创建方式,并且这种方法最具有柔性,可以定制创建出符合自己需要的索引。在使用这种方式创建索引时,可以使用许多选项,例如指定数据页的充满度、进行排序、整理统计信息等,这样可以优化索引。使用这种方法,可以指定索引的类型、唯一性和复合性,也就是说,既可以创建聚簇索引,也可以创建非聚簇索引,既可以在一个列上创建索引,也可以在两个或者两个以上的列上创建索引。

通过定义主键约束或者唯一性键约束,也可以间接创建索引。主键约束是一种保持数据完整性的逻辑,它限制表中的记录有相同的主键记录。在创建主键约束时,系统自动创建了一个唯一性的聚簇索引。虽然,在逻辑上,主键约束是一种重要的结构,但是,在物理结构上,与主键约束相对应的结构是唯一性的聚簇索引。换句话说,在物理实现上,不存在主键约束,而只存在唯一性的聚簇索引。同样,在创建唯一性键约束时,也同时创建了索引,这种索引则是唯一性的非聚簇索引。因此,当使用约束创建索引时,索引的类型和特征基本上都已经确定了,由用户定制的余地比较小。

当在表上定义主键或者唯一性键约束时,如果表中已经有了使用CREATE INDEX语句创建的标准索引时,那么主键约束或者唯一性键约束创建的索引覆盖以前创建的标准索引。也就是说,主键约束或者唯一性键约束创建的索引的优先级高于使用CREATE INDEX语句创建的索引

6.索引的特征

索引有两个特征,即唯一性索引和复合索引。

唯一性索引保证在索引列中的全部数据是唯一的,不会包含冗余数据。
如果表中已经有一个主键约束或者唯一性键约束,那么当创建表或者修改表时,SQL Server自动创建一个唯一性索引。然而,如果必须保证唯一性,那么应该创建主键约束或者唯一性键约束,而不是创建一个唯一性索引。当创建唯一性索引时,应该认真考虑这些规则:当在表中创建主键约束或者唯一性键约束时,SQL Server自动创建一个唯一性索引;如果表中已经包含有数据,那么当创建索引时,SQL Server检查表中已有数据的冗余性;每当使用插入语句插入数据或者使用修改语句修改数据时,SQL Server检查数据的冗余性:如果有冗余值,那么SQL Server取消该语句的执行,并且返回一个错误消息;确保表中的每一行数据都有一个唯一值,这样可以确保每一个实体都可以唯一确认;只能在可以保证实体完整性的列上创建唯一性索引,例如,不能在人事表中的姓名列上创建唯一性索引,因为人们可以有相同的姓名。

复合索引就是一个索引创建在两个列或者多个列上。
在搜索时,当两个或者多个列作为一个关键值时,最好在这些列上创建复合索引。当创建复合索引时,应该考虑这些规则:最多可以把16个列合并成一个单独的复合索引,构成复合索引的列的总长度不能超过900字节,也就是说复合列的长度不能太长;在复合索引中,所有的列必须来自同一个表中,不能跨表建立复合列;在复合索引中,列的排列顺序是非常重要的,因此要认真排列列的顺序,原则上,应该首先定义最唯一的列,例如在(COL1,COL2)上的索引与在(COL2,COL1)上的索引是不相同的,因为两个索引的列的顺序不同;为了使查询优化器使用复合索引,查询语句中的WHERE子句必须参考复合索引中第一个列;当表中有多个关键列时,复合索引是非常有用的;使用复合索引可以提高查询性能,减少在一个表中所创建的索引数量。

7.索引的类型

根据索引的顺序与数据表的物理顺序是否相同,可以把索引分成两种类型。

一种是数据表的物理顺序与索引顺序相同的聚簇索引
另一种是数据表的物理顺序与索引顺序不相同的非聚簇索引

在聚簇索引中,数据值的顺序总是按照升序排列
应该在表中经常搜索的列或者按照顺序访问的列上创建聚簇索引

创建聚簇索引时,应该考虑这些因素:
每一个表只能有一个聚簇索引,因为表中数据的物理顺序只能有一个;表中行的物理顺序和索引中行的物理顺序是相同的,在创建任何非聚簇索引之前创建聚簇索引,这是因为聚簇索引改变了表中行的物理顺序,数据行按照一定的顺序排列,并且自动维护这个顺序;关键值的唯一性要么使用UNIQUE关键字明确维护,要么由一个内部的唯一标识符明确维护,这些唯一性标识符是系统自己使用的,用户不能访问;聚簇索引的平均大小大约是数据表的百分之五,但是,实际的聚簇索引的大小常常根据索引列的大小变化而变化;在索引的创建过程中,SQL Server临时使用当前数据库的磁盘空间,当创建聚簇索引时,需要1.2倍的表空间的大小,因此,一定要保证有足够的空间来创建聚簇索引

8.系统如何访问表中的数据

一般地,系统访问数据库中的数据,可以使用两种方法:表扫描和索引查找

第一种方法是表扫描,就是指系统将指针放置在该表的表头数据所在的数据页上,然后按照数据页的排列顺序,一页一页地从前向后扫描该表数据所占有的全部数据页,直至扫描完表中的全部记录。在扫描时,如果找到符合查询条件的记录,那么就将这条记录挑选出来。最后,将全部挑选出来符合查询语句条件的记录显示出来。

第二种方法是使用索引查找。索引是一种树状结构,其中存储了关键字和指向包含关键字所在记录的数据页的指针。当使用索引查找时,系统沿着索引的树状结构,根据索引中关键字和指针,找到符合查询条件的的记录。最后,将全部查找到的符合查询语句条件的记录显示出来。

在SQL Server中,当访问数据库中的数据时,由SQL Server确定该表中是否有索引存在。如果没有索引,那么SQL Server 使用表扫描的方法访问数据库中的数据。查询处理器根据分布的统计信息生成该查询语句的优化执行规划,以提高访问数据的效率为目标,确定是使用表扫描还是使用索引。


4、使用TRUNCATE TABLE A删除表A中数据。运行结果是?

表A中的约束依然存在

删除表的语句为:DROP TABLE table_name;
DELETE和TRUNCATE TABLE都是删除表中的数据的语句。

DELETE和TRUNCATE TABLE的不同之处在于:

(1)TRUNCATE TABLE比DELETE的速度快
(2)TRUNCATE TABLE是删除表的所有行,而DELETE是删除表的一行或者多行(除非DELETE不带WHERE语句);
(3)在删除时如果遇到任何一行违反约束(主要是外键约束),TRUNCATE TABLE仍然删除,只是表的结构及其列、约束、索引等保持不变,但DELETE是直接返回错误
(4)对于被外键约束的表,不能使用TRUNCATE TABLE,而应该使用不带WHERE语句的DELETE语句
(5)如果想保留标识计数值,要用DELETE,因为TRUNCATE TABLE会对新行标志符列搜用的计数值重置为该列的种子。


5、事务与锁

1.锁的概念

对数据库中数据对象进行加锁,防止并发或者多用户操作破坏数据库数据的一致性
因为可能存在多个事务同时存取同一数据的情况。

2.数据库锁有哪些分类?

共享锁:多个事务可查看,不能修改
排它锁:只有一个事务封锁,其他不能进入,只能等待X锁释放才能访问。
更新锁:用来锁定修改的资源。防止共享锁导致的死锁现在!
共享锁修改数据两步:获得共享锁,升级为排它锁,再修改数据。

这里写图片描述

上图表示可以共存的锁,如,第二行表示,一个事务T1给某数据加了X锁,则事务T2就不能再给那数据加X锁了,同时也不能再加S锁了,只有到T1事务提交完成之后,才可以。默认来说,当sql脚本修改更新某条记录的时候,会给该条记录加X锁,读的话加的是S锁。
另外,并发操作会导致数据的不一致性,主要包括“丢失数据”,“不可重复读”,“读脏数据等。

3.悲观锁和乐观锁?

并发控制一般采用三种方法,分别是乐观锁悲观锁以及时间戳

乐观锁认为一个用户读数据的时候,别人不会去写自己所读的数据;
悲观锁就刚好相反,觉得自己读数据库的时候,别人可能刚好在写自己刚读的数据,其实就是持一种比较保守的态度;
时间戳就是不加锁,通过时间戳来控制并发出现的问题。

悲观锁就是在读取数据的时候,为了不让别人修改自己读取的数据,就会先对自己读取的数据加锁,只有自己把数据读完了,才允许别人修改那部分数据,或者反过来说,就是自己修改某条数据的时候,不允许别人读取该数据,只有等自己的整个事务提交了,才释放自己加上的锁,才允许其他用户访问那部分数据。

乐观锁就比较简单了,就是不做控制,这只是一部分人对于并发所持有的一种态度而已。

时间戳就是在数据库表中单独加一列时间戳,比如“TimeStamp”,每次读出来的时候,把该字段也读出来,当写回去的时候,把该字段加1,提交之前,跟数据库的该字段比较一次,如果比数据库的值大的话,就允许保存,否则不允许保存,这种处理方法虽然不使用数据库系统提供的锁机制,但是这种方法可以大大提高数据库处理的并发量,因为这种方法可以避免了长事务中的数据库加锁开销,大大提升了大并发量下的系统整体性能表现。

以上悲观锁所说的加“锁”,其实分为几种锁,分别是:
排它锁和共享锁,其中排它锁又称为写锁,共享锁又称为读锁。

4.活锁和死锁?

并发控制会造成活锁和死锁,就像操作系统那样,会因为互相等待而导致。

活锁指的是T1封锁了数据R,T2同时也请求封锁数据R,T3也请求封锁数据R,当T1释放了锁之后,T3会锁住R,T4也请求封锁R,则T2就会一直等待下去,这种处理方法就是采用“先来先服务”策略。

死锁就是我等你,你又等我,双方就会一直等待下去,比如:T1封锁了数据R1,正请求对R2封锁,而T2封住了R2,正请求封锁R1,这样就会导致死锁,死锁这种没有完全解决的方法,只能尽量预防。

诊断和判断死锁有两种方法:
超时法。如果某个事物的等待时间超过指定时限,则判定为出现死锁。
等待图法。如果事务等待图中出现了回路,则判断出现了死锁。

5.死锁原因

1)由于多用户、多任务的并发性和事务的完整性要求,当多个事务处理对多个资源同时访问时,若双方已锁定一部分资源但也都需要对方已锁定的资源时,无法在有限的时间内完全获得所需的资源,就会处于无限的等待状态,从而造成其对资源需求的死锁。

2)数据库本身加锁机制的实现方法不同,各数据库系统也会产生其特殊的死锁情况。如在Sybase SQL Server 11 中,最小锁为 2K 一页的加锁方法,而非行级锁。如果某张表的记录数少且记录的长度较短(即记录密度高,如应用系统中的系统配置表或系统参数表就属于此类表),被访问的频率高,就容易在该页上产生死锁。

表现一: 一个用户 A 访问表 A(锁住了表 A),然后又访问表 B。另一个用户 B 访问表 B(锁住了表 B),然后企图访问表
A。这时用户 A 由于用户 B 已经锁住表 B,它必须等待用户 B 释放表 B,才能继续,同样用户 B 要等用户 A 释放表 A
才能继续操作,这样就造成了死锁。

解决方法:
这种死锁是由于程序的 BUG 产生的,需调整程序对数据库层的实现逻辑。仔细分析程序的逻辑:
(1)尽量避免同时锁定两个资源
(2)必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源

表现二: 用户 A 读一条纪录,然后修改该条纪录,同时用户 B 也修改该条纪录。这里用户 A 的事务里锁的性质由共享锁企图上升到独占锁(for update),而用户 B 里的独占锁由于 A 有共享锁存在所以必须等 A 释放掉共享锁而 A 由于 B 的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。这种死锁比较隐蔽,但其实在稍大点的项目中经常发生。

解决方法:
让用户 A 的事务(即先读后写类型的操作),在 select 时就是用 update lock
语法如下:select * from table1 with(updlock) where ….

6.常见死锁情况及解决方法

1 不同的存储过程、触发器、动态SQL语句段按照不同的顺序同时访问多张表。

在系统实现时应规定所有存储过程、触发器、动态SQL语句段中,对多张表的操作总是使用同一顺序。如:有两个存储过程 procedure1、procedure2,都需要访问三张表 table1、table2和 table3,如果 procedure1 按照 table1、table2 和 table3 的顺序进行访问,那么,procedure2也应该按照以上顺序访问这 3 张表。

2 在交换期间添加记录频繁的表,但在该表上使用了非群集索引(non-clustered)。

对在交换期间添加记录频繁的表,使用群集索引(clustered),以减少多个用户添加记录到该表的最后一页上,在表尾产生热点,造成死锁。这类表多为往来账的流水表,其特点是在交换期间需要在表尾追加大量的记录,并且对已添加的记录不做或较少做删除操作。

3 表中的记录少,且单条记录较短,被访问的频率较高。

对单张表中记录数不太多,且在交换期间 select 或 update 较频繁的表可使用设置每页最大行的办法,减少数据在表中存放的密度,模拟行级锁,减少在该表上死锁情况的发生。这类表多为信息繁杂且记录条数少的表。如:系统配置表或系统参数表。在定义该表时添加如下语句:with max_rows_per_page=1

4 整张表被访问的频率高(如代码对照表的查询等)。

在存储过程、触发器、动态 SQL 语句段中,若对某些整张表 select 操作较频繁,则可能在该表上与其他访问该表的用户产生死锁。对于检查账号是否存在,但被检查的字段在检查期间不会被更新等非关键语句,可以采用在 select 命令中使用 at isolation read uncommitted 子句的方法解决。该方法实际上降低了 select 语句对整张表的锁级别,提高了其他用户对该表操作的并发性。在系统高负荷运行时,该方法的效果尤为显著。
例如: select*from titles at isolation read uncommitted

5 对流水号一类的顺序数生成器字段,可以先执行 update 流水号字段+1,然后再执行 select获取流水号的方法进行操作。

备注:

NOLOCK(不加锁):此选项被选中时,SQL Server 在读取或修改数据时不加任何锁。在这种情况下,用户有可能读取到未完成事务(Uncommitted Transaction)或回滚(Roll Back)中数据,即所谓的“脏数据”

UPDLOCK(修改锁):此选项被选中时,SQL Server 在读取数据时使用修改锁来代替共享锁,并将此锁保持至整个事务或命令结束。使用此选项能够保证多个进程能同时读取数据但只有该进程能修改数据

关于死锁的预防建议(个人总结)

1、优化索引
2、对所有的报表,非事务性的 select 语句 在 from 后都加了 with (nolock) 语句
3、对所有的事务性更新尽量使用相同的更新顺序来执行

7.数据库锁的粒度体现在哪些方面,各自有什么特点?

小-行:增大并发量,开销也大,维护锁多
大-表:降低并发量,开销低,维护锁少!

8.事务四大特性?

事务是恢复和并发控制的基本单位,单个逻辑工作单元执行的一系列操作
事务必须满足:ACID(原子性,一致性,隔离性,持久性)四特性。
原子性指的是事务是数据库的逻辑工作单位,事务中操作要么都做,要么都不做;
一致性指的是事务的执行结果必须是使数据库从一个一致性状态变到另一个一致性状态,一致性和原子性是密切相关的;(事务执行前与执行后数据内在的逻辑始终是成立的。比如转账前与转账后两人存款的总和始终不变。)
隔离性指的是一个事务执行不能被其他事务干扰;
持久性指的是一个事务一旦提交,他对数据库中数据的改变就是永久性的。

9.事务的4种隔离级别

这里写图片描述
数据库事务的隔离级别有4种,由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable 。而且,在事务的并发操作中可能会出现脏读,不可重复读,幻读。下面通过事例一一阐述它们的概念与联系。

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
重复读,就是在开始读取数据(事务开启)时,不再允许修改操作

事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。

分析:重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。

什么时候会出现幻读?

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

Serializable 序列化

Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。

这里写图片描述

值得一提的是:
大多数数据库(如Sql Server,Oracle)默认的事务隔离级别是Read committed
Mysql的默认隔离级别是Repeatable read