目录
为什么要有索引?
索引可以理解为书的目录,通过目录查询效率高。
例如:在数据库中主键默认是添加索引的。
索引是什么?
索引是帮助 mysql 高效获取数据的数据结构,在mysql中使用B+树的
主键:默认添加索引,主键维护在一个B+树中,保存这条记录的物理地址。
优点:
可以提高查询效率(使用B+树),把索引数据加载到内存中),减少IO次数;索引使用B+树结构,是有序的,排序时比较快捷方便,减少CPU消耗。
缺点:
主键索引:设置为主键候自动建立索引。
ALTER TABLE 表名 add PRIMARY KEY 表名(列名);
删除主键索引:
ALTER TABLE 表名 drop
单值索引/单列索引:一个索引只能包含单个列,一个表可以有多个单列索引。
创建单值索引
CREATE INDEX 索引名 ON 表名(列名);
删除索引:
DROP INDEX 索引名
唯一索引:索引列的值必须唯一,允许为null
CREATE UNIQUE INDEX 索引名 ON 表名(列名);
删除索引:
DROP INDEX 索引名 ON 表名
组合索引:一个索引包含多个列。
组合索引最左前缀原则: 最左侧索引原则 在使用组合索引时,最左侧的列必须被使用到,否则索引失效。
创建复合索引
CREATE INDEX 索引名 ON 表名(列 1,列 2...)
删除索引
DROP INDEX 索引名 ON 表名;
例:
- select * from table where a=’’and b=’’ 索引生效
- select * from table where b=’’and a=’’ 索引生效
- select * from table where a=’’and c=’’ 索引生效
- select * from table where b=’’and c=’’ 索引不生效
全文索引:需要模糊查询时,一般索引无效。
CREATE FULLTEXT INDEX 索引名 ON 表名(字段名) WITH PARSER ngram;
SELECT 结构 FROM 表名 WHERE MATCH(列名) AGAINST(搜索词');
查看索引: SHOW INDEX FROM 表名;
索引创建原则
哪些情况下适合添加索引?
主键,默认添加唯一索引
作为查询条件的列
作为外键关联的列
排序的列
分组的列
哪些情况下不适合建立索引?
表记录很少(例如系统参数设置表)
不作为查询条件的列
增删改较为频繁的表,最好为需要的列来添加
数据重复较高的(如性别)
索引的数据结构
mysql使用B+树来存储索引
mysql使用B+树的原因?
有序节省排序时间,一个节点存储多个元素,树的高度降低了,叶子节点之间有指针,便于区间范围查询。
聚簇索引:找到了索引就找到了数据(例如innodb引擎,索引和数据在同一文件中,找到索引就可以找到数据,使用主键作为条件查询,使用其他的列进行查询,查询的结果只有自己)
非聚簇索引:找到了索引,还需要回表进行查询(例如myisam引擎中,索引和数据在两个不同的文件中,找到索引,还需要在存储数据的文件中进行查找)
innodb非聚簇:使用其他列作为查询条件,查询结果除了本列之外还有其他的内容,这种情况需要通过该列先找到主键,在通过主键再次回表查询数据。
概述
事务就是一次数据库操作中的若干单元的管理,事务管理的目标是完整性,一次中的若干操作要么都执行成功,要么都失败。
ACID(原子性(Atomicity,或称不可 分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久 性(Durability))
原子性:一个事务的一组操作(一个或多个sql语句),要么都执行成功,要么都不成功。
隔离性
为什么使用隔离性?
为了保证多个事务在并发执行的过程中,互不影响。
出现问题:脏读、不可重复读、幻读
脏读
事务A开启事务,查询表数据;
事务B开启事务,向表添加一条数据后,未提交;
事务A再次查询该表,查询到已经添加后的数据。
但是事务B可能会回滚,那么事务A再读到的就是脏数据。
不可重复读
事务A开启事务,查询到一条记录(例如age = 10),
事务B开启事务,修改这一条记录(age = 20),并提交事务,
事务A再次查询,事务A为修改后的数据(age = 20)。这就是不可重复读
幻读
事务A开启事务,查询到一条记录,
事务B开始开启事务,增加一条记录,提交,
事务A再次查询,查询到两条记录。
注:幻读是数量上的变化
为了出现了这些问题,我们设置了隔离级别,隔离级别一共有四个。
读未提交(read uncommitted)
一个事务可以读到另一个事务还没提交的数据。
描述:事务A开启事务,查询表数据,
事务B开启事务,向表添加一条数据后,未提交
事务A再次查询该表,查询到已经添加后的数据。
但是事务B可能会回滚,那么事务B读到的就是脏数据。
问题:可能会出现脏读,其中还会出现不可重复读,幻读的情况。
读已提交(read committed))
一个事务只能查询到另一个事务已经提交的数据。
描述:
事务A开启事务,向表插入一条数据,未提交
事务B开启事务,查询该表,未查询到该条记录
事务A提交数据,事务B再次查询,查询到该条记录。
目的:解决了脏读问题
问题:会出现不可重复读和幻读问题。
可重复读(repeatable read MySQL 默认隔离级别)
一个事务开始时读到了一个数据,在事务中继续执行再次读时,读到数据与第一次是一致的。
描述:
事务A开启事务,查询到一条记录,(保存在快照中)
事务B开启事务,修改这条记录,并提交。
事务A再次查询,查询到的是原来的记录。
目的:解决不可重复读问题,
注:mysql在可重复读级别上只在Innodb引擎中不存在幻读问题。
可串行化(serializable)
对表进行操作时,只能是一个一个事务执行,如果有一个事务在执行中,即使是读操作,那么其他事务也必须进行等待。
持久性:事务执行完成后,将执行后的数据持久化的保存到硬盘上,不可回滚。
一致性:在事务执行之前和之后都是为了保持数据库的完整性。
事务设置
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED --读未提交
redolog 用于保证事务持久性;undolog 则是事务 原子性和隔离性实现的基础。
原子性实现:使用 undo log对事物进行回滚,记录一个相反的操作。执行一个修改操作。后来又回滚了,那么数据需要知道执行前是什么样子的。
比如:在使用insert 操作时,undo log日志就会记录一个相反的操作 delete;在执行 delete 操作时,会同时记录insert操作。这样在事务回滚时,可以借助日志信息进行还原。
隔离性:
持久性实现:mysql对数据操作时,并不是立即将数据写入磁盘,这样io多,效率低。数据又不能一直保持在缓存中,万一服务器宕机,那么数据就不存在了。mysql提供了redolog日志,可以将数据先暂时保持在日志中,记录哪些数据发生了修改,定期将日志数据写到磁盘。
MVCC 多版本并发控制
目的:mvcc机制是为了提高mysql并发方法性能(读-写,写-读);
MVCC 使得数据库读不会对数据加锁,普通的 SELECT 请求不会加锁, 提高了数据库的并发处理能力 。 借助MVCC,数据库可以实现READ COMMITTED,REPEATBLE READ等隔离级别。
每个事务在对表记录操作时,会将之前的数据保存在undo log 中,表中有两个隐藏列(事务id,版本id)一个保存了行的事务 ID(DB_TRX_ID),一个保存了行的回滚指针(DB_ROLL_PT)。
事务1 第一次插入数据
事务 2 第一次修改数
事务 3 第二次修改数据
ReadView中存储一些当前操作的活跃事务对象id,记录当前事务id、最大或最小
当为可重复读时,事务A查询后,事务B进行2次修改后,事务A再查,只能查到当前的数据,不能读到修改后的数据。
关注点是从readView 中判断应该获取版本链中的哪条记录出来
对于看到的读已提交,为什么其他事务提交后,就可以读到最新的版本?
因为每次读的时候就会产生一个readView ,读到最新的数据,称为当前读。
对于可重复读,是第一次读取数据时,就会生成readView,这样之后,再次读时,与第一次的版本是一致的,称为快照。
多版本并发控制, 为了读已提交,可重复读提供版本记录,
读已提交:因为每次读的时候读到其他事务提交的最新记录
可重复读:因为读的时候会拍照,在同一个事务中一直从此快照中读
按锁的粒度划分
行级锁:一个事务在对某行数据操作(写),其他事务不能对此行进行操作,锁定一行。
优:并发性好
缺:加锁的频率高(开销较大)
mysql中innodb支持行锁(默认为行锁),myisam不支持行锁
表级锁:一个事务在对某表中某行操作时,将某个表锁定了,其他事务不能操作。
优:并发性低
缺:加锁的频率少(开销小)
mysql中innodb和myisam都支持表锁。
间隙锁:对表中数据的某个区间进行加锁(区间锁)
update test set name = '111' where id > 1 and id < 10
排他锁(X):写锁,实际意义上的加锁,有事务进行写操作,其他事务不能执行。执行增、删、改操作时,自动加锁;查询语句如果有需要,也可以加排他锁。
select * from test where id = 5 for update
共享锁(S):读锁,添加查询语句,添加共享后,其他事务也可以添加共享锁,但是其他事务就不能添加排他锁了。
select * from test where id = 5 lock in share mode
为什么要对Sql进行优化?
在数据量增大时,sql的执行效率对程序运行效率的影响逐渐增大,优化sql,可以提高查询效率。
几种优化方案
适当添加索引(添加索引的原则)
应尽量避免索引失效
状态字段/流程(尽量使用整数类型)
尽量使用varchar(定长使用char,变长使用varchar)
查询结果列出需要的列,不要用 *
尽量避免一次性查询过多的数据
尽量避免值为null,可以赋给默认值。null是会占空间的,在count统计时,不被统计
索引失效情况:
组合索引中,不满足最左前缀原则
like 模糊查询
以 null 为条件作为查询语句
例:select id from test where num is null
使用or进行条件连接
在where 中使用表达式/函数
sql发送给服务器,在服务器内如何执行,执行流程是怎们样的,先执行谁,有没有用到索引等.....
EXPLAIN 作用
使用explain关键字,添加到查询语句的前面,输出sql执行过程的参数。
EXPLAIN SELECT * FROM USER WHERE id = 1
1. id
sql执行顺序
例如有嵌套的子查询
主查询id 1
子查询id 2 表名子查询是先执行
2. select_type
表示查询结构
simple 简单查询
PRIMARY 主查询
SUBQUERY 子查询
3. type
查询性能指标
system>const>eq_ref>ref>range>index>ALL
system 表中只有一条记录
const 通过索引一次性可以找到
ref 使用了索引 例如姓名,查询出来可能会有多条数据
range 使用了索引 范围查询
index 类型只遍历索引树。
All 全表扫描 索引失效 查询所有数据了
主键自增 B+ 123456789 203423
4. possible_keys
可能用到的索引,key 实际用到的索引