[TOC]
# 主 键
数据库表中对储存数据对象予以唯一和完整标识的数据列或属性的组合。一个数据列只能有一个主键,
且主键的取值不能缺失,即不能为空值(Null)。
# 外 键
在一个表中存在的另一个表的主键称此表的外键。
# 事务
数据库中一个独立的执行单元,通常由用户编写的程序执行而引起。当在数据库中更改数据成功时,在
事务中更改的的数据便会提交,不再改变。否则,事务会取消或回滚,更改无效。
## 四个属性
ACID,原子性,一致性,隔离性,持久性。
1. 原子性:事务是一个不可分割的整体,当数据修改时,要么全执行,要么全不执行,即不允许事务部分的完成。即不成功失败,不能只运行其中一个。
2. 一致性:事务处理要将数据库从一种状态转变为另一种状态。一旦提交了修改数据,那么其它人读取这个数据,也是被修改后的数据。例如:银行转账,转账前后两个账户总金额保持不变。
3. 隔离性:当两个或者多个事务并发执行之后,为了保证数据的安全性,**需要将多个事务执行分隔开来**。多个用户,不能同时读写同一个数据,应该有先后顺序,在数据库中是一个一 个事件地运行,如果事务的条件不满足,后续事件就回滚。
6. 持久性:事件一旦提交成功,数据就发生了变化。
## 举例
网上定票系统,扣钱和定票是一个事务,它需要有原子性即不能只运行扣钱不运行定票。符合原子性。
这张票被多人同时在网上定,就会有先来的才定上这个票,后来定票的动作,如果发现票已卖出,(票的状态改变了,其它人通过网站访问这个数据,就会发现票已卖出符合一致性),就会回滚到不扣钱,票订不上的状态。符合隔离性。
票被定了,在数据库里设置标志位,它就一直显示为卖出状态。符合持久性。
# 事务锁
数据库锁就是事务T在对某个数据对象(例如表、记录等)操作之前,先向系统发出请求,对其加锁。
加锁后事务T就对该数据对象有了一定的控制,在事务T释放它的锁之前,其它的事务不能更新此数据对象。
数据库锁是实现并发控制的重要技术,但是“锁”会带来系统额外的开销。所以需要注意选择封锁粒度时必须同时考虑开销和并发度两个因素,进行权衡,以求得最优的效果。
## 共享锁
多个事务可封锁一个共享页;任何事务都不能修改该页; 通常是该页被读取完毕,S锁立即被释放。
在执行select语句的时候需要给操作对象(表或者一些记录)加上共享锁,但加锁之前需要检查是否有排他锁,如果没有,则可以加共享锁(一个对象上可以加n个共享锁),否则不行。共享锁通常在执行完select语句之后被释放。
## 排他锁
仅允许一个事务封锁此页;其他任何事务必须等到X锁被释放才能对该页进行访问;X锁一直到事务结束才能被释放。执行insert、update、delete语句的时候需要给操作的对象加排他锁(我感觉在执行insert的时候应该是在表级加排他锁),在加排他锁之前必须确认该对象上没有其他任何锁,一旦加上排他锁之后,就不能再给这个对象加其他任何锁。
## 更新锁
用来预定要对此页施加X锁,它允许其他事务读,但不允许再施加U锁或X锁;当被读取的页将要被更
新时,则升级为X锁;U锁一直到事务结束时才能被释放。
### 用处
若两个事务同时对一个对象加共享锁,当他们都想修改这个对象时,数据库是支持在一个事务中进行自动锁升级的,所以这两个事务都想升级他们的锁,但由于这个对象上存在对方事务加的共享锁。所以无法升级。这样两个事务就在等待对方释放共享锁,进入死锁状态。
更新锁就是为了解决这个问题,即在执行查询操作的时候加的不是共享锁而是更新锁(一个对象上只能有一个更新锁和n个共享锁),当要更新的时候,再将更新锁升级为排他锁。
# 数据库**没有隔离级别时**的问题:
更新丢失:两个线程要更改数据,同时获取了原始值,A更新了后写回,B更新后也写回,这时A更新的内容就丢失了。
脏读:当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
不可重复读:一个事务对同一行数据重复读取两次,但是却得到了不同的结果。即为幻读,事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。
# 数据库的事务隔离级别:
1. 读未提交:允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据
2. 读提交:允许不可重复读取,但不允许脏读取。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
3. 可重复读:禁止不可重复读取和脏读取。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
4. 串行化:提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。
# 连接:分为内连接和外连接
内连接:返回的结果集选取了两个表中所有匹配的数据,舍弃了不匹配的数据。
例子:
```sql
select tlb_a.sid , tlb_b.name
from tlb_a inner join tlb_b
on tlb_a.id = tlb_b.id
```
左外连接:结果集包含左表的所有行,如果左表的某行在右表中没有匹配行,则在结果集中右表对应行为空。
例子:
```sql
select tlb_a.sid , tlb_b.name
from tlb_a left join tlb_b
on tlb_a.id = tlb_b.id
```
右连接同上
# 数据库范式
冗余:在设计数据库时,某一字段属于一个表,但它又同时出现在另一个或多个表,且完全等同于它在其本
来所属表的意义表示,那么这个字段就是一个[冗余字段](http://www.blueidea.com/tech/program/2011/8311.asp)
情景一:只在name表中有nickname,这样修改nickname时,只需要改这个表
情景二:想要根据id查找nickname,则需要使用join,当数据很多时,这样很费时
所有关系型数据库均满足第一范式
第一范式:属性不可分。指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如表中联系方式字段,不能既有座机号码,又有手机号码
第二范式:符合1NF,并且,非主属性完全依赖于码。
主属性:一个属性只要在任何一个候选码中出现过,这个属性就是主属性。
非主属性:与上面相反,没有在任何候选码中出现过,这个属性就是非主属性。
不能存在一个非主属性部分依赖于码,比如一个表中,主码是课程和学生,但是课程可以确定教材。
三种异常:
有什么不好吗?你可以想想:
1、校长要新增加一门课程叫“微积分”,教材是《大学数学》,怎么办?学生还没选课,而学生又是主
属性,主属性不能空,课程怎么记录呢,教材记到哪呢? ……郁闷了吧?(插入异常)
2、下学期没学生学一年级语文(上)了,学一年级语文(下)去了,那么表中将不存在一年级语文
(上),也就没了《小学语文1》。这时候,校长问:一年级语文(上)用的什么教材啊?……郁闷了
吧?(删除异常)
3、校长说:一年级语文(上)换教材,换成《大学语文》。有10000个学生选了这么课,改动好大
啊!改累死了……郁闷了吧?(修改异常)
第三范式:数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式
出现传递依赖A->B->C,即主键A可以确定出某一非关键字段B,而B又可以确定出C,这意味着C依赖于一个非关键字段B。因此第三范式又可描述为:表中不存在可以确定其他非关键字的非键字段。
问题就出在“老师”和“老师职称”这里。一个老师一定能确定一个老师职称。
有什么问题吗?想想:
1、老师升级了,变教授了,要改数据库,表中有N条,改了N次……(修改异常)
2、没人选这个老师的课了,老师的职称也没了记录……(删除异常)
3、新来一个老师,还没分配教什么课,他的职称记到哪?……(插入异常)
## BC范式:符合3NF,并且,主属性不依赖于主属性
视图:从数据库中选取出来的数据组成的逻辑窗口,是一个虚表。在数据库中,存放的只是视图的定义,而不包括的数据项
好处:
1. 当查询时使用复杂的sql语句
2. 可以把各个表之间复杂的操作和连接对用户隐藏
例子:
~~~
create view del as
select 职工号,姓名,部门名称,负责人 from work1,部门
where work1.部门编号=部门.部门编号
~~~
触发器
触发器是一种与表操作有关的数据库对象,当触发器所在表上出现指定事件时,将调用该对象,即表的操作事件触发表上的触发器的执行语法:
~~~
CREATE TRIGGER trigger_name
trigger_time
trigger_event ON tbl_name
FOR EACH ROW
trigger_stmt
~~~
> trigger_name:标识触发器名称,用户自行指定;
trigger_time:标识触发时机,取值为 BEFORE 或 AFTER;
trigger_event:标识触发事件,取值为 INSERT、UPDATE 或 DELETE;
tbl_name:标识建立触发器的表名,即在哪张表上建立触发器;
trigger_stmt:触发器程序体,可以是一句SQL语句,或者用 BEGIN 和 END 包含的多条语句。
其中,statement_list 代表一个或多个语句的列表,列表内的每条语句都必须用分号(;)来结尾。
而在MySQL中,分号是语句结束的标识符,遇到分号表示该段语句已经结束,MySQL可以开始执行了。因此,解释器遇到statement_list 中的分号后就开始执行,然后会报出错误,因为没有找到和 BEGIN 匹配的END。
这时就会用到 DELIMITER 命令(DELIMITER 是定界符,分隔符的意思),它是一条命令,不需要语句结束标识,语法为:
~~~
DELIMITER new_delemiter
~~~
`new_delemiter` 可以设为1个或多个长度的符号,默认的是分号(`;`),我们可以把它修改为其他符号,如`$`:
~~~
DELIMITER $
~~~
> [MySQL的学习--触发器](http://www.cnblogs.com/CraryPrimitiveMan/p/4206942.html)
# 存储过程和函数
sql语句在执行之前要先编译再执行。为了提高效率,将为了完成特定功能的语句集编译优化后放在数据库服务器中。
使用存储过程可以,传递参数并可以使用流程控制语句
## 与函数的区别
1. 函数可以作为查询的一部分,存储过程是独立的一部分
2. 存储过程在创建时就编译了,速度比函数快
# 索引
B-树是一种平衡的多路查找树,它在文件系统中很有用。
定义:一棵m 阶的B-树,或者为空树,或为满足下列特性的`m 叉树`:
1. 树中每个结点至多有m 棵子树;
2. 若根结点不是叶子结点,则至少有两棵子树;
3. 除根结点之外的所有非终端结点至少有[m/2] 棵子树;
4. 所有的非终端结点中包含以下信息数据:
~~~
(n,A0,K1,A1,K2,…,Kn,An)
~~~
其中:`Ki(i=1,2,…,n)`为关键码,且`Ki<Ki+1`,
`Ai` 为指向子树根结点的指针`(i=0,1,…,n)`,且指针`Ai-1` 所指子树中所有结点的关键码均小于`Ki (i=1,2,…,n)`,
`An` 所指子树中所有结点的关键码均大于`Kn`.
`n` 为关键码的个数。
5. 所有的叶子结点都出现在同一层次上,并且不带信息(可以看作是外部结点或查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空)
B+树是应文件系统所需而产生的一种B-树的变形树。
一棵m 阶的B+树和m 阶的B-树的差异在于:
1. 有n 棵子树的结点中含有n 个关键码;
2. 所有的叶子结点中包含了全部关键码的信息,及指向含有这些关键码记录的指针,且叶子结点本身依关键码的大小自小而大的顺序链接。
3. 所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键码。
通常在B+树上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点。因此可以对B+树进行两种
查找运算:一种是从最小关键字起顺序查找,另一种是从根节点开始,进行随机查找。
## 为什么使用B-Tree(B+Tree)
二叉查找树进化品种的红黑树等数据结构也可以用来实现索引,但是文件系统及数据库系统普遍采用B-/+Tree作为索引结构。
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,所以评价一个数据结构作为索引的优劣最重要的指标就是在查找过程中磁盘I/O操作次数的渐进复杂度。换句话说,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数。为什么使用B-/+Tree,还跟磁盘存取原理有关。
## 局部性原理与磁盘预读
磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。这样做的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。
预读的长度一般为页(page),磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中。
数据库系统巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入。
B-Tree中一次检索最多需要`h-1`次I/O(根节点常驻内存),而红黑树这种结构,`h`明显要深的多。
在 MySQL 中,主要有四种类型的索引:
B-Tree 索引, Hash 索引, Fulltext 索引和 R-Tree 索引。
> [MySQL索引背后的数据结构及算法原理](http://blog.codinglabs.org/articles/theory-of-mysql-index.html)
## Mysql B树索引有哪些?
### 普通索引
这是最基本的索引类型,而且它没有唯一性之类的限制。普通索引可以通过以下几种方式创建:
(1)创建索引: CREATE INDEX 索引名 ON 表名(列名1,列名2,...);
(2)修改表: ALTER TABLE 表名ADD INDEX 索引名 (列名1,列名2,...);
(3)创建表时指定索引:CREATE TABLE 表名 ( [...], INDEX 索引名 (列名1,列名 2,...) );
### UNIQUE索引
表示唯一的,不允许重复的索引,如果该字段信息保证不会重复例如身份证号用作索引时,可设置为`unique`:
(1)创建索引:`CREATE UNIQUE INDEX 索引名 ON 表名(列的列表)`;
(2)修改表:`ALTER TABLE 表名ADD UNIQUE 索引名 (列的列表)`;
(3)创建表时指定索引:`CREATE TABLE 表名( [...], UNIQUE 索引名 (列的列表) )`;
### 主键:PRIMARY KEY索引
主键是一种唯一性索引,但它必须指定为`PRIMARY KEY`。
(1)主键一般在创建表的时候指定:`CREATE TABLE 表名( [...], PRIMARY KEY (列的列表) );`。
(2)但是,我们也可以通过修改表的方式加入主键:`ALTER TABLE 表名ADD PRIMARY KEY (列的列表); `。
每个表只能有一个主键。 (主键相当于聚合索引,是查找最快的索引)
# MySql存储引擎
1. MyISAM:有较高的插入、查询速度,但不支持事务,数据文件和索引文件可以在不同的目录,5.5.5版本
2. 前是默认引擎
3. InnoDB:支持事务的ACID属性,给MySql提供了提交、回滚和崩溃的恢复事务能力,新版本的默认引擎
4. Memory