在前面先记录一个不错的博客内容http://blog.codinglabs.org/articles/theory-of-mysql-index.html
<http://blog.codinglabs.org/articles/theory-of-mysql-index.html>

这时在B站上看的东南大学的视频的学习笔记,主要是看了一天的数据库系统概念这本神书感觉有点吃力很累,所以还是决定这种看视频做笔记的模式了

* 引言
* 什么是数据库
* 什么事数据库管理系统
* 文件VS数据库
* 文件功能太少了只能OPEN啊READ啊这些太少了
* 文件查询也不好查
* 更不用说一些更复杂的问题了,什么一致性原子性这些
* 所以说数据库就是建立在文件系统之上的,高配版的
* 为什么使用DBMS
* 为何要研究数据库
* 数据 数据模型 数据模式
* 数据模型就是一种数据结构,什么网状模型关系模型之类的
* 数据模式就是用一种数据模型的结果,如果数据模型与数据模式的关系相当于C++语言和用C++写的程序的关系
* 关系数据模型的关系就是表
* 抽象级别
* 物理模式+逻辑模式+视图
* 数据独立性
* 就是说每层之间是隔离的
* 历史和分类
* 数据库系统
* 应用程序+DBMS+数据库+DBA
* 生命周期
* 规划
* 数据库设计(比如采用关系型数据库的话该怎么设计表啊)
* 加载数据
* 运行和管理
* 扩展和重构
* 数据模型
* 层次数据模型
* 用树来表达现实世界
* 一个实体表达成一个记录
* 但是现实世界中还有多对多的关系之类的,这个树就不好搞了
* 为了解决这些问题引入了虚记录的概念
* 网状数据模型
* 基本数据结构是SET,系,代表了一个一对多的关系
* 比如CLASS和STUDENGT的关系就可以是C-S SET,一个班级对应了很多学生
* 然后一条记录就是一个SET,是一个循环链表
* 查询时很麻烦,花式遍历链表
* 关系数据模型
* 基本的数据结构就是表,实体和联系都是用表来表达
* 因为都是表,就可以用数学的集合论、关系代数之类的来进行研究
* 非过程化的查询语言——SQL,不像层次和网状模型是要自己写细节的,是过程化的查询语言
* 软连接:你像层次数据模型和网状表示关系就是直接硬链接的比如用指针啥的,像关系型数据模型是用一张新表来表达的,不是那么直接
* 关系模型基本术语
* 属性和域
* 域就是属性的取值范围
* 表的每一个属性都得是原子的不能再分的,也就是不允许出现表中套表——1NF(第一范式)
* 关系和元组
* 关系就是表
* 属性的个数被称为目
* 元组就是记录就是行
* 主键
* 候选键:身份证号
* 超键:身份证号+年龄
* 主键:就是某一个候选键,因为候选键可能有很多,就选一个当主键
* 外键:是另一张被引用的表的主键,外键其实就是一个指针嘛,所以和指针一样外键也不能指空
* 其他完整性约束
* 域完整性约束——就是说每个值满足这个值的值域的约束
* 实体完整性约束——就是主键不允许为空
* 关系代数
* 五个基本操作
* 选择
* 删除列(投影操作)
* 投影按理说会把重复的元组消掉,但是实际的数据库系统这么做不会消掉
* 笛卡尔乘积(两个表的拼接)
* 减(找出属于表1不属于表2的行)
* 并
* 连接操作joins
* 笛卡尔乘积+选择
* 除法操作
* 用例子来理解比较稳
* 用否定之否定来实现
* 外连接outer join
* 外并outer unions
* 关系演算
* 关系代数是根据过程来写的,关系演算是根据条件啊结果等来写的
* 元组关系演算
* 变量定义在元组(域)上
* 域关系演算
* 变量定义在域上
* SQL语言建立在关系演算之上的
* 总结
* E-R图
* 一些拓展的概念
* 弱实体
* 聚集:把联系集看成实体集
* 范畴
* 面向对象数据模型
* 打破了第一范式
* 应用的一般
* 其他数据模型
* 基于逻辑的数据模型
* 时态数据模型
* 空间数据模型
* XML数据模型
* 用户接口和SQL语言
* SQL以关系演算为基础
* SQL语句的执行过程(理论上,实际上比这会优化很多)
* 根据FROM后面的表做这些表的笛卡尔乘积
* 利用WHERE把不满足条件的元组剔除掉
* 根据SELECT后面的属性做一个投影只留下要的属性
* 如果还有ISTINCT语句的话还要去重
* 剩下的就在讲花式用SQL了,没听
* 数据库管理系统
* 内部体系结构
* DBMS内部组成结构
* 接口(用户接口+API)+核心(编译器+授权检查+语义分析和查询处理+(并发控制+访问管理+恢复机制))+操作系统
* 编译器:对SQL语句进行词法语法分析,生成语法树
* 授权检查:看当前这个用户能不能进行这个操作
* 语义分析和查询处理:就调用具体的函数比如SELECT、INSERT的函数进行具体执行
* 访问管理:把INSERT函数等落实到文件层上的实现,把表、视图等的操作转化成文件的操作
* 单纯的执行SQL的话并发控制和恢复机制并不是必须的
* DBMS的进程结构
* 单进程结构
* DBMS代码和应用程序代码捆绑形成一个EXE,直接运行
* 多进程结构
* DBMS核心作为一个进程
* 自己用JAVA等开发的应用程序代码作为一个进程
* 然后用管道(一个双向管道,读管道和写管道)来进行进程间的通信,也就是应用程序与DBMS进行通信
* 多线程结构
* 但是多进程很费,每个应用程序要访问数据库都要新创建一个DBMS的进程,很蠢
* 一个系统只有一个DBMS进程,每连接一次就创建一个DBMS线程
* 这个进程的资源包括:DAEMON\catalog\lock table\buffer
*
应用程序申请连接—>DAEMON捕获到请求你,为应用程序创建一个DBMS核心线程,建立通信管道—>应用进程和核心线程进行通信开始执行SQL—>完事之后就CLOSE关闭连接,线程销毁
* 访问管理——物理层实现
* 把对于数据库的访问转化为对文件的操作
* 一般一个文件就是一个表
* 访问类型
* 一个查询需要访问到一个表里大部分(15%以上)的元组
*
15%就认为是大部分了是因为:磁盘读取是按块读的,不是一个字符一个字符读的,一次读取1KB,假设一条元组100个字节的话说明一个块就有10个元组,所以如果元组是均匀分布的话这15%的元组几乎就散布在所有物理块里了
* 查找某一条特定的元组
* 查询一些些元组(15%以下)——比如在数据库里查找名字为张三的学生
* 范围查询
* 更新UPDATE
* 底层存储结构
* 堆文件:很蠢,就是随着数据的写入数据就不断的添加到后面。要查找的话就只能顺序扫描
* 哈希文件:就可以按照某个属性进行精确定位
* 索引文件:B+树索引 + 堆文件
* 很完美,又能顺序扫描,又能进行特定值的查询,又能用叶子结点进行范围查询
* 动态哈希:不像之前的静态哈希,随着数据的增减来动态调整哈希的映射空间
* 栅格结构文件:按照多维数据的方式进行存储数据
* raw
disk:比操作系统还底层,自己实现文件管理,自己规定数据怎么摆放,这样的话把数据都放在一起,这样读取的时候比较快美滋滋。所以一张表比如如果总是用第一个属性进行查询,那就可以按照第一个属性按顺序在磁盘上摆放,这样就特别的好找。当然这个属性最好别太频繁的更新,不然总要维护啊
* 索引技术
* B+树索引
* 蔟集:就是我上面raw disk里说的这个技术
* 倒排文件:就是有一个倒排表,其实也是一种索引吧,和现实生活中字典的索引表是一样的,这个百度百科里讲的可以。在所有属性上都做了索引
* 动态哈希
* 栅格文件和分区哈希
* 位图索引
* 注:其实就前两个用的多
* SQL查询优化
* 不能像理论上的那样直接笛卡尔乘积来查询,太慢了,所以需要优化
* 过程
* 代数优化
* 概述
* 把初始查询等价的情况下把顺序什么的进行调整
* 举了一个整数和的平方的例子
* 详细讲解
* 对SQL进行语法分析,把SQL语句转化成查询树,这里就用到了关系代数,进行了代数优化
* 具体的优化方法就是把选择、投影这种一元操作压到树的下面也就是叶节点的位置
* 这样选择完之后再做联接就数据量会少很多
* 操作优化
* 概述
* 怎样利用存储的结构进行优化查询
* 在上面整数平方和的例子也有体现
* 详细讲解
* 具体要进行联接的时候顺序怎么确定
* 联接操作也有很多算法,选哪个
* 代数优化相关概念
* 查询树
* 从树叶都树根的顺序就是DBMS内部的操作执行顺序
* 关系代数的等价变换规则
* 交换律:左右子树可以交换
* 结合律:查询树可以旋转
* 投影操作的串接律
* 选择操作的串接律
* 选择与投影操作的交换律
* 在某种条件下可以吧选择操作压到联接操作的下面(有三种情况)
* 并操作可以压下来
* 差操作可以压下来
* 某种情况下投影操作也可以拆成两部分压下来
* 并和投影的交换律
* 老师说还有很多,这里只是说出来感受感受
* 代数优化基本原则
* 把一元操作尽可能往下压
* 寻找合并公共子表达式(就是重复的子表达式别重复计算了嘛)
* 操作优化
* 优化选择操作
* 优化投影操作
* 优化集合操作
* 优化联接操作(重点)
* 最基本的最蠢的联接算法就是嵌套循环
* 归并扫描
* 两个表都已经按照要联结的属性按照从小到大的顺序排过序了
* 使用B+数索引或哈希(用得最多的)
* 还是嵌套循环,不过是把有索引的那个表作为内循环
* 外循环还是正经勤勤恳恳循环
* 内循环就不用扫描循环了,直接用索引去找就行了
* 这样就快了很多
* 但是有个问题啊:加入在有索引的那个表中需要查找的那个值对应的元组有很多个,那这样找的话还不如直接顺序循环找呢
* 哈希联接
* 直接对两个表用同一个散列函数进行散列处理,那自然两个表中联接属性相同的元组会存在一起
* 以后要联结的时候就方便多了,直接在哈希文件里找就行
* 但是咯要是这两个表总是更新,那也不行,维护太麻烦
* 优化组合操作
* 事务管理
* 恢复
* 常用的恢复策略
* 周期性的转储:就是周期性的备份一次
* 改进:备份+增量转储——这样就可以缩短备份的周期这样就更稳
* 备份+日志
* 日志:就是自从上次备份以来数据库所有的修改的记录
* 事务
* 性质ACID准则
* 原子性:要不一次成功要不都不行
* 保持一致性
* 隔离性:同时运行很多事物互不干扰。和进程之间的隔离性是一个意思
* 持久性:一个事务只要成功完成那么对数据库的影响是永远的
* 作用:银行转账的经典例子
* 恢复中要用到的数据结构
* commit list(提交事务列表):已经提交过了的事务的ID(TID)的列表
* Active list:系统中正在运行的事务的列表
* log日志:数据库中修改数据的老值链表+TID+新值链表
* 恢复时两个规则
* commit rule:事务要提交前一定要写到硬盘(可以是数据库可以使日志)上
* log ahead rule:改数据库前一定要先改日志
* 更新策略(三种)
* 先改数据库再提交,提交阶段就没什么事做了
*
先commit再改数据库。开始,每遇到一条SQL语句就写入LOG,最后遇到了commit,再把记录在日志里的新值改到数据库里(这种策略在并发时效率比较高)
* 可以认为是对2的优化,在把SQL结果写入LOG时也见缝插针的把LOG的改变写入数据库中,当然最后还是要把剩余的LOG中的信息写到数据库中
* 并发控制
* 并发会出现的问题
* 丢失更新 写写冲突
* 读脏数据 写读冲突
* 不可重复的读 读写冲突
* 并发控制方法
* 封锁法
* 每个事务进行操作时,都要拿到一个锁,有锁才能执行
* 锁协议
* X锁(排他锁,整个系统里只有一个锁)
* SX锁(这样的话就可以允许多个事务同时进行读操作了)
* S锁:读操作时,不排他
* X锁:还要进行写操作时,排他
* SUX锁
* U锁:要更新对象时先上U锁
* 然后UPDATE执行时真的要把这个上了U锁的数据写回数据库时再把数据升级为X锁开始排他
* 以上3个锁协议并发效率越来越高
* 定理
* 如果每一个事务是well_formed且是2PL的那肯定是可串行化的
* 如果每个事务是well_formed且是2PL的且unlock推迟到事务结束前——>这样的话不仅可串行化也是可恢复的
* 死锁与活锁
* 死锁:就是操作系统学的那个
* 解决
* 防
* 操作系统中有用到的但是在数据库中不使用的方法
* 运行前直接拿到所有需要的资源(表那么多何必呢)
* 给资源编号,只能从编号低的开始要(把表排序不现实,而且表的改变很快)
* 数据库中常用的
*
事务创建时赋予一个时间戳,资源产生竞争时让年老的事务等待年轻的事务(这里的意思是年轻的事务正在运行了,那这是年老的事务就得等),年轻的事务释放自己的资源(要是正在运行的是年老的事务,来竞争的是年轻的事务的话就kill掉年轻的事务),这样就不会有循环,用媳妇熬成婆的方法避免饥饿
*
也是有一个时间戳,企图抢占的事务要是发现已经在运行的事务比他年老就等待(老人优先嘛),要是比自己年轻就kill掉这个年轻的事务。和上面的那个方法一样也是一个媳妇熬成婆的方法。
* 治(死锁检测)
* 死锁检测方法
* timeout:要是等的时间太长了就认为死锁了
* 等待图方法:就是看等待图有没有环路
* 如何解决
* 选一个牺牲者kill掉
*  
* 活锁:就是操作系统里的饥饿现象
* 安全性和完整性约束
* 如何保证数据库安全性
* 视图和查询重写
* 因为视图就是在隐藏基表嘛
* 查询重写就是系统根据用户的权限自动重写
* 访问控制
* 对用户进行分类
* 普通用户
* 拥有某些资源特权的用户
* DBA(爸爸)
* 用户标识和身份验证
* 授权
* 完整性约束
* 就是一组规则,保证数据是正确的
* 静态约束
* 固有约束:如第一范式——不能表中套表
* 隐含约束:比如域完整性约束、主键约束、外键约束
* 显式约束
* 在应用程序里实现
* 利用ASSERT来实现
* 利用CHECK来实现
* 动态约束
* 和触发器有关
* 数据库设计
* 函数依赖:一个属性确定了,其他的至少一个属性也就确定了
* 多值依赖:一个属性确定了,另一个属性的一组值也确定了——所以函数依赖是多值属性的特例(现实中少)
* 联接依赖:如果一张表能够实现无损连接分解那么这张表就是联接依赖的(现实中少)
* 建表的原则:
* 第一范式:每个属性必须是原子的
*
第二范式:满足第一范式,不存在属性对主键的部分函数依赖(就是说啊要满足第二范式,不能有属性有这种情况,什么情况呢,这个属性直接只对主键的部分有函数依赖,也就是说只用主键的一部分就可以确定这个属性了)
* 不满足第二范式会产生的问题:
* 插入异常
* 删除异常
* 更新异常:更新时很难维护数据一致性
* 说白了就是书里的那个经典的例子,就是这张表数据冗余了,本来可以拆成两张表的
*
第三范式:满足第二范式,且不存在属性对主键的传递依赖(其实啊就是说不能有两个非主键属性它俩有传递关系比如能够通过一个推出另一个之类的,也就是说这两个属性中有一个并不是只由主键确定,还可以通过另一个属性确定)
* 不满足第三范式会产生的问题:
* 插入异常
* 删除异常
* 更新异常
* 说白了还是一种冗余,解决的话还是拆分
*
总结:任何一个表至少应该满足1NF——>消除属性对主键的部分依赖则满足2BF——>消除属性对主键的传递依赖满足3NF,同时还有一个BCNF这个比3NF更严格(老师说几乎大部分3NF就满足BCNF,太追求范式也不太好,没必要,最多到3NF)。在3NF的基础上消除属性之间的多值依赖就得到4NF——>在4NF的基础上消除属性之间的连接依赖就得到5NF
* E-R模型和E-R图
* 数据库设计的方法
* 面向过程的方法
* 比较low,就是按照自然的想法来设计
* 面向数据的方法
*
说了一下设计流程:需求分析——概念设计(画出E-R图)——逻辑设计(针对用的那个DBMS生成模式比如生成表什么的更具体了)——物理设计(数据在磁盘上到底怎么存)
* 分布式数据库
* 概念:就是数据库的数据是分布在网络上的
* 分类:
* 物理上分布,逻辑上集中——一般的分布式数据库(中央集权)
* 物理上和逻辑上都是分布的——联邦式数据库
* 为什么要分布式的
* 单台计算机存储不够了
* 优点
* 局部自治性
* 可用性好(一张表可以在两台计算机上存储,就是说可以有一个副本有这样一个冗余)
* 灵活性好(就是像云服务器一样可以很快的拓展之类的)
* 效率高(就是不同节点上运行的应用基本都可以在本地取数据,说白了分布式数据库还是在追求少用网络传数据)
* 并行
* 缺点
* 难以集成多个已有的数据库(是一种革命的建设方法)
* 太复杂
* 需要解决的主要问题
* 查询优化
* 优化目标是不一样的,集中式的是减少IO减少访盘次数,分布式的目标是减少网络传输
* 优化时代数优化是一样的,但是操作优化时还多了一步转化操作(就是把对表的查询转换为对裂片的查询)
* 并发控制
* 存在多个副本的时候单纯的分别加锁也还是会有数据不一致问题
* 全局死锁问题(出现全局的循环等待)
*  
* 恢复机制
* 事务内同时提交的问题
* 故障组合问题
* 数据如何分布的问题
* 数据分布策略
* 集中式的(极端情况):数据集中存放,应用分布运行。就像C/S结构嘛。
* 划分的方式:不同节点上的数据不重复
* 全复制(另一种极端情况):每个节点上的数据库都是一个完整的数据库。维护代价很大,但如果是只读的话就还行,只查询
* 混合方式:按照每个节点的应用的需要来放数据
* 数据的分布单位
* 以关系来分布(就是按表来分布数据)
* 根据裂片来分布(裂片就是把表进行切分的结果)
* 可以水平分割(按照元组分割)
* 竖直分割(按照属性进行分割)
* 混合分割
* 带来的问题
* 多副本的一致性
* 分布一致性
* 重新分布
* 背包
* 把全局查询转化成对裂片的查询和副本选择问题
* 如何分割的问题
* 联邦式数据库
* 企图采用松耦合的方法来实现多个已有的异构数据库的集成
* 定义:每个联邦成员都是自治的,成员之间通过协商相互协作,没有一个统一的全局模式。成员之间通过协商来决定各自的输入和输出模式
* 数据库在新领域的应用(拓展内容)
* 数据仓库和OLAP
* 原来学的都是OLTP应用——On-Line Transaction Processing联机事务处理
<https://baike.baidu.com/item/%E8%81%94%E6%9C%BA%E4%BA%8B%E5%8A%A1%E5%A4%84%E7%90%86>
过程。其基本特征是前台接收的用户数据可以立即传送到计算中心进行处理,并在很短的时间内给出处理结果,是对用户操作快速响应的方式之一
* 但是这样的话数据随着时间的推移积累的量太大了。然后人们就想进行一波数据挖掘找一波规律,然后就出现了数据仓库
* OLAP里面这个A就是analyse分析的意思
* 说白了数据仓库还是一个数据库,只不过是用来做决策分析的
* 数据挖掘
* 信息检索
* 半结构化数据和XML