索引原理与数据库优化
Posted saoqiang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了索引原理与数据库优化相关的知识,希望对你有一定的参考价值。
存储引擎机制
# 磁盘预读
# 4096字节 block
# b树
# balance tree
# b+树
# 数据只存储在叶子节点
# 在子节点之间加入了双向地址连接,更方便的在子节点之间进行数据的读取
聚集索引
索引原理
索引相当于字典的音序表,如果要查某个字,如果不使用音序表,则需要从几百页中逐页去查。
# innodb索引
# 聚集索引 只有一个主键
# 辅助索引 除了主键之外所有的索引都是辅助索引
# 回表: 只查询一个索引并不能解决查询中的问题,还需要到具体的表中去获取正行数据
# myisam索引
# 辅助索引 除了主键之外所有的索引都是辅助索引
索引的影响
1、在表中有大量数据的前提下,创建索引速度会很慢
2、在索引创建完毕后,对表的查询性能会发幅度提升,但是写性能会降低
# 创建了索引之后的效率大幅度提高
# 文件所占的硬盘资源也大幅度提高
你是否对索引存在误解?
索引是应用程序设计和开发的一个重要方面。若索引太多,应用程序的性能可能会受到影响。而索引太少,对查询性能又会产生影响,要找到一个平衡点,这对应用程序的性能至关重要。一些开发人员总是在事后才想起添加索引----我一直认为,这源于一种错误的开发模式。如果知道数据的使用,从一开始就应该在需要处添加索引。开发人员往往对数据库的使用停留在应用的层面,比如编写SQL语句、存储过程之类,他们甚至可能不知道索引的存在,或认为事后让相关DBA加上即可。DBA往往不够了解业务的数据流,而添加索引需要通过监控大量的SQL语句进而从中找到问题,这个步骤所需的时间肯定是远大于初始添加索引所需的时间,并且可能会遗漏一部分的索引。当然索引也并不是越多越好,我曾经遇到过这样一个问题:某台mysql服务器iostat显示磁盘使用率一直处于100%,经过分析后发现是由于开发人员添加了太多的索引,在删除一些不必要的索引之后,磁盘使用率马上下降为20%。可见索引的添加也是非常有技术含量的。
结论 最好在建表时就添加索引 考虑好相关事情 索引不是越多越好会影响写性能
当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计非常有帮助。
索引的数据结构
1. 平衡树 b树
树状图是一种数据结构,它是由n(n>=1)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
特点:每个结点有零个或多个子结点;没有父结点的结点称为根结点;每一个非根结点有且只有一个父结点;除了根结点外,每个子结点可以分为多个不相交的子树
2. b+树
B+树是通过二叉查找树,再由平衡二叉树,B树演化而来
特性:
- 数据只存储在叶子节点
- 在叶子节点之间加入了双向地址连接,更方便的在子节点之间进行数据的读取
- 最左匹配特性
- 索引字段越小查询速度越快
- B+树的高度一般都在2~4层
- B+树索引可以分为聚集索引(clustered index)和辅助索引(secondary index),
- 其他的树根啊树枝啊保存的就是数据的索引
数据库中的
聚集索引与辅助索引相同的是:不管是聚集索引还是辅助索引,其内部都是B+树的形式,即高度是平衡的,叶子结点存放着所有的数据。
聚集索引与辅助索引不同的是:叶子结点存放的是否是一整行的信息
叶子结点存放的是索引值和主键**
回表: 只查询一个索引并不能解决查询中的问题,还需要到具体的表中去获取整行数据
索引字段要尽量的小
.索引的最左匹配特性
索引的种类
MySQL常用的索引
普通索引 index:加速查找
唯一索引:
-主键索引PRIMARY KEY:加速查找+约束(不为空、不能重复)
-唯一索引UNIQUE:加速查找+约束(不能重复)
联合索引:
-primary KEY(id,name):联合主键索引
-unique(id,name):联合唯一索引
-index(id,name):联合普通索引
聚集索引
聚集索引的好处之一:它对主键的排序查找和范围查找速度非常快,叶子节点的数据就是用户所要查询的数据。如用户需要查找一张表,查询最后的10位用户信息,由于B+树索引是双向链表,所以用户可以快速找到最后一个数据页,并取出10条记录
聚集索引的好处之二:范围查询(range query),即如果要查找主键某一范围内的数据,通过叶子节点的上层中间节点就可以得到页的范围,之后直接读取数据页即可
# priamry key 的创建自带索引效果 非空 + 唯一 + 聚集索引
自己不创建 系统默认创建
#如果未定义主键,MySQL取第一个唯一索引(unique)而且只含非空列(NOT NULL)作为主键,InnoDB使用它作为聚簇索引。
#如果没有这样的列,InnoDB就自己产生一个这样的ID值,它有六个字节,而且是隐藏的,使其作为聚簇索引。
好处
聚集索引的好处之一:它对主键的排序查找和范围查找速度非常快,叶子节点的数据就是用户所要查询的数据。如用户需要查找一张表,查询最后的10位用户信息,由于B+树索引是双向链表,所以用户可以快速找到最后一个数据页,并取出10条记录
聚集索引的好处之二:范围查询(range query),即如果要查找主键某一范围内的数据,通过叶子节点的上层中间节点就可以得到页的范围,之后直接读取数据页即可
- 叶子结点存放的是一整行的信息
- 由于实际的数据也是聚集索引的一部分,并且数据只能按照一棵B+树进行排序,所以每张表只能有一个聚集索引就是主键,(1、如果未定义主键,MySQL取第一个唯一索引(unique)而且只含非空列(NOT NULL)作为主键,InnoDB使用它作为聚簇索引;2、如果没有这样的列,InnoDB就自己产生一个这样的ID值,它有六个字节,而且是隐藏的,使其作为聚簇索引)
聚集索引的优点:
- 它对主键的排序查找和范围查找速度非常快,叶子节点的数据就是用户所要查询的数据。如用户需要查找一张表,查询最后的10位用户信息,由于B+树索引是双向链表,所以用户可以快速找到最后一个数据页,并取出10条记录
- 范围查询(range query),即如果要查找主键某一范围内的数据,通过叶子节点的上层中间节点就可以得到页的范围,之后直接读取数据页即可
辅助索引
表中除了聚集索引外其他索引都是辅助索引(Secondary Index,也称为非聚集索引)(unique key啊、index key啊),与聚集索引的区别是:辅助索引的叶子节点不包含行记录的全部数据。
叶子节点存放的是对应的那条数据的主键字段的值,除了包含键值以外,每个叶子节点中的索引行中还包含一个书签(bookmark),其实这个书签你可以理解为是一个{‘name字段‘,name的值,主键id值}的这么一个数据
通过辅助索引的叶子节点不能直接拿到age的值,需要通过辅助索引的叶子节点中保存的主键id的值再去通过聚集索引来找到完整的一条记录,然后从这个记录里面拿出age的值,这种操作有时候也成为回表操作,就是从头再回去查一遍,这种的查询效率也很高,
- 叶子结点存放的是索引值和主键
- 回表: 只查询一个索引并不能解决查询中的问题,还需要到具体的表中去获取整行数据
覆盖索引
设置字典name为辅助索引 查到他为name 就是覆盖索引
简单来说就是是设置什么查到什么
索引的两大类型hash与btree
#我们可以在创建上述索引的时候,为其指定索引类型,分两类
hash类型的索引:查询单条快,范围查询慢
btree类型的索引:b+树,层数越多,数据量指数级增长(我们就用它,因为innodb默认支持它)
#不同的存储引擎支持的索引类型也不一样
InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
Memory 不支持事务,支持表级别锁定,支持 B-tree、Hash 等索引,不支持 Full-text 索引;
NDB 支持事务,支持行级别锁定,支持 Hash 索引,不支持 B-tree、Full-text 等索引;
Archive 不支持事务,支持表级别锁定,不支持 B-tree、Hash、Full-text 等索引;
索引的优缺点
# 优点 : 查找速度快
# 缺点 : 浪费空间,拖慢写的速度
# 不要在程序中创建无用的索引
创建删除索引
#方法一:创建表时
CREATE TABLE 表名 (
字段名1 数据类型 [完整性约束条件…],
字段名2 数据类型 [完整性约束条件…],
[unique | fulltext | spatial ] index | kye
[索引名] (字段名)
);
实列
#方式一
create table t1(
id int,
name char,
age int,
sex enum('male','female'),
unique key uni_id(id),
index ix_name(name) #index没有key
);
#方法二:CREATE在已存在的表上创建索引
sreate [unique | fulltext | spatial ] index 索引名
on 表名 (字段名) ;
实列
create index ix_age on t1(age);
#方法三:ALTER TABLE在已存在的表上创建索引
ALTER TABLE 表名 ADD [UNIQUE | FULLTEXT | SPATIAL ] INDEX
索引名 (字段名) ;
实列
alter table t1 add index ix_sex(sex);
#删除索引:DROP INDEX 索引名 ON 表名字;
实列
drop index ix_sex on t1;
正确的使用索引
如何命中索引
在条件中不能带运算或者函数,必须是"字段 = 值"
4.数据对应的范围小一点
# between and > < >= <= != not in
6.多条件的情况
# and 只要有一个条件列是索引列就可以命中索引
# or 只有所有的条件列都是索引才能命中索引
# 字段 能够尽量的固定长度 就固定长度
不能命中索引条件
# 1.所查询的列不是创建了索引的列
# 2.在条件中带运算或者函数 不能命中,必须是"字段 = 值"
# 3.如果创建索引的列的内容重复率高也不能有效利用索引
# 重复率不超过10%的列比较适合做索引
# 4.数据对应的范围如果太大的话,也不能有效利用索引
# between and > < >= <= != not in
# 5.like如果把%放在最前面也不能命中索引
# 6.多条件的情况
# and 只要有一个条件列是索引列就可以命中索引
# or 只有所有的条件列都是索引才能命中索引
什么是联合索引
什么是联合索引
联合主键 联合唯一
# 7.联合索引
# 在多个条件相连的情况下,使用联合索引的效率要高于使用单字段的索引
# where a=xx and b=xxx;
创建联合索引
# 对a和b都创建索引 - 联合索引
# create index 索引名 on 表名(字段1,字段2)
# create index ind_mix on s1(id,email)
# 1.创建索引的顺序id,email 条件中从哪一个字段开始出现了范围,索引就失效了
# select * from s1 where id=1000000 and email like 'eva10000%' 命中索引
# select count(*) from s1 where id > 2000000 and email = 'eva2000000' 不能命中索引
# 2.联合索引在使用的时候遵循最左前缀原则
# select count(*) from s1 where email = 'eva2000000@oldboy';
# 3.联合索引中只有使用and能生效,使用or失效
# 字段 能够尽量的固定长度 就固定长度
# varchar 尽量往后面放
mysql 神器 explain
# 查看sql语句的执行计划
# explain select * from s1 where id < 1000000;
# 是否命中了索引,命中的索引的类型
关于explain,如果大家有兴趣,可以看看这篇博客,他总结的挺好的:http://www.cnblogs.com/yycc/p/7338894.html
开启慢日志
知道mysql可以开启慢日志
# 慢日志是通过配置文件开启
# 如果数据库在你手里 你自己开
# 如果不在你手里 你也可以要求DBA帮你开
事务
事务定义 一件事从开始发生到结束的整个过程
锁和事务
innodb存储引擎默认是行级锁
myISAM 表锁
事务
原子性
其对数据的修改,要么全部成功,要么全部都不成功。
一致性
事务开始到结束的时间段内,数据都必须保持一致状态。
隔离性
数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的"独立"环境执行。
持久性
事务完成后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。
我们可以在mysql事务处理过程中定义保存点(SAVEPOINT),然后回滚到指定的保存点前的状态。
定义保存点,以及回滚到指定保存点前状态的语法如下。
1.定义保存点---savepoint 保存点名;
2.回滚到指定保存点---rollback to savepoint 保存点名:
实列 开启一个事务
开启事务,给数据加锁
# begin;#开始
# select id from t1 where name = 'alex' for update;
# update t1 set id = 2 where name = 'alex';
# commit;#结束
实列 开启回滚
mysql> begin; -- 或者 start transaction;#开启事务
mysql> INSERT INTO user VALUES ('3','one','0','');#向表user中插入2条数据
mysql> INSERT INTO user VALUES ('4,'two','0','');#向表user中插入2条数据
mysql> select * from user;#查询user表
mysql> savepoint test;#指定保存点,保存点名为test
mysql> INSERT INTO user VALUES ('5','three','0','');#向表user中插入第3条数据
mysql> select * from user;#查表
mysql> ROLLBACK TO SAVEPOINT test;#回滚到保存点test
mysql> select * from user;#查表
数据表库的导入导出
# 备份表 :homwork库中的所有表和数据
# mysqldump -uroot -p123 homework > D:s23day42a.sql
# 备份单表
# mysqldump -uroot -p123 homework course > D:s23day42a.sql
# 备份库 :
# mysqldump -uroot -p123 --databases homework > D:s23day42db.sql
# 恢复数据:
# 进入mysql 切换到要恢复数据的库下
# sourse D:s23day42a.sql
mysqldump
语法:mysqldump -u用户名 -p -B(又不用自己创库) -d 库名>路径(g:avav.sql)
备份:mysqldump -uroot -p -B -d 库名>(g:avav.sql)
语法mysql -uroot -p < (g:avav.sql)
还原:mysql -uroot -p < 路径(g:avav.sql)
7表联查速度慢怎么办?
# 1.表结构
# 尽量用固定长度的数据类型代替可变长数据类型
# 把固定长度的字段放在前面
# 2.数据的角度上来说
# 如果表中的数据越多 查询效率越慢
# 列多 : 垂直分表
# 行多 : 水平分表
# 3.从sql的角度来说
# 1.尽量把条件写的细致点儿 where条件就多做筛选
# 2.多表尽量连表代替子查询
# 3.创建有效的索引,而规避无效的索引
# 4.配置角度上来说
# 开启慢日志查询 确认具体的有问题的sql
# 5.数据库
# 读写分离
# 解决数据库读的瓶颈
以上是关于索引原理与数据库优化的主要内容,如果未能解决你的问题,请参考以下文章