数据库设计的六个阶段
系统需求分析阶段
系统需求分析的两种方式
- 自顶向下
从最顶层的系统组织机构入手,采用逐层分解的方式分析 - 自底向上
概念结构设计阶段
逻辑结构设计阶段
物理结构设计阶段
数据库实施阶段
数据库运行与维护阶段
数据库的三范式
数据库的设计范式(NF: Normal Form)是数据库设计所需要满足的规范,满足这些规范的数据库是简洁的、结构明晰的,同时,不会发生插入(insert)、删除(delete)和更新(update)操作异常。
第一范式:列不可再分
- 每一列的属性都是不可再分的属性值,确保每一列的原子性
- 两列的属性相近或相似或一样,尽量合并属性一样的列,确保不产生冗余数据
第二范式:属性完全依赖于主建
第二范式是在第一范式的基础上建立起来的,即满足第二范式必须先满足第一范式。第二范式要求数据库表中的每个实例或行必须可以被惟一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。这个惟一属性列被称为主键
第三范式:属性直接依赖于主键,属性不依赖于其它非主属性
数据不能存在传递关系,即每个属性都跟主键有直接关系而不是间接关系。像:a-->b-->c 属性之间含有这样的关系,是不符合第三范式的。
注意:数据库设计最重要的是看需求跟性能,需求>性能>表结构;所以不能一味的去追求范式建立数据库.
数据的事物
事物的四大特性(ACID)
- 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行失败 - 一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态 - 隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。 - 持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
在事物的特性中,最困难的是事物的隔离性
事物的隔离级别
多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。
如果不考虑事物的隔离性,将会引发的问题
- 脏读
脏读是指一个事务读取了另外一个事务未提交的数据
这是非常危险的,假设A向B转帐100元,对应sql语句如下所示
update account set money=money+100 where name='B';
update account set money=money-100 where name='A';
当第1条sql执行完,第2条还没执行(A未提交时),如果此时B查询自己的帐户,就会发现自己多了100元钱。如果A等B走后再回滚,B就会损失100元。
- 不可重复读
不可重复读指在一个事务内读取表中的某一行数据,多次读取结果不同。
例如银行想查询A帐户余额,第一次查询A帐户为200元,此时A向帐户内存了100元并提交了,银行接着又进行了一次查询,此时A帐户为300元了。银行两次查询不一致,可能就会很困惑,不知道哪次查询是准的。
不可重复读和脏读的区别是,脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据。
- 幻读
幻读是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。
如丙存款100元未提交,这时银行做报表统计account表中所有用户的总额为500元,然后丙提交了,这时银行再统计发现帐户为600元了,造成虚读同样会使银行不知所措,到底以哪个为准。
为了解决这些问题,而出现了事物隔离级别
针对事物的隔离性而设置的四种隔离级别
- Read uncommitted(读未提交):最低级别,以上情况均无法保证
- Read committed(读已提交):可避免脏读情况发生。
- Repeatable read(可重复读):可避免脏读、不可重复读情况的发生
- Serializable(串行化):可避免脏读、不可重复读、幻读情况的发生。
主要是为了解决并发情况的事物问题,非并发情况下应该不会出现上述的事物问题
索引相关
什么是索引?
索引是对数据库表中的一列或多列的值进行排序的一种结构。
索引是建立在表的基础之上的,有助于无需检查所有记录而快速定位所需记录的一种辅助存储结构,由一系列存储在磁盘上的索引项组成,每一种索引项由索引字段和行指针组成。
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。所以说索引的本质,就是一种数据结构
我们知道,数据库查询是数据库的最主要功能之一。我们都希望查询数据的速度能尽可能的快,因此数据库系统的设计者会从查询算法的角度进行优化。最基本的查询算法当然是顺序查找(linear search),这种复杂度为O(n)的算法在数据量很大时显然是糟糕的,好在计算机科学的发展提供了很多更优秀的查找算法,例如二分查找(binary search)、二叉树查找(binary tree search)等。如果稍微分析一下会发现,每种查找算法都只能应用于特定的数据结构之上,例如二分查找要求被检索数据有序,而二叉树查找只能应用于二叉查找树上,但是数据本身的组织结构不可能完全满足各种数据结构(例如,理论上不可能同时将两列都按顺序进行组织),所以,在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引
建立索引有什么好处?
- 通过建立索引,可以在查询的过程中,提高系统的性能。
- 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
- 在使用分组和排序自居进行数据兼续哦是,可以减少查询中分组和排序的时间。
- 建立数据库索引,可以确保数据的唯一性。
- 可以加快表和表之间的连接,实现表和表之间的参照完整性。
建立索引有什么坏处?
- 创建和维护索引是需要耗费很多时间的,而且时间随着数据量的增加而增大。
- 索引需要占用物理空间,如果要建立聚簇索引,所需要的物理空间就会更大。
- 建立索引后,在对表中的数据进行增加删除和修改时需要耗费较多的时间,因为这个过程需要动态的维护索引。
索引的分类有哪些?
各类索引其实很多是具有交集的,联合索引可以是覆盖索引,主键索引可以是聚集索引也可以是非聚集索引,非聚集索引可以是联合索引,它们并不是相互对立的,而是根据不同的应用情况有着不同的名称。
索引按数据结构分类:
索引按索引的底层数据结构分类,分为B+索引和hash索引以及全文索引
- 主建索引(PRIMARY KEY)
某一个属性组能唯一标识的一条记录
特点:
最常见的索引类型
确保数据记录的唯一性
确定特定数据记录在数据库中的位置 - 唯一索引 (UNIQUE)
作用:
避免同一个表中某数据列种的值重复
与主键索引的区别:
主建索引只能有一个,唯一索引可有多个。 - 普通索引(INDEX)
作用:
快速定位特定数据
注意:
不易添加太多常规索引,影响数据的插入,删除和修改操作 - 全文索引(FULLTEXT)
作用:
快速定位特定数据
注意:
只能用于MyISAM类型的数据表
只能用于 CHAR, VARCHAR, TEXT 数据列类型
使用大型数据集
索引分类
- 根据存储结构的不同可以分为聚集索引和非聚集索引
聚集索引:
指索引项的排序方式和表中数据记录排序方式一致的索引也就是说聚集索引的顺序就是数据的物理存储顺序。它会根据聚集索引键的顺序来存储表中的数据,即对表的数据按索引键的顺序进行排序,然后重新存储到磁盘上。由于数据在物理存放时只能有一种排序方式,所以一个表只能有一个聚集索引。
非聚集索引:
索引顺序与物理存储顺序不同
非聚集索引必须是稠密索引
- 根据索引的数量可以分为稠密索引和稀疏索引
稠密索引:
在稠密索引中,文件中的每个搜索码值都对应一个索引值。也就是说,稠密索引为数据记录文件的每一条记录都设一个键-指针对。如下图所示,索引项包括索引值以及指向该搜索码的第一条数据记录的指针,即我们所说的键-指针对。
稀疏索引:
在稀疏索引中,只为搜索码的某些值建立索引项。也就是说,稀疏索引为数据记录文件的每个存储块设一个键-指针对,存储块意味着块内存储单元连续。
- 根据其他的分类方法有不同的划分。
索引的实现方式
-
B+树
B+索引是数据库使用最多的索引,底层是B+树。我们经常听到B+树就是这个概念,用这个树的目的和红黑树差不多,也是为了尽量保持树的平衡,当然红黑树是二叉树,但B+树就不是二叉树了,节点下面可以有多个子节点,数据库开发商会设置子节点数的一个最大值,这个值不会太小,所以B+树一般来说比较矮胖,而红黑树就比较瘦高了。
关于B+树的插入,删除,会涉及到一些算法以保持树的平衡。ORACLE的默认索引就是这种结构的。
如果经常需要同时对两个字段进行AND查询,那么使用两个单独索引不如建立一个复合索引,因为两个单独索引通常数据库只能使用其中一个,而使用复合索引因为索引本身就对应到两个字段上的,效率会有很大提高。 -
散列索引
Hash索引的底层是hash表,运用某种哈希算法对索引值进行运算,获得hash值,再通过hash值映射到特定的位置,查找速度及其快。但它只能用于等值查询,无法进行数据库常用的操作-范围查找和排序,而且当记录变多或者索引值重复过多时,容易产生hash碰撞,因此主流的存储引擎都使用B+索引。InnoDB支持自适应Hash索引,也就是说我们不能人为创建hash索引,而是InnoDB引擎进行优化创建hash索引,当InnoDB引擎监视到某个二级索引在频繁的使用时,经常访问的二级索引数据会自动被生成到hash索引里面去(最近连续被访问三次的数据),自适应哈希索引通过缓冲池的B+树构造而来,因此建立的速度很快。
就是通过散列函数来定位的一种索引,不过很少有单独使用散列索引的,反而是散列文件组织用的比较多。
散列文件组织就是根据一个键通过散列计算把对应的记录都放到同一个槽中,这样的话相同的键值对应的记录就一定是放在同一个文件里了,也就减少了文件读取的次数,提高了效率。
散列索引呢就是根据对应键的散列码来找到最终的索引项的技术,其实和B树就差不多了,也就是一种索引之上的二级辅助索引,我理解散列索引都是二级或更高级的稀疏索引,否则桶就太多了,效率也不会很高。
- 位图索引
位图索引是一种针对多个字段的简单查询设计一种特殊的索引,适用范围比较小,只适用于字段值固定并且值的种类很少的情况,比如性别,只能有男和女,或者级别,状态等等,并且只有在同时对多个这样的字段查询时才能体现出位图的优势。
位图的基本思想就是对每一个条件都用0或者1来表示,如有5条记录,性别分别是男,女,男,男,女,那么如果使用位图索引就会建立两个位图,对应男的10110和对应女的01001,这样做有什么好处呢,就是如果同时对多个这种类型的字段进行and或or查询时,可以使用按位与和按位或来直接得到结果了。
B+树最常用,性能也不差,用于范围查询和单值查询都可以。特别是范围查询,非得用B+树这种顺序的才可以了。
HASH的如果只是对单值查询的话速度会比B+树快一点,但是ORACLE好像不支持HASH索引,只支持HASH表空间。
位图的使用情况很局限,只有很少的情况才能用,一定要确定真正适合使用这种索引才用(值的类型很少并且需要复合查询),否则建立一大堆位图就一点意义都没有了。
注意:
索引并不总是最好的工具。总的来说,只有当索引帮助存储引擎快速查找到记录带来的好处大于其他带来的额外工作时,索引才是有效的。
- 对于非常小的表,大部分情况下简单的全表扫描更有效
- 对于中到大型表,索引就非常有效
- 对于特大型表,建立和使用索引的代价随之增长
对于特大型表的情况下,需要一种技术可以直接区分出查询需要的一组数据,而不是一条一条记录地匹配。例如: 分区技术。 如果表的数据特别多,可以建立一个元数据信息表,用来查询需要用到的某些特性。对于TB级别的数据,定位单条记录的意义不大,所以经常会使用块级别元数据技术代替索引。
索引命中:
like
模糊查询以%开头,会导致索引失效
即使不以%开头,使用like也会降低查询效率
如果用户量很大的话,不使用like 而会导入第三方工具处理文字
避免使用函数
例如使用reverse(email) 会导致索引失效
尽量将类似的功能在代码中完成
or
当or条件中有未建立的索引列会失效
例: SELECT * FROM TB WHERE 索引列 or 非索引列 则在此 索引失效
但是 SELECT * FROM TB WHERE 索引列 or 非索引列 and 索引列 则会只用首尾的索引列
类型不一致
即传入的数据类型要与列类型相符 不然索引失效
例如列类型为字符 而传入数
!= >
普通索引使用 != 索引失效 但主键有效
普通索引字符型 > 索引失效 数字或者主键有效
order by
当根据索引排序时,选择的映射如果不是索引,则失效
例如: select name from tb where email='123@' email是索引,但映射name 索引失效
但是对主键排序,索引有效
最左前缀
如果组合索引为(name,email)
name and email 有效
name 有效
email 失效
一个表建的索引尽量不要超过5个。
尽量使用覆盖索引。
尽量不要在重复数据多的列上建索引。