数据库范式
- 第一范式:最基本的范式,如果数据库表中所有字段值都是不可分解的原子值,就说明该数据库表满足第一范式
- 第二范式:关系模式必须满足第一范式,并且所有非主属性都完全依赖于主键。注意,符合第二范式的关系模型可能还存在数据冗余,更新异常等问题。eg:关系模型(学号、姓名、专业编号、专业名称)中,学号->姓名,而专业编号->专业名称,不满足数据库第二范式
- 第三范式:关系模型满足第二范式,所有非主属性对任何候选关键字都不存在传递依赖。即每个属性都跟主键又直接关系而不是间接关系。eg:关系模型(学号、姓名、年龄、性别、所在院校、院校地址)院校地址和学号不存在直接关系,所以不满足第三范式
MySQL架构
应用层:负责相应客户端请求,建立连接,返回数据
逻辑层:包括 SQK 接口,解析器,优化器,返回数据
引擎层:InnoDB,MyISAM
物理层:负责文件存储,日志等
简述SQL语言执行过程
- 客户端首先通过连接器进行身份认证和权限相关
- 如果执行查询语句的时候,先查询缓存,如果命中缓存,立刻返回存储在缓存中的结果,否则进入下一阶段
- 服务器端进行SQL解析、预处理、再由优化器生成对应的执行计划
- MySQL 根据优化器生成的执行计划,调用存储引擎的 API 来执行查询
- 将结果返回给客户端(增量、逐步返回的过程)
事务
事务指的是满足 ACID 特性的一组操作,可以通过 commit 提交一个事务,也可以使用 rollback 进行回滚
ACID
- 原子性(Atomicity)
事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚
- 一致性(Consistency)
数据库在事务执行前后都保持一致性状态,所有事物对同一个数据的读取结果都是相同的
- 隔离性(Isolation)
一个事务在最后提交之前,对其他事务都是不可见的
- 持久性(Durability)
一旦事务提交,其所作的修改将会永久保存到数据库中,即使系统发生崩溃,事务执行的结果也不会丢失

InnoDB如何保证事务的原子性、持久性和一致性?
-
利用undo log保障原子性。该log保存了事务发生之前的数据的一个版本,可以用于回滚,从而保证事务原子性。
-
利用redo log保证事务的持久性,该log关注于事务的恢复.在重启mysql服务的时候,根据redo log进行重做,从而使事务有持久性。
-
利用undo log+redo log保障一致性。事务中的执行需要redo log,如果执行失败,需要undo log 回滚。
数据库索引
概念
索引是对数据库表中一个或多个列的值进行排序的数据结构,以协助快速查询,更新数据库表中数据。索引的实现通常使用B_TREE及其变种。索引加速了数据访问,因为存储引擎不会再去扫描整张表得到需要的数据;相反,它从根节点开始,根节点保存了子节点的指针,存储引擎会根据指针快速寻找数据。
索引的优点
- 大大加快数据的检索速度
- 加速表和表之间的连接
- 在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间
- 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性
联合索引
使用多列值组成的 b+ tree 索引。遵循最左匹配原则,从左到右使用索引中的字段,一个查询可以只使用索引中的一部分,但只能是最左侧部分
什么情况下设置了索引但无法使用
- LIKE 语句模糊匹配
- OR 语句前后没有同时使用索引
- 数据类型出现隐式转化(如varchar不加单引号可能会自动转换成int型)
- 对于联合索引,必须满足 最左匹配原则(多列索引 col1,col2,col3,则索引生效的情形包括col1 或 col1,col2 或 col1,col2,col3)
- mysql 会一直向右匹配直到遇到范围查询(<, >, between, like)就停止匹配
什么样的情况适合创建索引
- 经常作查询选择的字段
- 经常作表连接的字段
- 经常出现在 order by、group by、disticnt 后面的字段
创建索引时需要注意什么
- 非空字段: 应该指定列为 NOT NULL,除非你想存储 NULL。在 MySQL 中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值
- 取值离散大的字段 :(变量各个取值之间的差异程度)的列放到联合索引的前面,可以通过count()函数查看字段的差异值,返回值越大说明字段的唯一值越多字段的离散程度高
- 索引字段越小越好 :数据库的数据存储以页为单位,一页存储的数据越多一次IO操作获取的数据越大效率越高
- 索引列不能参与运算:b+ tree 中存的都是数据表中的字段值,进行聚合运算时,需要把所有元素都应用函数才能比较,显然成本太大
聚簇索引和非聚簇索引
- 聚簇索引:又称主索引。主索引的叶子节点 data 域存着完整的数据记录,找到这个节点就能找到所有数据。因为无法把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引
- 非聚簇索引:辅助索引的叶子节点 data 域记录主键的值,因此在使用辅助索引进行查找的时候,需要先查找到主键值,再到主索引中寻找。
主键就是聚簇索引吗
并不是。
主键默认是加了唯一约束的聚簇索引,但是也可以在主键创建的时候,指定为唯一约束的非聚簇索引
b+ 树相对于 b 树的优点
- b+ 树只在叶节点中存储数据,内节点只存索引,所以每一次查询都需要遍历到叶节点,查询性能比较稳定;而 b 树内节点也存有数据,查找时需要找到匹配元素,最好情况下只要找到根节点,最坏情况下要查找到叶节点,性能不稳定
- 从磁盘IO的角度,b+ 树由于在内节点只存索引,所以单一节点可以存储更多的元素,使得在查询时IO次数更少
- b+ 树的叶节点通过链表连接起来,更便于范围查询
为什么不用二叉树,AVL,红黑树
- 对于二叉树,容易退化成链表,此时查询复杂度变成了O(n)
- 对于 AVL和红黑树,首先树是必须存在内存里的,数据库表太大了,存不进去;其次,这两种树都是二叉的,对于同样的数据层高更高,也就意味着要访问更多的节点,磁盘IO读写次数也就更多
锁
MySQL 中按粒度的锁分类
- 全局锁:
flush tables with read lock。当需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(增删改)、数据定义语句(建表、修改表结构)、更新类事务的提交语句
全局库的典型使用场景:做全库逻辑备份
- 表级锁: 对当前操作的整张表加锁,实现简单,加锁快,但并发能力低。表级锁有两种,一种是表锁,一种是元数据锁(meta data lock,MDL)
在MySQL5.5中引入MDL,MDL 不需要显式使用,在访问一个表的时候会被自动加上。MDL 的作用是保证读写的正确性。当对一个表做增删改查操作的时候,加MDL读锁;当要对表做结构变更操作的时候,加MDL写锁(读锁之间不互斥,读写锁之间、写锁之间互斥)
-
行锁: 锁住某一行,如果表存在索引,那么记录锁是锁在索引上的,如果表没有索引,那么 InnoDB 会创建一个隐藏的聚簇索引加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
-
Gap 锁:也称为间隙锁: 锁定一个范围但不包括记录本身。其目的是为了防止同一事物的两次当前读出现幻读的情况。
-
Next-key Lock: 行锁+gap锁
乐观锁和悲观锁
- 悲观锁:悲观地认为所有的操作都会引发线程安全问题。所以在执行操作之前都会加锁,并且要确保锁获取成功才能执行操作。
- 乐观锁:先进行业务操作,在最后提交更新数据时检查数据是否被更新过,若未被更新,则更新成功,否则就失败。一般做法是在需要锁的数据上增加一个版本号或时间戳
一般情况下,读多写少适合用乐观锁,写多读少适合用悲观锁。乐观锁在不发生取锁失败的情况下开销比悲观锁小,但是一旦发生失败回滚开销比较大,因此适合用在取锁失败概率比较小的场景,可以提升系统并发性能
排他锁和共享锁
-
共享锁 (share lock)又称读锁(S锁)。相互不阻塞,多个事务对于同一数据可以共享一把锁,可以访问到数据,但是不能修改
-
排他锁(exclusive lock)又称写锁(X锁)。会阻塞其他的写锁和读锁(不能和其他的锁共存),确保在给定时间内只有一个用户能执行写入并防止其他用户读取正在写入的同一资源
如何解决数据库死锁
- 直接等待当查询的时间到达锁等待超时的设定后放弃锁请求。(InnoDB 中,innodb_lock_wait_timeout 的默认值是50s,这意味着当出现死锁时,需要等待 50s 第一个被锁住的线程才会超时退出,这个时间往往是无法接受的)
- 主动发起死锁检测,发现死锁后,主动回滚到死锁链条中的某一个事务,让其他事务得以继续执行
MySQL是如何保证主备一致的?
MySQL通过binlog(二进制日志)实现主备一致。binlog记录了所有修改了数据库或可能修改数据库的语句,而不会记录select、show这种不会修改数据库的语句。在备份的过程中,主库A会有一个专门的线程将主库A的binlog发送给 备库B进行备份。其中binlog有三种记录格式:
- statement:记录对数据库进行修改的语句本身,有可能会记录一些额外的相关信息。优点是binlog日志量少,IO压力小,性能较高。缺点是由于记录的信息相对较少,在不同库执行时由于上下文的环境不同可能导致主备不一致。
- row:记录对数据库做出修改的语句所影响到的数据行以及对这些行的修改。比如当修改涉及多行数据,会把涉及的每行数据都记录到binlog。优点是能够完全的还原或者复制日志被记录时的操作。缺点是日志量占用空间较大,IO压力大,性能消耗较大。
- mixed:混合使用上述两种模式,一般的语句使用statment方式进行保存,如果遇到一些特殊的函数,则使用row模式进行记录。MySQL自己会判断这条SQL语句是否可能引起主备不一致,如果有可能,就用row格式, 否则就用statement格式。但是在生产环境中,一般会使用row模式。
存储引擎
InnoDB
MYSQL 默认的事务性引擎
实现四个标准的隔离级别。(提交读,未提交读,可重复读,序列化)默认级别是可重复读。在可重复读情况下,通过多版本并发控制(MVCC)+ Next-Key Locking 防止幻影读
主索引是聚簇索引
MyISAM
不支持事务
设计简单,数据以紧密格式存储
不支持行锁,只能对整张表加锁,读取时会对需要读到的表加共享锁,写入时则对表加排他锁
Redis
基于内存的,支持持久化的高性能键值对 NoSQL 数据库
支持五种数据类型:string, list, sorted set, hash
常被用做缓存的解决方案




