数据库原理

基础知识

时间复杂度

时间频度用以描述一个算法处理一定量数据需要花多长时间。这里的时间不是常规意义上的时分秒,那依赖于硬件环境,没有意义;这里的时间指的是运算次数,运算次数的多少决定了时间多少的相对值,运算次数多的相对执行时间就长。

数据量不同,运算次数就不同,所以比较绝对运算次数也是没有意义的;实际上比较的是当数据量增加时,运算次数增加的趋势,这就是时间复杂度;好的算法不会随着数据量的增加和翻倍增加运算次数。

下图列出了常见的时间复杂度,展示了当数据量增加时,运算次数增加的趋势。

常见时间复杂度

从低到高解释一下上述算法时间复杂度,以需要处理2000条数据为例:

  • O(1):只需要执行一次运算;
  • O(log(n)):需要执行7次运算;
  • O(n):需要执行2000次运算;
  • O(n*log(n)):需要执行14,000次运算;
  • O(n^2):需要执行4,000,000次运算;
  • O(n!):溢出了。

以查找为例,查找不同数据的情况所需的运算次数是不一样的;当我们讨论一个算法的时间复杂度时,都以平均情况为例进行讨论。

常见排序和查找方法的时间复杂度

算法 平均时间复杂度 最坏时间复杂度
冒泡排序 O(n^2) O(n^2)
快速排序 O(n^2) O(n*log(n))
二叉树排序 O(n^2) O(n*log(n))
堆排序 O(n*log(n)) O(n*log(n))
顺序查找 O(n)
二分查找 O(log(n))
二叉排序树查找 O(log(n))
哈希表法 O(1)

合并排序

排序树

二叉查找树是带有特殊属性的二叉树,每个节点的关键字必须:

  • 比保存在左子树的任何键值都要大
  • 比保存在右子树的任何键值都要小

B+树

哈希表

数据库

数据库是由多种互相交互的组件构成的。

客户端管理器

客户端管理器是处理客户端通信的。

查询管理器

事务管理器

一个ACID事务是一个工作单元,它要保证4个属性:

  • 原子性Atomicity): 事务『要么全部完成,要么全部取消』,即使它持续运行10个小时。如果事务崩溃,状态回到事务之前(事务回滚)。
  • 隔离性Isolation): 如果2个事务 A 和 B 同时运行,事务 A 和 B 最终的结果是相同的,不管 A 是结束于 B 之前/之后/运行期间。
  • 持久性Durability): 一旦事务提交(也就是成功执行),不管发生什么(崩溃或者出错),数据要保存在数据库中。
  • 一致性Consistency): 只有合法的数据(依照关系约束和函数约束)能写入数据库,一致性与原子性和隔离性有关。

经典的例子是从账户A到账户B的汇款。假设有2个事务:

  • 事务1(T1)从账户A取出100美元给账户B
  • 事务2(T2)从账户A取出50美元给账户B

我们回来看看ACID属性:

  • 原子性确保不管 T1 期间发生什么(服务器崩溃、网络中断…),你不能出现账户A 取走了100美元但没有给账户B 的现象(这就是数据不一致状态)。
  • 隔离性确保如果 T1 和 T2 同时发生,最终A将减少150美元,B将得到150美元,而不是其他结果,比如因为 T2 部分抹除了 T1 的行为,A减少150美元而B只得到50美元(这也是不一致状态)。
  • 持久性确保如果 T1 刚刚提交,数据库就发生崩溃,T1 不会消失得无影无踪。
  • 一致性确保钱不会在系统内生成或灭失。

原子性和一致性太像了?

SQL一般定义4个隔离级别:

  • 串行化(Serializable,SQLite默认模式):最高级别的隔离。两个同时发生的事务100%隔离,每个事务有自己的『世界』。
  • 可重复读(Repeatable read,MySQL默认模式):每个事务有自己的『世界』,除了一种情况。如果一个事务成功执行并且添加了新数据,这些数据对其他正在执行的事务是可见的。但是如果事务成功修改了一条数据,修改结果对正在运行的事务不可见。所以,事务之间只是在新数据方面突破了隔离,对已存在的数据仍旧隔离。
    举个例子,如果事务A运行”SELECT count(1) from TABLE_X” ,然后事务B在 TABLE_X 加入一条新数据并提交,当事务A再运行一次 count(1)结果不会是一样的。
    这叫幻读(phantom read)。
  • 读取已提交(Read committed,Oracle、PostgreSQL、SQL Server默认模式):可重复读+新的隔离突破。如果事务A读取了数据D,然后数据D被事务B修改(或删除)并提交,事务A再次读取数据D时数据的变化(或删除)是可见的。
    这叫不可重复读(non-repeatable read)。
  • 读取未提交(Read uncommitted):最低级别的隔离,是读取已提交+新的隔离突破。如果事务A读取了数据D,然后数据D被事务B修改(但并未提交,事务B仍在运行中),事务A再次读取数据D时,数据修改是可见的。如果事务B回滚,那么事务A第二次读取的数据D是无意义的,因为那是事务B所做的从未发生的修改(已经回滚了嘛)。
    这叫脏读(dirty read)。

TODO 结合事务

参考