索引是什么?
索引是高效获取数据的数据结构。
索引可以是hash的,可以是B+Tree的,hash索引存放在内存中,B+Tree存放在硬盘中
自适应hash索引(innodb引擎)
数据量大时内存占用大;只支持等值查询;对范围查询和排序支持不好。
哪种存储引擎支持手动hash索引?
memory,因为它将数据放在内存中。适合做中间表
AVL树为什么不能做索引的存储结构?
数据量大会导致树变深,增加查询IO次数;每个节点关键字只有一个,每次IO拿到数据太少(pagesize=16kb)
BTree为什么不做索引的存储结构?
最多关键字个数=路数-1
每个节点三部分:关键字,数据区,子节点指针。数据区理解为数据存储在磁盘中的位置,相当于磁盘指针
瘦高的树变为矮胖的树,减少IO次数,节点关键字变多,每次IO拿到的数据变多
B+Tree为什么作为索引存储结构?
基于索引排序和范围查找能力更强;基于索引的扫库扫表能力更强;查询时间稳定;取消了数据区使读写能力更强
B+Tree怎么存储?
都存在硬盘中,但myisam和innodb存储引擎有差别
myisam引擎有myd和myi文件,一个存放索引,一个存放数据。无论是主键索引还是非主键索引,都是查询到叶子节点,
叶子节点中存放的是磁盘指针,根据指针值在myd文件中获取具体内容。
innodb引擎中只有ibd文件,叶子节点直接挂载的是真正的行记录,根据主键查到后直接返回行记录。非主键索引的叶子节点存放的是索引列的值和主键的值。
根据非主键索引查到主键id,再根据主键ID找到行记录
聚集索引和非聚集索引
innodb引擎中,只有主键是聚集索引,其他都是非聚集索引
myisam引擎中,都是非聚集索引
手动建立主键索引的好处
如果不手动创建,MySQL会自动建_rowid的主键索引,int占用6kb,手动的为4kb,造成资源浪费;不手动建索引,还会使行锁变表锁
列的离散型
对离散型好的列建立索引,因为计算机在BTree中能清楚的知道走那一路,如果用离散型不好的列,例如sex建立索引,计算机不知道走哪一路,会都不走,变成无效索引
联合索引
例如查找"dafei+1385254225+18"数据,先比较name确认搜索方向,找到dafei后再依次比较phone和age,最后得到检索的数据。
如果查询条件改成phone+age,第一步就确定不了搜索方向,索引失效。
如果查询条件改为"dafei+18"会比较name找出所有叫dafei的,再依次比较其中的age。
以上就叫做最左匹配原则。
范围之后全失效
例如where name=dafei and phone>130000 and age=18
只生效了name和phone索引,因为name和phone筛选过后还有很多数据,条条大路通罗马,会都不走。这是索引的离散性。
优化器
选择性较好的列放在where左边,优化器自动调整。
连接器,分析器,优化器,执行器
like "dafei%" 会不会走索引?
这要看数据的离散性,如果是"aily","tom","dafei"这种数据,查询结果只有一个,会走索引,因为内部是ascii码值,会根据d分路,再根据a分路,依次类推。
如果是"dafei1","dafei2","dafei3"这种数据就不会走,条条大路通罗马,都不走
覆盖索引
查询的时候没有回表操作,直接返回数据叫覆盖索引。
什么叫回表操作?例如对name+phone建立联合索引,select * from user where name=dafei and phone=1380898 会先根据索引找到这一列的id在根据id返回数据,这样查询了id就叫回表操作。
反之select id,name,phone from user where name=dafei and phone=1380898这种就不触发回表操作,就是覆盖索引。
三星索引
1.where后面匹配的索引关键字列越多越好,扫描的数据越精确越少越好-通过索引筛选出的数据越少越好
2.避免再次排序(order by最好是索引列)
3.尽可能应用覆盖索引,减少回表操作
开启慢查询配置
慢查询是日志文件,记录查询时间超过阈值的sql语句
配置文件中
log-output=FILE #存储在文件中,可以是表,但不推荐
general-log=0 #未开启,不知道是什么
general-log-file="xxx.log"
slow-query-log=1 #开启慢查询
slow-query-log-file="xxx.log" #慢查询日志存放位置
long-query-time=10 #阈值,单位秒
如何查找慢查询日志中的哪些sql最慢?
perl mysql/bin/mysqldumpslow.pl -s t -t f:\xx.log (说明:第一个t表示按时间排序,第二个-t表示取前面5条)
常见优化方式
1、服务器硬件(略) 机械硬盘_固态硬盘
2、mysql服务器(略) linux-windows
3、sql本身优化
效率:关联查询join/表乘积select * from a,b where a.x=b.x>子查询(单独sql)>关联子查询(子查询关联其他表)
关联:禁止超过三张表的join,关联字段类型必须一致,关联字段必须有索引
规范:
1)【强制】不要使用 count(列名)或 count(常量)来替代 count(*),count(*)是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
说明:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
正例:使用如下方式来避免 sum 的 NPE 问题:SELECT IFNULL(SUM(column), 0) FROM table;
4)【强制】使用 ISNULL()来判断是否为 NULL 值。
说明:NULL 与任何值的直接比较都为 NULL。
(1) NULL<>NULL 的返回结果是 NULL,而不是 false。
(2) NULL=NULL 的返回结果是 NULL,而不是 true。
(3) NULL<>1 的返回结果是 NULL,而不是 true。
说明:以学生和成绩的关系为例,学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。
外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
8)【强制】数据订正(特别是删除、修改记录操作)时,要先 select,避免出现误删除,确认无误才能执行更新语句。
10)【推荐】in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。
11)【参考】如果有国际化需要,所有的字符存储与表示,均以 utf-8 编码,注意字符统计函数的区别。
说明:
SELECT LENGTH("轻松工作"); 返回为 12
SELECT CHARACTER_LENGTH("轻松工作"); 返回为 4
说明:TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。
4、反范式设计优化
范式化是为了减少冗余,对增,删,改比较友好,对查询不友好
冗余:对不经常修改的字段,允许适当冗余,提高查询性能
例如:where status=1 or status=2可以冗余字段,优化成 where status12=true
规范:
正例:商品类目名称使用频率高,字段长度短,名称基本一不变,可在相关联的表中冗余存储类目名称,避免关联查询。
5、物理设计优化 (字段类型,长度设计,存储引擎)
当一个列可以选择多种数据类型时,优先考虑数字类型,其次是日期/时间类型,最后是字符类型,能使用char的不使用varchar,对于相同级别的类型,应该优先选用占用空间小的数据类型
规范:
1)表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint(1 表示是,0 表示否)。 说明:任何字段如果为非负数,必须是 unsigned。
2)如果存储的字符串长度几乎相等,使用 char 定长字符串类型。
3)varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
4)表必备三字段:id, create_time, update_time。说明:其中 id 必为主键,类型为 bigint unsigned、单表时自增、步长为 1。create_time, update_time的类型均为 datetime 类型。
5)合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。
对象 年龄区间 类型 字节 表示范围
人 150 岁之内 tinyint unsigned 1 无符号值:0 到 255
龟 数百岁 smallint unsigned 2 无符号值:0 到 65535
恐龙化石 数千万年 int unsigned 4 无符号值:0 到约 42.9 亿
太阳 约 50 亿年 bigint unsigned 8 无符号值:0 到约 10 的 19 次方
6、索引优化
普通索引: 即一个索引只包含单个列,一个表可以有多个单列索引
唯一索引: 索引列的值必须唯一,但允许有空值
主键索引: 只要创建了主键就有主键索引。主键索引跟唯一索引的区别是不能为空
复合索引: 即一个索引包括多个列
聚集索引: 并不是一种单独的索引类型,而是一种数据存储方式,具体细节取决于不同的实现,在同一个结构中保存了B+Tree和数据行,比如innodb引擎。innodb中只有主键索引是聚集索引,其他都是非聚集索引。
非聚集索引: 除了聚集索引,其他的都是非聚集索引。 innodb引擎中出了主键,其他都是非聚集索引,myisam引擎中都是非聚集索引。
优化策略:范围条件放最后,建索引的时候,和查询的时候
规范:
1)主键索引名为pk_字段名;唯一索引名为 uk_字段名;普通索引名则为 idx_字段名
说明:不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外
即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。
3)【强制】超过三个表禁止 join。需要 join 的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段需要有索引。
说明:即使双表 join 也要注意表索引、SQL 性能。
4)【强制】在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度即可。
说明:索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分度会高达90%以上,可以使用 count(distinct left(列名, 索引长度))/count(*)的区分度来确定。
5)【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。
说明:索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。
6)【推荐】如果有 order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。
正例:where a=? and b=? order by c; 索引:a_b_c
反例:索引如果存在范围查询,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b; 索引 a_b 无法排序。
说明:MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行,那当offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL改写。
正例:先快速定位需要获取的 id 段,然后再关联: SELECT a.* FROM 表 1 a, (select id from 表 1 where 条件 LIMIT 100000,20 ) b where a.id=b.id
9)【推荐】SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是consts 最好。
说明:
(1) consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
(2) ref 指的是使用普通的索引(normal index)。
(3) range 对索引进行范围检索。
反例:explain 表的结果,type=index,索引物理文件全扫描,速度非常慢,这个 index 级别比较 range还低,与全表扫描是小巫见大巫。
10)【推荐】建组合索引的时候,区分度最高的在最左边。
正例:如果 where a=? and b=? ,如果 a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即可。
说明:存在非等号和等号混合时,在建索引时,请把等号条件的列前置。如:where c>? and d=? 那么即使 c 的区分度更高,也必须把 d 放在索引的最前列,即索引 idx_d_c。
(1) 宁滥勿缺。认为一个查询就需要建一个索引。
(2) 宁缺勿滥。认为索引会消耗空间、严重拖慢记录的更新以及行的新增速度。
(3) 抵制惟一索引。认为业务的惟一性一律需要在应用层通过“先查后插”方式解决。
怎样知道sql中用到索引没有?
执行sql语句时,前面加explain
----------------------------------------------------------------------------------------------
id select_type table type posible_keys key key_len ref rows Extra
----------------------------------------------------------------------------------------------
如果key有值表示用到了索引,key表示用到的索引名称
怎样知道索引是否被充分使用?
看key_len的值,
key_len的算法:4个参数
(1) 数据类型varchar+2 char+0 (2)字符集utf8=3字节 (3)是否为空 null+1 !null +0 (4)本身长度 例如varchar(50) 50表示50个中文或英文字符
例如:对date_str(50) not null 字段加了索引(复合索引),如果用到了该字段的索引,key_len=2+0+50*3=152
mysql并发的瓶颈?
一般300-700,硬件好的话最多1000,为什么,不知道
什么是数据库事务?ACID
原子性:事务不可分割,要嘛全部成功,要嘛全部失败
一致性:数据经过N多操作,数据是一致的,比如转账,总金额是一致的,一致性最关键
隔离性:各个事务之间之间不产生影响
持久性:事务执行成功进行落盘操作
隔离级别有哪些?
mysql事务测试
1.关闭自动提交
select @@autocommit; //查看事务是否自动提交
set autocommit=0; // 关闭自动提交
3.测试事务
read uncommitted 读未提交
命令:set session transaction isolation level read uncommitted;
read commited 读已提交
命令:set session transaction isolation level read committed;
repeatable read 可重复读
命令:set session transaction isolation level repeatable read;
searializible 序列化
脏读:在一个事务里,两次查询结果不一致,读到另一个事务未提交的数据
不可重复读:在一个事务里面,两次查询结果不一致,读到了另一个事务已提交的数据
幻读:在一个事务里,查询无数据,却insert主键失败(是另一个事务先commit insert导致)
测试方法:
打开A、B两个mysql客户端
1 A、B都关闭事务自动提交:set autocommit=0;
2 A、B都开始事务:start transaction;
3 A那边操作。。B那边操作。。。略
隔离级别 | 异常1 | 异常2 | 异常3 |
读未提交 | 脏读 | 不可重复读 | 幻读 |
读已提交 | | 不可重复读 | 幻读 |
可重复读 | | | 幻读 |
序列化 | | | |
事务有哪些操作?
1.开始事务 2. 执行语句(失败回滚) 3.提交事务(失败回滚)