database

包含一些database常见的问题,MySQL的索引,怎么使用索引等等。

数据库的三大范式

第一范式:每个列都不能再拆分

第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分

第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。

引擎

MySQL里面储存引擎MyISAM与InnoDB的区别:

InnoDB引擎:提供了对数据库ACID的支持,并且提供了行级锁和外键的约束。它的设计目的就是处理大数据容量的数据库系统。可以自适应哈希索引(聚簇索引:在索引中保存了数据,从而避免直接读取磁盘,因此对查询性能有很大的提升)

MyISAM引擎:不提供事务支持,也不支持行级锁和外键。只支持表级锁。支持压缩表和空间数据索引。(非聚簇索引:索引和数据分开存,在索引里面找到需要的内存地址,然后去读取)

MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。InnoDB现在是MySQL的默认引擎。

MySQL的ACID原理

原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)

我们以从A账户转账50元到B账户为例进行说明一下ACID,四大特性。

原子性:

一个事物是一个不可分割的单位,其中的操作要么全做,要么不做,不存在中间状态。要么转账成功,要么失败,不存在中间状态。

如果没办法保证原子性会怎么样?

可能会导致A的账户少了50元,但是B的账户也没多50。系统总金额会少50.。。。

隔离性:

是指在并发执行的时候,事务内部的操作与其他的事务是隔离的,并发操作的时候各个事务之间不能相互干扰。

如果没办法保证隔离性会发生什么?

比如说A有200,B有0,A向B转账两次,每次50元。如果没发保证隔离性,就会导致A扣款了两次,但是B只增加了一次,A就变成了100,B只有50。又凭空蒸发了。

持久性:

指的是当事务提交之后,它对数据库的改变就应该是永久性的。如果接下来有什么错误发生也不会被影响到。

如果没办法保证持久性会发生什么?

如果你正在写入磁盘的时候,突然服务器宕机了,在内存中修改的数据全部丢失了,持久性就无法保证。

一致性:

是指事务执行前后,数据处于一种合法的状态。。那什么是合法的数据状态呢? oK,这个状态是满足预定的约束就叫做合法的状态,再通俗一点,这状态是由你自己来定义的。「满足这个状态,数据就是一致的,不满足这个状态,数据就是不一致的」!(就是自己去规定的一个状态,比如账户余额必须大于100)

如果没办法保证一致性会发生什么?

例一:A账户有200元,转账300元出去,此时A账户余额为-100元。你自然就发现了此时数据是不一致的,为什么呢?因为你定义了一个状态,余额这列必须大于0。

数据库必须要实现AID三大特性,才有可能实现一致性。例如,原子性无法保证,显然一致性也无法保证。

MySQL怎么实现ACID的

MySQL里面InnoDB是使用undo log 是实现原子性的关键,当事物回滚的时候能够撤销所有已经成功执行的SQL语句。他需要记录你要回滚的相应日志信息:

  1. 当你delete一条数据的时候,就需要记录这条数据的信息,回滚的时候,insert这条旧数据
  2. 当你update一条数据的时候,就需要记录之前的旧值,回滚的时候,根据旧值执行update操作
  3. 当年insert一条数据的时候,就需要这条记录的主键,回滚的时候,根据主键执行delete操作

InnoDB使用redo log是实现持久性的关键,Mysql是先把磁盘上的数据加载到内存中,在内存中对数据进行修改,同时会在redo log里面记录该操作,当宕机重启之后,会将redo log中的内容回复到数据库里面。

MySQL使用的是锁和MVCC机制保证隔离性。

ACID

数据库事务中的四大特性:ACID

Atomicity

A:原子性(Atomicity),一个事务(transcation)中的所有操作,要么全部完成,要么全部不完成,不会出现完成一半的情况。事务在执行过程中发生错误会被回滚(rollbac)到事务开始前的状态。

原子性和一致性通过Undo log来实现。Undo log的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为Undo log)。然后进行数据的修改。如果出现了错误或者用户执行了ROLLBACK语句,系统可以利用Undo log中的备份将数据恢复到事务开始之前的状态。

Consistency

C:一致性(Consistency),事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。(参考分布式数据库的一致性)。

数据一致性可以参考Raft算法和Paxos算法。

Isolation

I:隔离性(Isolation),指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。 打个比方,你买东西这个事情,是不影响其他人的。

如果要实现数据库事务最高隔离性,也就是最安全的隔离。最简单的解决方法就是当一个事务执行的时候,其他事务都阻塞,等当前事务执行完之后再执行。但是对于现在的多核CPU是非常浪费资源的。所以为了充分利用计算资源,可以使用多版本并发控制(MVCC)来解决该问题。

Read & Write Lock

读写锁还是比较容易理解的。当在读取数据的时候,应该对读取的数据先加锁后读取,读取完之后的某个时间再进行解开读锁。读锁只能读取数据不能写入。因为如果加了读锁,说明某个事务准备读取数据,如果被别的事务修改了数据,那么数据就不准确了。但是多个事务可以同时对同一个数据进行读锁,因为所有事务都是只读不写。

写锁具有排他性(exclusive lock)。当一个事务准备对一个数据进行写操作的时候,先要对数据加写锁,然后才可以修改数据。这个时候其他事务就无法对这个数据进行解锁,除非这个写锁释放。写锁是为了必然多个事务同时修改同一个数据。

One Phase Lock

一段锁即事务开始的时候,一次性申请全部的锁,以后不会申请任何锁。如果期中某个锁不可用,则整个申请就不成功,事务就不会执行,在事务尾端则一次性释放所有锁。一段锁不会造成死锁,但是事务的并发度不高。

Two Phase Lock

两段锁,整个事务分成两个阶段,前一个阶段为加锁,后一个阶段为解锁。在加锁阶段,事务只能加锁,也能操作数据,但不能解锁。直到事务释放第一个锁,就进入解锁阶段,此过程当中事务只能解锁,也能操作数据,不能再加锁。两段锁协议使得事务具备较高的并发。由于在加锁阶段没有顺序要求,所以会存在死锁的情况。如果两个事务分别申请了A,B锁,接着又申请对方的锁,此时进入到死锁状态。

Multiversion Concurrency Control

加锁的方式处理事务一个比较大的问题就是会造成死锁(dead lock),原因就是一个事务加锁的数据并不止只有一行。事务A对行C加写锁,事务B对行D加写锁,接着事务希望获取行D的锁,事务B希望获取行C的锁,这样很容就死锁了。

MVCC提供的只是一种思路,具体的实现比较多样化。大体的思路是每一行保存冗余数据,读写的时间戳,也可以称为版本号,在对某一行数据继续update或者delete的时候,并不直接操作,而是复制多一份副本进行操作,这个就是所谓多版本(multiversion)

相当于利用时间戳对数据进行维护保持数据的隔离性。

Durability

D:持久性(Durability),指的是只要事务成功结束,它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。打个比方,你买东西的时候需要拿收据,即使老板忘记了那也有据可查。

重做日志(Redo Log)用来实现事务的持久性。Redo log记录的是新数据的备份。在事务提交前,只要将Redo log持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是Redo log已经持久化。系统可以根据Redo log的内容,将所有数据恢复到最新的状态。

----- End Thanks for reading-----