三、数据库
1.连接查询
1.左连接
(左外连接) 以左表为基准 进行查询, 左表数据会全部显示 出来, 右表和左表匹配的数据 则会 显示 出 响应的数据 ,如果 不匹配,则显示为null
2.右连接
以 右表为基准 进行查询, 右表数据会全部显示出来,左表和右表匹配的数据则显示相应的字段的数据,不匹配返回null
2.聚合函数
1.聚合函数
SQL中提供的聚合函数可以用来统计、求和、求最值等
2.分类
COUNT :统计行 数量
SUM :获取单个列的 合计值
AVG :获取某个列的 平均值
MAX :获取列的 最大值
MIN :获取列的 最小值
3.SQL关键字
1.分页
MySQL的分页关键词 limit
SELECT * FROM student LIMIT 2,6 ;查询学生表中数据,从第三条开始显示,显示6条
2.分组
MySQL的分组关键字: group by
SELECT sex, count(*) FROM student GROUP BY sex
3.去重
去重关键字: distinct
select DISTINCT name from student;
4. SQL Select 语句完整的执行顺序:
查询中用到的关键词主要包含如下展示,并且他们的顺序依次为 form...on ... left...join ... where ... group by ... avg()/sum() ... having ... select ... order by ... asc/desc ... limit...
from:需要从那个数据表检索数据
where:过滤表中数据的条件
group by:如何将上面过滤出的数据分组算结果
order by:按照什么样的顺序来查看返回的数据
5.数据库三范式
第一范式:1NF 原子性 ,列或者字段不能再分,要求 属性具有原子性,不可再分解
第二范式:2NF 唯一性 , 一张表只说一件事,是对记录的唯一性约束,要求记录由唯一标示
第三范式:3NF 直接性 ,数据不能存在传递关系,即 每个属性都跟主键有直接关系,而不是间接关系
6. 存储引擎
1.MyISAM存储引擎
主要特点:
MySQL5.5版本之前的默认存储引擎
支持 表级锁 (表级锁式MYSQL中 锁定粒度最大的一种锁 ,表示对 当前操作的整张表加锁);
- 不支持事务,外键
2.每个 myisam 在磁盘上存储 3个文件 , 文件名和表明相同,扩展名分别是
.frm ------ 存储表定义
.MYD ------MYData, 存储数据
.MYI ------MYIndex, 存储索引
2.InnoDB存储引擎
主要特点:
MySQL 5.5版本之后的默认存储引擎;
支持事务;
支持 行级锁 ( 行级锁 是mysql中 锁定粒度最细 的一种锁,表示 只针对当前操作的行进行加锁);
支持 聚集索引方式存储数据。
MyISAM和InnoDB的区别: - MyISAM不支持事务
- innodb 支持事务
- innodb 提供行级锁和表锁
- myisam只支持表锁
- myisam使用非聚簇索引
- innodb使用聚簇索引
- MyISAM 的读操作更快 但是 写操作更慢
7.数据库事务
1.事务特性
原子性 :即不可分割性,事务要么 全部被执行 ,要么就 全部不被执行
一致性 :确保事务开始之前和完成之后,数据库都是处于一致的状态, 事务将数据库从一个一致状态转换到另一个一致状态 。
隔离性 : 确保 并发执行的事务不会互相干扰 ,每个事务 独立执行
持久性 : 事务正确提交后 ,其 结果将永久保存在数据库中,即使发送系统故障也不会丢失
2.隔离级别(并发事务问题)
脏读 问题: 一个事务读取 了 另一个事务未提交的数据
不可重复读 :一个事务内, 多次读取同一数据时 ,由于 其他事务的修改导致每次读取的结果不一致
虚读 :一个事务中, 查询结果集被其他事务 的 插入或删除 操作所 改变
(1)读未提交(read Uncommited)
在该隔离级别, 所有的事务都可以读取到别的事物中未提交的数据 ,会产生脏读问题,在项目中基本不怎么用, 安全性太差
(2) 读已提交(read commited):
这是大多数数据库默认的隔离级别,但是不是MYSQL的默认隔离级别;这个隔离级别满足了简单的隔离要求:一个 事务在执行过程中 , 只能查询到其他事务已经完成了的更改 ,所以会 避免脏读问题 ;由于 一个事务可以看到别的事物已经提交的数据 ,于是随之而来产生了 不可重复读 和 虚读等问题
(3) 可重复读 (Repeatable read):
这是 MYSQL的默认隔离级别 ,它 确保了一个事务中多个实例在并发读取数据的时候会读取到一样的数据;
不过理论上,这会导致另一个棘手的问题: 幻读(Phantom Read)。
幻读 指 当用户读取某一范围的数据行时 , 另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的"幻影"行。
innoDB 和Faicon存储引擎通过 多版本并发控制 ( MVCC ,Multiversion Concurrency Control) 机制解决了该问题。
3.5 MVCC(多版本并发控制) :通过 保持数据的不同版本 来 提高并发性能 ,从而 避免多个事务对同一数据的直接竞争
- 多版本存储 :不同事务读取不同版本
- 锁机制独立 :不使用锁 的情况提供并发控制 ,提高系统性能
- 事务隔离级别 :MVCC 主要是读已提交 和可重复读
- InnoDB实现:通过保存数据行的旧版本来实现
(4)可串行化(系列化)(serializable):
事务的最高级别 ,它通过 强制事务排序 ,使之不可能相互冲突,从而 解决幻读问题 。简言之,它是在 每个读的数据行上加上共享锁 。在这个级别,可能 导致大量的超时现象和锁竞争,一般为了提升程序的吞吐量不会采用这个;
MVCC的执行原理
全程Multi-Version Concurrency Control,多版本并发控制。指 维护一个数据的多个版本,使得读写操作没有冲突。
- 记录中的隐藏字段
- DB_TRX_ID :最近修改事务id ,记录插入这条数据或最后一次修改该记录的事务ID。
- DB_ROLL_PTR :回滚指针 ,指向这条记录的上一个版本 ,用于配合undo log,指向上一个版本
- DB_ROW_ID :隐藏主键 ,如果表结构没有指定主键,将会生成隐藏字段
- undo log:
回滚日志,在insert,update,delete的时候产生的便于数据回滚的日志。
当 insert 的时候,产生的undo log日志 只在回滚时需要 ,在 事务提交后,立刻删除。
而 update、delete 的时候、产生的undo log日志不仅在 回滚时需要,MVCC版本访问也需要 , 不会立即被删除。 - undo log版本链
不同事务或相同事务对同一条记录进行修改 ,会导致该记录的undolog生成 一条记录版本链表 ,链表的头部时最新的旧记录,链表尾部是最早的旧记录( 看图更容易理解 ) 通过DB_ROLL_PTR回滚指针连接
4.readview
1.ReadView(读视图)是 快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。
2.当前读:
读取的是记录版本的 最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:select ...lock in share mode(共享锁),select ...for update、update 、insert、delete(排他锁)都是一种当前读
3.快照读:
简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。 - Read Committed:每次select,都生成一个快照读
- Repeatable Read:开启事务后第一个select语句才是快照读的地方
8.索引
1. 索引的概念和优点
概念:
索引 存储在内存 中,为服务器存储引擎为了快速找到记录的 一种数据结构 。索引的主要作用是 加快数据查找速度 , 提高数据库的性能
空间换取时间
优点:
- 创建唯一性索引 ,保证 数据库表中每一行数据的唯一性
- 大大加快 数据的检索速度,这也是创建索引的最主要的原因
- 加速表和表 之间的连接,特别是在实现数据的参考完整性方面特别有意义
- 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
适合:
频繁 的 作为查询条件的字段 应该 创建索引
不适合: - 字段唯一性太差
- 更新很频繁
- 不出现where语句的字段
2.索引的分类
- 普通索引:最基本的索引,它没有任何限制
- 唯一索引 :与普通索引类似,不同的就是索引列的值必须唯一 ,但允许有空值 。如果是组合索引 ,则列值的组合必须唯一。
- 主键索引 :它是一种特殊的唯一索引 ,用于唯一标识数据库中的某一条记录,不允许有空值,一般用primary key来约束
- 联合索引 (复合索引):多个字段上建立的索引,能够加速复合查询条件的检索
- 全文索引:老版本MYSQL自带的全文索引只能用于数据库引擎为MyISAM的数据表,新版本MYSQL5.6的InnoDB支持全文索引。默认MYSQL不支持中文全文索引,可以通过扩展MYSQL,添加中文全文索引或为中文内容表提供一个对应的英文索引表的方式来支持中文
3. 索引的底层实现原理
1.索引结构
索引 是在 mysql 的 存储引擎(innoDB,MyISAM)层 中 实现的,而不是在服务层实现的。所以每种存储引擎的索引都不一定完全相同,也不是所有的存储引擎都支持所有的索引类型的,MYSQL目前提供了以下4中索引:
B+Tree索引:最常见的索引类型,大部分索引都支持B+树索引
Hash索引:只有Memory引擎支持,使用场景简单
R-Tree 索引( 空间索引 ):空间索引是MyISAM引擎的一个特殊索引类型,主要 地理空间数据,使用也很少
S-Full-tex ( 全文索引):全文索引也是MyISAM的一个特殊索引类型,主要用于全文索引,InnoDB从MYSQL5.6版本开始支持全文索引
2.BTree结构
B+Tree是BTree基础上进行演变的,所以我们先来看看BTree, BTree 又叫 多路平衡搜索树,
一颗 m叉BTree特性如下:
- 树中每个节点最多包含m个孩子
- 除根节点与叶子节点外,每个节点至少有 [ceil(m/2)](m/2向上取整)个孩子
- 若根节点 不是叶子节点,则至少有两个孩子
- 每个非叶子节点由n个key和n+1个指针 组成,其中[ceil(m/2)-1]<=n<=m-1以5叉BTree为例,key的数量 :公式推导[ceil**(m/2)-1** ]<=n <=m-1
3.B+Tree结构
B+Tree为BTree的变种,B+Tree与BTree的区别:
- B+树的叶子节点保存所有的key信息,依靠key大小排序
- B+树叶子节点元素维护了一个单向链表(MYSQL优化后叶子节点与叶子节点之间是双向链表)
所有的非叶子节点都可以看作是key的索引部分
由于B+树只有叶子节点保存key信息,查询任何key都要从root走的叶子。所以 B+Tree查询效率更稳定
Mysql索引数据结构对 经典的B+树进行了优化,在原B+树的基础上,增加了一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+树,提高区间访问的性能
MySQL中的B+Tree索引结构示意图
- 在B树中,每个节点都会包含索引及数据,所以树的层高相对来说会高一些
- 同时在进行数据查找的时候,由于数据是存储在每个节点的,所以极端情况下要遍历整个树才能找到数据,所以查询时间的稳定性较差
- B树的所有叶子节点都在同一层,意味着从根节点到每个叶子节点的距离相等,这样可以保证无论访问那个叶子节点,时间复杂度都是O(logn)
b+树 - B+树的非叶子节点只存储索引 ,所有数据记录保存到叶子节点 中,这样可以更好的缩小树的层高,提高查找效率
- B+树的所有叶子节点通过一个双向指针连接,使得范围查找的效率更高
4.如何避免索引失效
- 范围查询 ,右边列的索引会失效
索引失效案例
select* from tb_seller where name="小米科技"and status >"1" and address = "北京市";
address索引失效,因为status是大于号,范围查询 - 在索引上**使用运算(使用函数)**也会失效
比如在索引上使用切割函数,就会使索引失效.
- 字符串不加引号,造成索引失效
如果索引列是字符串类型的整数,条件查询的时候不加引号会造成索引失效,Mysql内置的优化会有隐式转换
- 尽量使用覆盖索引 ,**避免select *,**这样能提高查询效率
- or关键字连接
用 or分割 的条件 必须满足or前后的列都有索引,如果有一个没有那么索引都会失效
9.数据库锁
1.行锁和表锁
- 主要是针对锁粒度划分 的,一般分为:行锁 、表锁 、库锁
行锁 :访问数据库的时候, 锁定整个行数据,防止并发错误
表锁 :访问数据库的时候, 锁定整个表数据,防止并发错误 - 行锁和表锁的区别:
表锁 : 开销小 , 加锁快 , 不会出现死锁 ; 锁定粒度大 ,发送 锁冲突的概率高 , 并发度最低
行锁 :开销 大 ,加锁 慢 , 会出现死锁 ;锁定粒度 小 ,发送锁冲突的概率 低 ,并发度 高
2.悲观锁和乐观锁
- 悲观锁 :顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改 ,所以每次拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁
- 乐观锁 :顾名思义,就是很乐观的小伙,每次去拿数据的时候都会认为别人不会修改 ,所以不会上锁 ,但是更新的时候判断一下别人有没有更新 ,可以使用版本号 和CAS等机制
10.MySql 优化

(1) 定位执行效率慢的sql语句.(了解)
- 命令:show status like 'Com_',通过这条命令我们可以知道当前数据库是以查询为主还是更新为主,如果是查询为主,就重点查询;如果增删改多就重点优化写入操作。
- explain+sql语句查询sql执行过程,通过执行计划,我们能得到哪些信息:
- 哪些步骤花费的成本比较高
- 那些步骤产生的数量多
- 这条sql语句是否走索引
- showprofile分析SQL,可以查看所有sql语句的执行效率(所用时间). 前提是这个命令需要被打开, 严格的说也就是打开这个命令后执行的所有sql语句, 它都能记录下执行时间, 并展示出来. 可以通过这个命令分析哪些sql语句执行效率低. 耗时长, 就更有针对性的优化这条sql.
- 慢查询日志(常用的工具)慢查询日志记录了所有执行时间超过参数 long_query_time 的 sql 语句的日志, long_query_time 默认为 10 秒(可以通过配置文件设置), 日志保存在 /var/lib/mysql/目录下,有个slow_query.log 文件,
(2)优化索引
2.1索引的设计原则
索引的设计需要遵循一些已有的原则,这样便于提交索引的使用效率,更高效的使用索引
- 对查询频次较高 ,且数据量比较大 的表 ,建立索引
- 索引字段的选则 ,最佳候选列应当从where子句的条件中提取,如果where子句中的组合比较多,那么应当挑选最常用,过滤效果最好的列的组合
- 使用唯一索引 ,区分度越高 ,使用索引的效率越高
- 索引并非越多越好,如果该表增、删、改操作较多 ,尽量不要建立索引 ,过多的索引会降低表的维护效率
- 使用短索引,提高索引访问时的I/O效率,因此也响应提升了Mysql查询效率
- 如果where后又多个条件经常被用到 ,建议建立复合索引,复合索引需要遵循最左前缀法则,N个列组合而成的复合索引,相当于创建了N个索引
复合索引命名规则 index _表名_列名1_列名2_列名3
比如:create index idx_seller_name_sta_addr on tb_seller(name, status, address)
2.2避免索引失效
遵循最左前缀法则: 组合索引在内部 是以 树形结构存储 的,从 最左列开始 对 数据进行排序 。当 查询条件不在最左侧 的时候 数据库无法确定如何定位索引中的其他列
假设我们有一个组合索引(A, B, C)
正确:WHERE A = ... AND B = ... AND C = ...
错误:WHERE B = ... AND C = ...
- 如果在查询的时候,使用了复合索引 ,需要遵循最左前缀法则 ,也就是查询从索引的最左列开始,并且不能跳过索引中的列
- 尽量不要在where子句中 对字段进行null值判断,否则将导致引擎放弃使用索引而进行全表扫描
- where子句 中避免使用!=、>、<操作符,否则引擎将放弃使用索引而进行全表扫描
- 不做列运算 where age +1=10,任何列的操作都将导致表扫描,包括数据库函数、计算表达式 等都会使索引失效
- 尽量避免使用or 因为or必须左右都有索引,否则索引失效
- 字符串要加单引号: MYSQL的查询优化器,会自动的进行类型转换,造成索引失效
- 以**%开头的Like模糊查询**会导致索引实现
- 范围查询右边的列会索引失效
3.Sql语句调优
- 根据业务场景建立复合索引只查询业务需要的字段,如果这些字段被索引覆盖,将极大的提高查询效率
- 多表连接的字段上建立索引
- where条件字段上建立索引 ,但是where上不能使用运算函数,避免索引失效
- 排序字段上建立索引,因为排序效率低,添加索引能提高查询效率
- 优化insert语句 :尽量批量列插入 ,因为批量列插入数据要比单个列插入数据效率高
- 优化order by语句 :在使用order by 语句时,不要使用select * ,select 后面要查有索引的列 ,如果一条sql语句中对多个列进行排序 ,在业务允许情况下(尽量使用相同的排序方式)尽量同时用升序或同时用降序
- 优化group by语句 :我们进行某一个字段进行分组的时候,mysql默认了排序,但有时这个排序不是我们业务的需要,在进行额外的排序就会降低效率,所以可以禁用排序 (使用 order by null)
- 尽量避免子查询 ,可以将子查询优化为join多表连接查询
- select语句必须指明字段,避免使用select *
- 避免索引失效
4.合理的数据库设计(了解)
遵循数据库三范式:
- 第一范式:数据表中每个字段都必须是不可拆分的最小单元,也就是确保每一列的原子性
- 第二范式:满足第一范式后,表中每一列都必须有唯一性,都必须依赖于主键
- 第三范式:满足二范式后,表中的每一列只与主键直接相关而不是间接相关(外键也是直接相关),字段没有冗余
注意:没有最好的设计,只有最合适的设计,所以不要过分注重理论。
三范式可以作为一个基本依据,不要生搬硬套。
有时候可以根据场景合理地反规范化:
A:保留冗余字段。
当两个或多个表在查询中经常需要连接时,可以在其中一个表上增加若干冗余的字段,以避免表之间的连接过于频繁,一般在冗余列的数据不经常变动的情况下使用。
B:增加派生列。
派生列是由表中的其它多个列的计算所得,增加派生列可以减少统计运算,在数据汇总时可以大大缩短运算时间, 前提是这个列经常被用到, 这也就是反第三范式。
C:分割表。数据表拆分:
主要就是垂直拆分和水平拆分。水平切分:将记录散列到不同的表中,各表的结构完全相同,每次从分表中查询, 提高效率。垂直切分:将表中大字段单独拆分到另外一张表, 形成一对一的关系。
D: 字段设计 - 表的字段尽可能用NOTNULL 2. 字段长度固定的表查询会更快3. 把数据库的大表按时间或一些标志分成小表
11. binlog和redolog
binlog:
二进制日志,记录插入更新删除等操作的信息。
作用:数据恢复和复制。进行点对点复制和主从复制。
通过 重放binlog记录的事件进行恢复和同步
redo log
重做日志,主要用于记录数据库事务的修改。
作用:数据库系统崩溃后,利用redolog记录的信息恢复未完成的事务,从而保证事务的持久性和一致性
日志文件undo log 和 redo log的区别:
1.缓冲池与数据页
- 缓冲池(buffer pool) :主内存 中的一个区域,里面可以缓存磁盘上经常操作的真实数据 ,在执行CRUD操作时 ,先操作缓冲池中的数据 (若缓冲池没有数据,则从磁盘加载并缓存 ),以一定频率刷新到磁盘 ,从而减少磁盘IO ,加快处理速度
- 数据页 (page):是InnoDB 存储引擎磁盘管理的最小单元 ,每个页的大小默认为16KB 。页中存储的是行数据。
2.redo log------实现持久性
重做日志 ,记录的是事务提交时 数据页的物理变化,服务宕机可以用来同步数据。
该日志文件由两部分: 重做日志缓冲 ( 内存中 )(redo log buffer)以及 重做日志文件 ( 磁盘中 )(redo log file)。 当事务提交之后会把所有修改信息都存到该日志文件中,用于在刷新脏页到磁盘,发生错误时,进行数据恢复使用
3.undo log------实现原子性和一致性
回滚日志 ,用于 记录数据被修改前的信息 ,作用包含两个: 提供回滚 和 MVCC(多版本并发控制)。undo log和redo log记录物理日志不一样,他是逻辑日志。
记录的是 逻辑日志 ,当事务回滚时, 通过逆操作恢复原来的数据
- 可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然
- 当update一条记录时,他记录一条对应相反的update记录。当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。
总结:
redo log日志记录 的是 数据页的物理变化 ,服务宕机可用来 同步数据 ,而 undo log 不同,它主要记录的是 逻辑日志 。当事务回滚的时候,通过逆操作来恢复数据, 若删除一条数据,就会在undo log日志文件中新增一条delete语句 ,如果发生回滚则执行 逆操作
12.聚簇索引与非聚簇索引
聚簇索引:主要是指数据与索引放到一块,B+树 的叶子节点保存了整行数据,主键在作为聚簇索引的有且只有一个
非聚簇索引(二级索引):指的是数据与索引分开存储,B+树的叶子节点保存对应的主键,可以有多个,一般我们自定义的索引都是非聚簇索引
13.回表查询
回表查询:先通过二级索引找到对应的键值,然后再通过主键值找到聚集索引中所对应的整行数据,这个过程就是回表
14.(覆盖索引)超大分页问题: 返回的列 都 必须添加了索引
在数据量比较大的时候,如果进行limit分页查询,在查询时 越往后分页查询效率越低。
数据量比较大的时候使用分页查询的话 需要对数据进行排序,耗费的时间就比较多,通常使用 覆盖索引 和 子查询来解决
索引覆盖 :查询使用了索引,并且 返回的列 都 必须添加了索引
- 使用id查询,直接走聚集索引查询,一次索引扫描,直接返回数据。性能高
- 如果返回的列中没有创建索引,有可能会触发回表查询,尽量避免使用select *
先分页查询数据的id字段,确定了id之后,再用子查询来过滤,只查询这个id列表中的数据就可以了,因为查询id的时候,走的覆盖索引,所以效率可以提升很好