干货-MySQL

索引是什么?

索引是高效获取数据的数据结构。

索引可以是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建立索引,计算机不知道走哪一路,会都不走,变成无效索引

联合索引

例如name+phone+age联合索引,相当于建立了三列的索引: name, name+phone,  name+phone+age  是从左到右建立搜索树的。
例如查找"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 值的行。

  2)【强制】count(distinct col) 计算该列除 NULL 之外的不重复行数,注意 count(distinct col1, col2) 如果其中一列全为 NULL,那么即使另一列有不同的值,也返回为 0。
  3)【强制】当某一列的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为 NULL,因此使用 sum()时需注意 NPE 问题。 

    正例:使用如下方式来避免 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。

  5) 【强制】代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句。 
  6)【强制】不得使用外键与级联,一切外键概念必须在应用层解决。 

    说明:以学生和成绩的关系为例,学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。

    外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。

  7) 【强制】禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。 
  8)【强制】数据订正(特别是删除、修改记录操作)时,要先 select,避免出现误删除,确认无误才能执行更新语句。
  10)【推荐】in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。
  11)【参考】如果有国际化需要,所有的字符存储与表示,均以 utf-8 编码,注意字符统计函数的区别。

    说明:
    SELECT LENGTH("轻松工作"); 返回为 12
    SELECT CHARACTER_LENGTH("轻松工作"); 返回为 4

     如果需要存储表情,那么选择 utf8mb4 来进行存储,注意它与 utf-8 编码的区别。
  12)【参考】TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,TRUNCATE 无事务且不触发 trigger,有可能造成事故,故不建议在开发代码中使用此语句

    说明:TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。



4、反范式设计优化
  范式化是为了减少冗余,对增,删,改比较友好,对查询不友好
  冗余:对不经常修改的字段,允许适当冗余,提高查询性能
  例如:where status=1 or  status=2可以冗余字段,优化成  where  status12=true
  规范:
  1)推荐】字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循: 1) 不是频繁修改的字段。 2) 不是 varchar 超长字段,更不能是 text 字段。 3) 不是唯一索引的字段。 

  正例:商品类目名称使用频率高,字段长度短,名称基本一不变,可在相关联的表中冗余存储类目名称,避免关联查询。

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_字段名
  2)【强制】业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。 

    说明:不要以为唯一索引影响了 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 无法排序。

  7) 【推荐】利用覆盖索引来进行查询操作,避免回表。 
  8)【推荐】利用延迟关联或者子查询优化超多分页场景。 

    说明: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。

  11)【推荐】防止因字段类型不同造成的隐式转换,导致索引失效。
  12)【参考】创建索引时避免有如下极端误解: 

    (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.提交事务(失败回滚)

原文地址:https://www.cnblogs.com/staff/p/13175019.html