Interview_数据库_day24
数据库事务
数据库事务是向数据库进行访问或修改的一系列操作序列,要么全部执行,要么全部不执行,是不可分割的工作单位。
数据库事务有四大特性 \(ACID\):原子性\((Atomicity)\)、一致性\((Consistency)\)、隔离性\((Isolation)\)、持久性\((Durability)\)。
- 原子性:一个事务是不可分割的工作单位,事务内操作要么全部执行,要么全部失败回滚。
- 一致性:事务处理前后,数据都处于一个合法状态。比如 \(A\) 向 \(B\) 转账时,转账前后不能有人金额为负数,\(A\) 金额和 \(B\) 金额保持总和不变。
- 隔离性:多个事务并发发生时,事务内部的操作和其他事务是隔离的,事务之间是互不干扰的。
- 持久性:一旦一个事务被提交了,那么对数据库中的数据改变是永久的。即使数据库系统遇到故障也不会影响已经提交的事务所完成的操作。
数据库索引
数据库索引是为了增加查询速度而对表字段附加的一种标识。
默认的查询方式是根据条件进行全表扫描,遇到匹配的结果就加入结果集合。通过索引可以快速定位到特定值的行数,减少遍历时匹配的行数,加快查询速度。
优点:
- 创建唯一性索引,可以保证数据库中每一行数据的唯一性
- 大大加快查找速度
- 加速表和表之间的连接
缺点:
- 建立和维护索引需要额外的时间
- 索引实际上也是一种数据结构,需要占用额外的物理空间
- 对表中数据修改时,索引也要动态维护
索引背后的数据结构
添加索引原则
对于很少使用的列不应该创建索引,因为很少用到,索引不会明显的提高速度,增加了索引,反而会因为修改数据时要维护索引而花费额外的时间。
对于很少数据值的列也不应该创建索引,比如性别,因为数据值很少,所以通过索引要查找的行数还是很多,并不能提高速度。
当修改性能远远大于查找性能时也不创建索引,因为修改多时必定要带来大量的额外维护索引的时间。
聚集索引和非聚集索引
- 聚集索引:表中存储的数据按照索引的顺序存储,检索效率比较高。叶子节点保存的就是整行数据。
- 非聚集所引:数据存储和索引存储在不同的地方,检索效率比较低。叶子节点保存的是聚集索引字段的值,再通过这个值去聚集索引中查找数据。
在一个数据库中,只能有一个聚集索引,可以有多个非聚集索引。
自然连接、外连接左外连接、右外连接
表1:
A | B | C |
---|---|---|
a1 | b1 | 5 |
a1 | b2 | 6 |
a2 | b3 | 8 |
a2 | b4 | 12 |
表2:
B | E |
---|---|
b1 | 3 |
b2 | 7 |
b3 | 10 |
b3 | 2 |
b3 | 2 |
自然连接时比较的必须是同名属性组,并在结果中把重复的属性列去掉。
自然连接:
A | B | C | E |
---|---|---|---|
a1 | b1 | 5 | 3 |
a1 | b2 | 6 | 7 |
a2 | b3 | 8 | 10 |
a2 | b3 | 8 | 2 |
可以发现在自然连接中有一些 表1 中的元组在 表2 中没有共同的属性,那么就会被舍弃掉,这些舍弃掉的远足称为 悬浮元组。
外连接就是把这些悬浮元组都保留在结果关系中,并在空值处填上 NULL。左外连接就是保留 表1 中的悬浮元组,右外连接就是保留 表2 中的悬浮元组。
外连接:
A | B | C | E |
---|---|---|---|
a1 | b1 | 5 | 3 |
a1 | b2 | 6 | 7 |
a2 | b3 | 8 | 10 |
a2 | b3 | 8 | 2 |
a2 | b4 | 12 | NULL |
NULL | b5 | NULL | 2 |
左外连接:
A | B | C | E |
---|---|---|---|
a1 | b1 | 5 | 3 |
a1 | b2 | 6 | 7 |
a2 | b3 | 8 | 10 |
a2 | b3 | 8 | 2 |
a2 | b4 | 12 | NULL |
右外连接:
A | B | C | E |
---|---|---|---|
a1 | b1 | 5 | 3 |
a1 | b2 | 6 | 7 |
a2 | b3 | 8 | 10 |
a2 | b3 | 8 | 2 |
NULL | b5 | NULL | 2 |
数据库的乐观锁和悲观锁
- 悲观锁:它认为当某一用户读取某一数据的时候,其他用户也会对该数据进行访问,所以在读取的时候就对数据进行加锁,在该用户读取数据的期间,其他任何用户都不能来修改该数据,但是其他用户是可以读取该数据的,只有当自己读取完毕才释放锁。
- 优势:能避免冲突的发生。
- 劣势:开销较大,因为数据库加锁解锁开销大。
- 乐观锁:它假定当某一个用户去读取某一个数据的时候,其他的用户不会来访问修改这个数据。在最后进行事务提交的时候会进行版本的检查,以判断在该用户的操作过程中,没有其他用户修改了这个数据。开销比较小。
- 优势:避免了数据库加锁解锁开销,大大提升了大并发量下的系统整体性能表现。适合于多读的情况。
- 劣势:只能在提交数据时才发现事务将要失败,如果系统的冲突非常的多,乐观锁也会带来很大的问题。而且乐观锁无法解决脏读问题。
不考虑隔离的问题
- 脏读:事务 \(A\) 对一个 \(data\) 进行了更新,然后事务 \(B\) 读取了 \(data\),然后事务 \(A\) 由于某种情况失败回滚,此时事务 \(B\) 读出来的数据就成了脏数据。
- 不可重复读:事务 \(A\) 第一次读取了 \(data\),然后事务 \(B\) 对 \(data\) 进行了更新,当事务 \(A\) 第二次读取 \(data\) 的时候发现两次的数据不同,就造成了不可重复读。
- 幻读:事务 \(A\) 读取记录时,事务 \(B\) 向数据库添加了一条数据,当事务 \(A\) 再次读取记录时,发现了新添加的数据,就造成了幻读。
数据库四种隔离等级
- 读未提交:三大问题都不能避免
- 读已提交:可以避免 脏读 的发生
- 可重复读:可以避免 脏读、不可重复读 的发生
- 串行化:可以避免 脏读、不可重复读、幻读 的发生