关于数据库知识点的深度探析

2019-04-20  本文已影响0人  楚_kw

面试中吃了几次数据库上面的亏,做个总结(关系型数据库)

数据库知识点

感觉面试的必问的一块内容了,但一开始重新看数据库的时候,就看了下数据库的一些属性,SQL规则,有哪几种锁,ACID,索引。
结果到了面试的时候就发现完全不是这么简单了...
一个一个来,慢慢补充。


数据库事务

定义:数据库事务是构成单一逻辑工作单元的操作集合(通俗的说就是SQL执行单元)

事务四大特征(ACID)

回滚日志和重做日志

到现在为止我们了解了 MySQL 中的两种日志,回滚日志(undo log)和重做日志(redo log);在数据库系统中,事务的原子性和持久性是由事务日志(transaction log)保证的,在实现时也就是上面提到的两种日志,前者用于对事务的影响进行撤销,后者在错误处理时对已经提交的事务进行重做,它们能保证两点:

  1. 发生错误或者需要回滚的事务能够成功回滚(原子性);
  2. 在事务提交后,数据没来得及写会磁盘就宕机时,在下次重新启动后能够成功恢复数据(持久性);

在数据库中,这两种日志经常都是一起工作的,我们可以将它们整体看做一条事务日志,其中包含了事务的 ID、修改的行元素以及修改前后的值。

事务日志

隔离级别的实现


MySQL 和常见数据库中的锁都分为两种,共享锁(Shared)和互斥锁(Exclusive),前者也叫读锁,后者叫写锁。

读锁与写锁

读锁保证了读操作可以并发执行,相互不会影响,而写锁保证了在更新数据库数据时不会有其他的事务访问或者更改同一条记录造成不可预知的问题。

时间戳
除了锁,另一种实现事务的隔离性的方式就是通过时间戳,例如 PostgreSQL 会为每一条记录保留两个字段;读时间戳中保存了所有访问该记录的事务中的最大时间戳,而记录行的写时间戳中保存了将记录改到当前值的事务的时间戳。

时间戳

使用时间戳实现事务的隔离性时,往往都会使用乐观锁,先对数据进行修改,在写回时再去判断当前值,也就是时间戳是否改变过,如果没有改变过,就写入,否则生成一个新的时间戳并再次更新数据。

多版本和快照隔离
通过维护多个版本的数据,数据库可以允许事务在数据被其他事务更新时对旧版本的数据进行读取,很多数据库都对这一机制进行了实现;因为所有的读操作不再需要等待写锁的释放,所以能够显著地提升读的性能,MySQL 和 PostgreSQL 都对这一机制进行自己的实现,也就是 MVCC,虽然各自实现的方式有所不同,MySQL 就通过文章中提到的回滚日志实现了 MVCC,保证事务并行执行时能够不等待互斥锁的释放而直接获取数据。


这里又要分 MyISAM 和 InnoDB,表锁与行锁,共享锁和排他锁,意向锁,记录锁,间隙锁,意向锁等。锁详解
意向锁

间隙锁(Next-Key锁)
当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据的索引项加锁;
对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁).
间隙锁的目的

死锁
注意:MyISAM表锁是deadlock free的,这是因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么等待,因此不会出现死锁。
但在InnoDB中,除单个SQL组成的事务外,锁是逐步获得的,这就决定了InnoDB发生死锁是可能的。
发生死锁后,InnoDB一般都能自动检测到,并使一个事务释放锁并退回,另一个事务获得锁,继续完成事务。
这里列举两种死锁情况:

可以设计索引,并尽量使用索引访问数据,使加锁更精确,从而减少锁冲突的机会。

数据库索引为什么采用B+树?

  1. B+树的磁盘读写代价更低:B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了。
  2. B+树的查询效率更加稳定:由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
  3. 由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历。

数据库是怎么实现锁的?

在InnoDB内部用uint32类型数据表示锁的类型, 最低的 4 个 bit 表示 lock_mode, 5-8 bit 表示 lock_type(目前只用了 5 和 6 位,大小为 16 和 32 ,表示 LOCK_TABLE 和 LOCK_REC), 剩下的高位 bit 表示行锁的类型record_lock_type;

record_lock_type lock_type lock_mode
  1. lock_mode:
    lock_is/lock_ix/lock_s/lock_x
    (表级意向共享锁,表级意向排他锁,行共享锁,行排他锁)
  2. record_lock_type:
    LOCK_ORDINARY(next-key lock,锁住记录本身和记录之前的 gap,当用RR隔离级别的时候,为了防止当前读语句的幻读使用)
    LOCK_GAP(间隙锁,只锁住索引记录之间或者第一条索引记录前或者最后一条索引记录之后的范围,并不锁住记录本身)
    LOCK_REC_NOT_GAP(记录锁,仅锁住记录行,不锁范围)
    LOCK_INSERT_INTENTION(插入意向锁,当插入索引记录的时候用来判断是否有其他事务的范围锁冲突,如果有就需要等待)

关于具体SQL的加锁分析,可参考 《非常好的加锁逻辑分析》

上一篇下一篇

猜你喜欢

热点阅读