1、什么是索引?

  索引是帮助MySQL高效获取数据的排好序的数据结构。

2、索引的数据结构  

  1. B+Tree(B-Tree变种)

    1. 非叶子节点不存储data,只存储索引(冗余),可以放更多的索引
    2. 叶子节点包含所有索引字段

    3. 叶子节点用指针连接,提高区间访问的性能

  2. MyISAM索引文件和数据文件是分离的(非聚集)

  3. InnoDB索引实现(聚集)

    1. 表数据文件本身就是按B+Tree组织的一个索引结构文件
    2. 叶节点包含了完整的数据记录

    3. 建议InnoDB表必须建主键,并且推荐使用整型的自增主键
    4. 为什么非主键索引结构叶子节点存储的是主键值?(一致性和节省存储空间)
      MySQL 底层原理+优化MySQL 底层原理+优化

  4. 联合索引数据结构
    MySQL 底层原理+优化

     

二、Explain工具使用

1、explain? 

  在 select 语句之前增加 explain 关键字,MySQL 会在查询上设置一个标记,执行查询会返回执行计划的信息,而不是执行这条SQL

  1. explain extended+show warnings:会在 explain 的基础上额外提供一些查询优化的信息。紧随其后通过 show warnings 命令可以得到优化后的查询语句
  2. explain partitions相比 explain 多了个 partitions 字段,如果查询是基于分区表的话,会显示查询将访问的分区。

2、explain中的列

MySQL 底层原理+优化

 

 

  • id:

有几个 select 就有几个id,并且id的顺序是按 select 出现的顺序增长的。id列越大执行优先级越高,id相同则从上往下执行,id为NULL最后执行。

  • select_type:

对应行是简单还是复杂的查询

    1. simple:简单查询。查询不包含子查询和union
    2. primary:复杂查询中最外层的 select
    3. subquery:包含在 select 中的子查询(不在 from 子句中)
    4. derived:包含在 from 子句中的子查询。MySQL会将结果存放在一个临时表中,也称为派生表(derived的英文含义)
    5. union:在 union 中的第二个和随后的 select

 

  • table这一列表示 explain 的一行正在访问哪个表。

  • type:

  • system > const > eq_ref > ref > range > index > ALL,一般来说要保证达到range级别

    1. const, system:mysql能对查询的某部分进行优化并将其转化成一个常量(可以看show warnings 的结果)。
    2. eq_ref:primary key 或 unique key 索引的所有部分被连接使用 ,最多只会返回一条符合条件的记录。
    3. ref:相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,索引要和某个值相比较,可能会找到多个符合条件的行。
    4. range:范围扫描通常出现在 in(), between ,> ,<, >= 等操作中。使用一个索引来检索给定范围的行。
    5. index:扫描全索引就能拿到结果,一般是扫描某个二级索引,这种扫描不会从索引树根节点开始快速查找,而是直接对二级索引的叶子节点遍历和扫描,速度还是比较慢的,这种查询一般为使用覆盖索引,二级索引一般比较小,所以这种通常比ALL快一些。
    6. ALL:即全表扫描,扫描你的聚簇索引的所有叶子节点。通常情况下这需要增加索引来进行优化了。
  • possible_keys:显示查询可能使用哪些索引来查找。
  • key:实际采用哪个索引来优化对该表的访问。
  • key_len:通过这个值可以算出具体使用了索引中的哪些列。
    • 字符串:char(n):3n ,  varchar(n): 3n + 2 ,加的2字节用来存储字符串长度,因为varchar是变长字符串
    • 数值类型:tinyint:1  smallint:2  int:4  bigint:8字节
    • 时间类型:date:3  timestamp:4  datetime:8
    如果字段允许为 NULL,需要1字节记录是否为 NULL索引最大长度是768字节,当字符串过长时,mysql会做一个类似左前缀索引的处理,将前半部分的字符提取出来做索引。
  • ref:这一列显示了在key列记录的索引中,表查找值所用到的列或常量,常见的有:const(常量),字段名(例:film.id)
  • rows:这一列是mysql估计要读取并检测的行数,注意这个不是结果集里的行数。
  • Extra:这一列展示的是额外信息。常见的重要值如下:
  1. Using index:使用覆盖索引
  2. Using where:使用 where 语句来处理结果,并且查询的列未被索引覆盖
  3. Using index condition:查询的列不完全被索引覆盖,where条件中是一个前导列的范围;
  4. Using temporary:mysql需要创建一张临时表来处理查询。出现这种情况一般是要进行优化的,首先是想到用索引来优化。
  5. Using filesort:将用外部排序而不是索引排序,数据较小时从内存排序,否则需要在磁盘完成排序。这种情况下一般也是要考虑使用索引来优化的。
  6. Select tables optimized away:使用某些聚合函数(比如 max、min)来访问存在索引的某个字段是

 

3、优化建议 

MySQL 底层原理+优化

 

三、bin-log归档

1、如果误删了数据库,可以使用bin-log进行归档

  • Binlog在MySql的Server层实现
  • bin-log为逻辑日志,记录每条语句的原始逻辑
  • bin-log不限大小,追加写入,不会覆盖以前日志

2、开启bin-log

#配置开启binlog,my.conf
log‐bin=/usr/local/mysql/data/binlog/mysql‐bin
#注意5.7以及更高版本需要配置本项:
server‐id=123454(自定义,保证唯一性);
#binlog格式,有3种statement,row,mixed
binlog‐format=ROW
#表示每1次执行写入就与硬盘同步,会影响性能,为0时表示,事务提交时mysql不做刷盘操作,由系统决定
sync‐binlog=1 

bin-log 命令

1 mysql> show variables like '%log_bin%'; 查看bin‐log是否开启 
2 mysql> flush logs; 会多一个最新的bin‐log日志 
3 mysql> show master status; 查看最后一个bin‐log日志的相关信息 
4 mysql> reset master; 清空所有的bin‐log日志

查看bin-log内容

1 mysql> /usr/local/mysql/bin/mysqlbinlog ‐‐no‐defaults /usr/local/mysql/data/binlog/mysql‐bin. 000001 查看binlog内容

数据归档操作

1 从bin‐log恢复数据  
2 恢复全部数据 
3 /usr/local/mysql/bin/mysqlbinlog ‐‐no‐defaults /usr/local/mysql/data/binlog/mysql‐bin.000001 |mysql ‐uroot ‐p tuling(数据库名) 
4 恢复指定位置数据 
5 /usr/local/mysql/bin/mysqlbinlog ‐‐no‐defaults ‐‐start‐position="408" ‐‐stop‐position="731" /usr/local/mysql/data/binlog/mysql‐bin.000001 |mysql ‐uroot ‐p tuling(数据库) 
6 恢复指定时间段数据 
7 /usr/local/mysql/bin/mysqlbinlog ‐‐no‐defaults /usr/local/mysql/data/binlog/mysql‐bin.000001 ‐‐stop‐date= "2018‐03‐02 12:00:00" ‐‐start‐date= "2019‐03‐02 11:55:00"|mysql ‐uroot ‐p test(数 据库)

四、性能调优

1、索引设计原则

1、代码先行,索引后上

  不知大家一般是怎么给数据表建立索引的,是建完表马上就建立索引吗?这其实是不对的,一般应该等到主体业务功能开发完毕,把涉及到该表相关sql都要拿出来分析之后再建立索引。

2、联合索引尽量覆盖条件

  比如可以设计一个或者两三个联合索引(尽量少建单值索引),让每一个联合索引都尽量去包含sql语句里的where、order by、group by的字段,还要确保这些联合索引的字段顺序尽量满足sql查询的最左前缀原则。

3、不要在小基数字段上建立索引

  索引基数是指这个字段在表里总共有多少个不同的值,比如一张表总共100万行记录,其中有个性别字段,其值不是男就是女,那么该字段的基数就是2。如果对这种小基数字段建立索引的话,还不如全表扫描了,因为你的索引树里就包含男和女两种值,根本没
法进行快速的二分查找,那用索引就没有太大的意义了。一般建立索引,尽量使用那些基数比较大的字段,就是值比较多的字段,那么才能发挥出B+树快速二分查找的优势来。

4、长字符串我们可以采用前缀索引

  尽量对字段类型较小的列设计索引,比如说什么tinyint之类的,因为字段类型较小的话,占用磁盘空间也会比较小,此时你在搜索的时候性能也会比较好一点。当然,这个所谓的字段类型小一点的列,也不是绝对的,很多时候你就是要针对varchar(255)这种字段建立
索引,哪怕多占用一些磁盘空间也是有必要的。对于这种varchar(255)的大字段可能会比较占用磁盘空间,可以稍微优化下,比如针对这个字段的前20个字符建立索引,就是说,对这个字段里的每个值的前20个字符放在索引树里,类似于 KEY
index(name(20),age,position)。此时你在where条件里搜索的时候,如果是根据name字段来搜索,那么此时就会先到索引树里根据name字段的前20个字符去搜索,定位到之后前20个字符的前缀匹配的部分数据之后,再回到聚簇索引提取出来
完整的name字段值进行比对。但是假如你要是order by name,那么此时你的name因为在索引树里仅仅包含了前20个字符,所以这个排序是没法用上索引的, group by也是同理。所以这里大家要对前缀索引有一个了解。

5、where与order by冲突时优先where

  在where和order by出现索引设计冲突时,到底是针对where去设计索引,还是针对order by设计索引?到底是让where去用上索引,还是让order by用上索引?一般这种时候往往都是让where条件去使用索引来快速筛选出来一部分指定的数据,接着再进行排序。
因为大多数情况基于索引进行where筛选往往可以最快速度筛选出你要的少部分数据,然后做排序的成本可能会小很多。

6、基于慢sql查询做优化

  可以根据监控后台的一些慢sql,针对这些慢sql查询做特定的索引优化。 

2、索引下推(like'Lilei%')

 可以在索引遍历过程中,对索引中包含的所有字段先做判断,过滤掉不符合条件的记录之后再回表,可以有效的减少回表次数。

  为什么范围查找Mysql没有用索引下推优化? 

 是Mysql认为范围查找过滤的结果集过大,like KK% 在绝大多数情况来看,过滤后的结果集比较小,所以这里Mysql选择给 likeKK% 用了索引下推优化,当然这也不是绝对的,有时like KK% 也不一定就会走索引下推。 

 

3、Order by与Group by优化

1、优化例子

  1. 利用最左前缀法则:中间字段不能断,因此查询用到了name索引,从key_len=74也能看出,age索引列用在排序过程中,因为Extra字段里没有using filesortMySQL 底层原理+优化
  2.  从explain的执行结果来看:key_len=74,查询使用了name索引,由于用了position进行排序,跳过了age,出现了Using filesort。

    MySQL 底层原理+优化

  3. 查找只用到索引name,age和position用于排序,无Using filesort。MySQL 底层原理+优化
  4. 和Case 3中explain的执行结果一样,但是出现了Using filesort,因为索引的创建顺序为name,age,position,但是排序的时候age和position颠倒位置了。 MySQL 底层原理+优化
  5. 与Case 4对比,在Extra中并未出现Using filesort,因为age为常量,在排序中被优化,所以索引未颠倒,不会出现Using filesort
    MySQL 底层原理+优化
  1.  虽然排序的字段列与索引顺序一样,且order by默认升序,这里position desc变成了降序,导致与索引的排序方式不同,从而产生Using filesort。Mysql8以上版本有降序索引可以支持该种查询方式。
    MySQL 底层原理+优化  
  2. 对于排序来说,多个相等条件也是范围查询

    MySQL 底层原理+优化

     

     

  3. 可以用覆盖索引优化 

    MySQL 底层原理+优化

     

     

2、优化总结

  1、MySQL支持两种方式的排序filesort和index,Using index是指MySQL扫描索引本身完成排序。index效率高,filesort效率低。
  2、order by满足两种情况会使用Using index。
    1) order by语句使用索引最左前列。
    2) 使用where子句与order by子句条件列组合满足索引最左前列。
  3、尽量在索引列上完成排序,遵循索引建立(索引创建的顺序)时的最左前缀法则。
  4、如果order by的条件不在索引列上,就会产生Using filesort。
  5、能用覆盖索引尽量用覆盖索引
  6、group by与order by很类似,其实质是先排序后分组,遵照索引创建顺序的最左前缀法则。对于groupby的优化如果不需要排序的可以加上order by null禁止排序。注意,where高于having,能写在where中的限定条件就不要去having限定了。 
 

4、分页查询优化

  很多分页查询的sql实现 ,看似只查询了 10 条记录,实际这条 SQL 是先读取 10010条记录,然后抛弃前 10000 条记录,然后读到后面 10 条想要的数据。因此要查询一张大表比较靠后的数据,执行效率是非常低的。 

 mysql> select * from employees limit 10000,10;

  常见技巧:

  1、根据自增且连续的主键排序的分页查询
  MySQL 底层原理+优化

   满足以下两个条件:

    • 主键自增且连续
    • 果是按照主键排序的 

 

  2、根据非主键字段排序的分页查询    

select * from employees ORDER BY name limit 90000,5;  (没有使用name索引)
select * from employees e inner join (select id from employees order by name limit 90000,5) ed on e.id = ed.id; (使用了索引,并且时间减少了一半以上)

  3、Join关联查询优化 
    3.1 标关联常见的两种算法      

      • 嵌套循环连接 Nested-Loop Join(NLJ) 算法 (走索引)
        一次一行循环地从第一张表(称为驱动表)中读取行,在这行数据中取到关联字段,根据关联字段在另一张表(被驱动表)里取出满足条件的行,然后取出两张表的结果合集。 
      • Block Nested-Loop Join 算法(不走索引)
        把驱动表的数据读入到 join_buffer 中,然后扫描被驱动表,把被驱动表每一行取出来跟 join_buffer 中的数据做对比。
         
        对于关联sql的优化
        关联字段加索引:让mysql做join操作时尽量选择NLJ算法
        小表驱动大表:写多表连接sql时如果明确知道哪张表是小表可以用straight_join写法固定连接驱动方式,省去mysql优化器自己判断的时间 

     3.2 in和exsits优化

 select * from A where id in (select id from B)

        in:当B表的数据集小于A表的数据集时,in优于exists 

        exists:当A表的数据集小于B表的数据集时,exists优于in 

      3.3 count(*)查询优化

        字段有索引:count(*)≈count(1)>count(字段)>count(主键 id) //字段有索引,count(字段)统计走二级索引,二级索引存储数据比主键索引少,所以count(字段)>count(主键 id)
        字段无索引:count(*)≈count(1)>count(主键 id)>count(字段) //字段没有索引count(字段)统计走不了索引,count(主键 id)还可以走主键索引,所以count(主键 id)>count(字段) 
 

5、阿里Mysql规范解读

  1、数值类型
  MySQL 底层原理+优化

优化建议
1. 如果整形数据没有负数,如ID号,建议指定为UNSIGNED无符号类型,容量可以扩大一倍。
2. 建议使用TINYINT代替ENUM、BITENUM、SET。
3. 避免使用整数的显示宽度(参看文档最后),也就是说,不要用INT(10)类似的方法指定字段显示宽度,直接用
INT。
4. DECIMAL最适合保存准确度要求高,而且用于计算的数据,比如价格。但是在使用DECIMAL类型的时候,注意
长度设置。
5. 建议使用整形类型来运算和存储实数,方法是,实数乘以相应的倍数后再操作。
6. 整数通常是最佳的数据类型,因为它速度快,并且能使用AUTO_INCREMENT。
 

2、日期和时间 
  MySQL 底层原理+优化 

  优化建议
  1. MySQL能存储的最小时间粒度为秒。
  2. 建议用DATE数据类型来保存日期。MySQL中默认的日期格式是yyyy-mm-dd。
  3. 用MySQL的内建类型DATE、TIME、DATETIME来存储时间,而不是使用字符串。
  4. 当数据格式为TIMESTAMP和DATETIME时,可以用CURRENT_TIMESTAMP作为默认(MySQL5.6以后),MySQL会自动返回记录插入的确切时间。
  5. TIMESTAMP是UTC时间戳,与时区相关。
  6. DATETIME的存储格式是一个YYYYMMDD HH:MM:SS的整数,与时区无关,你存了什么,读出来就是什么。
  7. 除非有特殊需求,一般的公司建议使用TIMESTAMP,它比DATETIME更节约空间,但是像阿里这样的公司一般会用DATETIME,因为不用考虑TIMESTAMP将来的时间上限问题。
  8. 有时人们把Unix的时间戳保存为整数值,但是这通常没有任何好处,这种格式处理起来不太方便,我们并不推荐它。

 

3、字符串 
  MySQL 底层原理+优化

  优化建议

  1. 字符串的长度相差较大用VARCHAR;字符串短,且所有值都接近一个长度用CHAR。
  2. CHAR和VARCHAR适用于包括人名、邮政编码、电话号码和不超过255个字符长度的任意字母数字组合。那些要用来计算的数字不要用VARCHAR类型保存,因为可能会导致一些与计算相关的问题。换句话说,可能影响到计算的准确性和完整性。
  3. 尽量少用BLOB和TEXT,如果实在要用可以考虑将BLOB和TEXT字段单独存一张表,用id关联。
  4. BLOB系列存储二进制字符串,与字符集无关。TEXT系列存储非二进制字符串,与字符集相关。
  5. BLOB和TEXT都不能有默认值。 
 

五、事务

  1、事务的特点(ACID)

    1. 原子性:一个事务是对数据库操作的最小单位,不可细分。
    2. 隔离性:事务之间可以同时执行,不会相互干扰。
    3. 一致性:事务执行成功数据库变更,事务执行失败则不会变更。
    4. 持久性:事务执行成功以后,结果是持久的。

  2、事务的隔离级别

    MySQL 底层原理+优化

 

    常看当前数据库的事务隔离级别: show variables like 'tx_isolation';
    设置事务隔离级别:set tx_isolation='REPEATABLE-READ';
    Mysql默认的事务隔离级别是可重复读,用Spring开发程序时,如果不设置隔离级别默认用Mysql设置的隔离级别,如果Spring设置了就用已经设置的隔离级别

   3、锁

    结论1

    1、对MyISAM表的读操作(加读锁) ,不会阻寒其他进程对同一表的读请求,但会阻赛对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。
    2、对MylSAM表的写操作(加写锁) ,会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作
    3、MyISAM在执行查询语句SELECT前,会自动给涉及的所有表加读锁,在执行update、insert、delete操作会自动给涉及的表加写锁。
    4、InnoDB在执行查询语句SELECT时(非串行隔离级别),不会加锁。但是update、insert、delete操作会加行锁。

    锁优化建议

    1、尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
    2、合理设计索引,尽量缩小锁的范围
    3、尽可能减少检索条件范围,避免间隙锁
    4、尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的sql尽量放在事务最后执行
    5、尽可能低级别事务隔离



 

 


 

 

 

 

 

 

 

 

 

 

 

 

相关文章: