@@ -551,13 +551,14 @@ select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx
551
551
数据库端:
552
552
553
553
1 . 监控 information_schema.Innodb_trx 表,设置长事务阈值,超过就报警 / 或者 kill;
554
-
555
554
2 . Percona 的 pt-kill 这个工具不错,推荐使用;
556
-
557
555
3 . 在业务功能测试阶段要求输出所有的 general_log,分析日志行为提前发现问题;
558
-
559
556
4 . 如果使用的是 MySQL 5.6 或者更新版本,把 innodb_undo_tablespaces 设置成 2(或更大的值)。如果真的出现大事务导致回滚段过大,这样设置后清理起来更方便。
560
557
558
+
559
+
560
+
561
+
561
562
### 4.索引
562
563
563
564
@@ -570,9 +571,15 @@ select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx
570
571
571
572
572
573
573
- >
574
- >
575
- > 为了让一个查询尽量少地读磁盘,就必须让查询过程访问尽量少的数据块。那么,我们就不应该使用二叉树,而是要使用“N 叉”树。这里,“N 叉”树中的“N”取决于数据块的大小。
574
+
575
+
576
+ 下面只说明 innoDB的 B-tree 索引
577
+
578
+ #### ** 4.1 索引存储结构**
579
+
580
+
581
+
582
+ > 为了让一个查询尽量少地读磁盘,就必须让查询过程访问尽量少的数据块。那么,我们就不应该使用二叉树,而是要使用“N 叉”树。这里,“N 叉”树中的“N”** 取决于数据块的大小** 。
576
583
>
577
584
> 以 InnoDB 的一个整数字段索引为例,这个 N 差不多是 1200。这棵树高是 4 的时候,就可以存 1200 的 3 次方个值,这已经 17 亿了。考虑到树根的数据块总是在内存中的,一个 10 亿行的表上一个整数字段的索引,查找一个值最多只需要访问 3 次磁盘。其实,树的第二层也有很大概率在内存中,那么访问磁盘的平均次数就更少了。
578
585
@@ -594,142 +601,144 @@ N 叉树由于在读写上的性能优点,以及适配磁盘的访问模式,
594
601
595
602
596
603
597
- #### ** 最左匹配原则 **
604
+ > 为什么使用B树 ?
598
605
599
- 当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;
606
+ 系统读取内存上的数据时是非常快的,主存存取的时间仅仅与存取次数呈线性关系,不存在机器操作,效率非常快。
600
607
601
- 但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性 。
608
+ 系统读取磁盘上的数据时,磁盘I/O存在机械运动损耗,需要磁盘头去寻道,效率不高 。
602
609
603
- 最左前缀可以是联合索引的最左 N 个字段,也可以是字符串索引的最左 M 个字符 。
610
+ 索引一般以文件形式存储在磁盘上 。
604
611
605
- #### InnoDB 的索引模型
612
+ 将一个B树的节点(包括索引页节点、数据节点)大小设为等于一个页,这样每个节点只需要一次 磁盘I/O就可以完全载入。
606
613
614
+ 为了减少访问磁盘的次数,树要矮壮一些,树的高度不能太高,红黑树相对来说较高,多叉树,像B树非常合适。像上面分析的可以有1000+的叉,三层就可以存储百万以上的数据。搜索只需要最多只需要3次磁盘I/O
607
615
608
616
609
- 每一个索引名称在 InnoDB 里面对应一棵 B+ 树。
610
617
618
+ > InnoDB (聚集索引) 与 MyISAM (非聚集索引)的区别 ?
611
619
612
620
613
621
622
+ Innodb使用的是聚集索引(** 聚簇索引** ),将主键作为索引,行数据存储在主键的索引树的叶子节点上。将数据存储与索引放到了一起。
614
623
615
- 例子:
624
+ id 为主键有主键索引 name 为字段 有name字段的辅助索引
616
625
617
- ``` mysql
626
+ * 查询时用 where id = '1001' 直接查找这棵树的主键索引树,返回行数据。
627
+ * 查询时用 where name = 'xxx' ,先搜寻name自己的辅助索引,辅助索引存储的是主键索引的值,拿到主键的值之后,再重新查询主键的索引树,拿到行数据。这个操作叫做 "回表" , 需要查询两棵树。
618
628
619
- mysql> create table T(
620
- id int primary key ,
621
- k int not null ,
622
- name varchar (16 ),
623
- index (k))engine= InnoDB;
624
- ```
629
+ <img src =" 高性能Mysql.assets/image-20210526151334965.png " alt =" image-20210526151334965 " style =" zoom :50% ;" />
625
630
626
- <img src =" Mysql 实战.assets/dcda101051f28502bd5c4402b292e38d.png " alt =" dcda101051f28502bd5c4402b292e38d " style =" zoom :67% ;" />
627
631
628
- R1~ R5 的 (ID,k) 值分别为 (100,1)、(200,2)、(300,3)、(500,5) 和 (600,6)
629
632
633
+ MyISAM 使用的是非聚集索引,非聚集索引的两颗B+树没有什么区别,节点的结构完全一致,只是存储内容不同,主键的索引树的叶子节点存储的是主键,辅助索引树叶子节点存储的是辅助键。
630
634
635
+ 行数据存储在独立的地方,辅助键检索树不需要通过主键去拿行数据,不会有"回表"的情况。
631
636
632
- #### 索引类型
637
+ < img src = " 高性能Mysql.assets/image-20210526155244876.png " alt = " image-20210526155244876 " style = " zoom : 50 % ; " />
633
638
634
- * 主键索引 (聚簇索引 clustered index)
635
- * 非主键索引
636
639
637
640
641
+ <img src =" 高性能Mysql.assets/image-20210526155455519.png " alt =" image-20210526155455519 " style =" zoom :50% ;" />
638
642
639
- > 基于主键索引和普通索引的查询有什么区别?
640
643
641
- * 如果语句是 select * from T where ID=500,即主键查询方式,则只需要搜索 ID 这棵 B+ 树;
642
644
643
- * 如果语句是 select * from T where k=5,即普通索引查询方式,则需要先搜索 k 索引树,得到 ID 的值为 500,再到 ID 索引树搜索一次。这个过程称为 ** 回表 ** 。
645
+ 二级索引同辅助索引
644
646
645
647
646
648
647
- 基于非主键索引的查询需要多扫描一棵索引树,在应用中应该尽量使用主键查询。
649
+ ** 聚集索引的特点 **
648
650
651
+ * 唯一性 数据和索引放在一起,所以一个表仅有一个聚簇索引。
652
+ * 表中行的物理顺序和索引中行的物理顺序相同。数据行 按照一定的顺序排列,并且自动维护这个顺序(按照主键索引的顺序排列);
653
+ * 聚集索引默认是主键,如果表中没有主键,InnoDB会选择唯一且非空的索引代替,如果没有这样的索引,InnoDB会隐式定义一个主键。
649
654
650
655
651
- * ** 自增主键** 都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂。用整型做主键,则只要 4 个字节,如果是长整型(bigint)则是 8 个字节。
652
- * ** 业务主键** 往往不容易保证有序插入,这样写数据成本相对较高。
653
656
657
+ ** 聚集索引的优点**
654
658
659
+ * 因为数据行是按照主键顺序排列的,所以可以把相关的数据保存在一起。
655
660
661
+ 例如实现电子邮箱的 时候,可以根据用户id来聚集数据,这样只需要从磁盘读取少量的数据页就能获得某个用户的全部邮件。如果没有使用聚集索引,则每封邮件都可能导致一次磁盘I/O。
656
662
663
+ * 数据访问更快。因为数据和索引保存在同一棵B-Tress中。
657
664
658
- ``` mysql
659
- # 重建索引 k
660
- alter table T drop index k;
661
- alter table T add index(k);
665
+ * 覆盖索引索引扫描的查询可以直接使用页节点中的主键值。
662
666
663
- # 重建主键索引
664
667
665
- alter table T drop primary key ;
666
- alter table T add primary key (id);
667
- ```
668
668
669
+ ** 聚集索引的缺点**
669
670
671
+ * 对于在磁盘上的数据,聚簇索引极大的提高了效率。但是如果数据在内存中的话,数据的有序对访问效率就没那么重要了,聚集所以也就没什么优势了。因为内存根据地址直接返回数据,而磁盘需要一个磁头读取的时间。
672
+ * 插入速度依赖于插入顺序。按照主键自增插入是最快的方法,但是如果不按照主键顺序插入数据,要移动后面的数据,如果页满了无法插入也会有** 页分裂** 的情况,影响性能。
673
+ * 更新聚簇索引列的代价很高,InnoDB将每个被更新的行移动到新的位置。也可能会导致** 页分裂** 的问题。当行的主键值要求必须将这一行插入到某个已满的页中时,存储引擎会将该页分裂成两个页面来容纳该行,导致表占用更多的磁盘空间。
674
+ * 辅助索引(二级索引)的叶子节点保存了行的主键列,主键列比较大的话,辅助索引也会比较占空间。
675
+ * 二级索引访问需要两次索引查找,** 回表** 问题。
670
676
671
677
672
678
673
- #### ** 索引下推** :
674
679
675
- 以市民表的联合索引(name, age)为例。如果现在有一个需求:检索出表中“名字第一个字是张,而且年龄是 10 岁的所有男孩”
676
680
677
- ``` mysql
681
+ ** MyISAM数据分布**
682
+
683
+ MyISAM的数据分布很简单,按照数据的插入顺序存储在磁盘上。MyISAM的辅助索引(二级索引)和主键索引并无区别。
684
+
685
+
686
+
687
+ ![ MyISAM数据分布] ( 高性能Mysql.assets/MyISAM数据分布.png )
678
688
679
- mysql> select * from tuser where name like ' 张%' and age= 10 and ismale= 1 ;
680
- ```
681
689
682
- <img src =" Mysql 实战.assets/89f74c631110cfbc83298ef27dcd6370.jpg " alt =" 89f74c631110cfbc83298ef27dcd6370 " style =" zoom :67% ;" />
683
690
684
- 最左匹配,所以这个语句在搜索索引树的时候,只能用 “张”,找到第一个满足条件的记录 ID3。
685
691
686
- 然后判断其他条件是否满足。
687
692
688
- MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
693
+ ** InnoDB 数据分布 **
689
694
690
- < img src = " Mysql 实战 .assets/b32aa8b1f75611e0759e52f5915539ac.jpg " alt = " b32aa8b1f75611e0759e52f5915539ac " style = " zoom : 67 % ; " />
695
+ ![ image-20210526200518391 ] ( 高性能Mysql .assets/image-20210526200518391.png )
691
696
692
- 图3 ** 无索引下推的情况 **
697
+ InnorDB的聚簇索引,就是整张表。
693
698
694
- < img src = " Mysql 实战.assets/76e385f3df5a694cc4238c7b65acfe1b.jpg " alt = " 76e385f3df5a694cc4238c7b65acfe1b " style = " zoom : 67 % ; " />
699
+ 聚簇索引的每一个叶子节点都包含了
695
700
696
- 图4** 有索引下推的情况**
701
+ * 主键值
702
+ * 事务ID
703
+ * 回滚指针 (用于事务和mvcc)
704
+ * 剩余的数据列 (例子中是col2)
697
705
698
706
699
707
700
- 图 3 中,在 (name,age) 索引里面我特意去掉了 age 的值,这个过程 InnoDB 并不会去看 age 的值,只是按顺序把“name 第一个字是’张’”的记录一条条取出来回表。因此,需要回表 4 次。图 4 跟图 3 的区别是,InnoDB 在 (name,age) 索引内部就判断了 age 是否等于 10,对于不等于 10 的记录,直接判断并跳过。在我们的这个例子中,只需要对 ID4、ID5 这两条记录回表取数据判断,就只需要回表 2 次 。
708
+ InnorDB (辅助索引)二级索引的叶子节点存储的不是 “行指针”,而是主键值,以此去聚簇索引搜寻行数据 。
701
709
702
710
703
711
704
- #### ** 覆盖索引 **
712
+ > innorDB 为什么建议自定义自增主键?
705
713
706
- #### 查询只需要访问索引,而不需要访问数据行 。
714
+ 如果正在使用的 InnorDB表 没有什么数据需要聚集,那么可以定义一个代理键作为主键 。
707
715
716
+ 这种主键的数据应该和业务无关,最简单的是使用 AUTO_INCREMENT 自增列。可以保证数据行是按顺序写入。
708
717
718
+ 最好避免随机的(不连续且值的范围非常大)聚簇索引。
709
719
710
- < img src = " Mysql 实战.assets/image-20210524195201757.png " alt = " image-20210524195201757 " style = " zoom : 50 % ; " />
720
+ 从性能的角度,使用UUID作为索引很糟糕,使得聚簇索引的插入变得完全随机。数据没有任何聚集特性。
711
721
712
- 对于表中的每一行数据,索引包括了 last_name、first_name、dob列的值
722
+ 使用UUID作为主键插入数据时,因为新行的主键值不一定比之前插入大,所以InnoDB无法简单地总是把新行插入到索引的最后,而是需要寻找新的位置,很有可能是中间位置。这会导致:
713
723
714
- <img src =" Mysql 实战.assets/image-20210524194929591.png " alt =" image-20210524194929591 " style =" zoom :50% ;" />
724
+ * 写入的目标页可能已经从缓存中移除,或者还没有加载到缓存,那就只能从磁盘去拿,导致大量的磁盘I/O
725
+ * 写入是乱序的,InnoDB不得不频繁地做页分裂操作,以便为新的行分配空间,页分裂会导致大量的数据移动。
726
+ * 由于页分裂,页会变得稀疏,最终会有数据碎片。
715
727
716
- #### B-Tree 索引的限制:
728
+ ** tips ** : 在把这些随机值载入到聚簇索引后,也许需要一次 ** OPTIMIZE TABLE ** 重建表的底层优化这些页。
717
729
718
- * 如果不是按照索引的最左列开始查找,则无法使用索引。
719
730
720
- 例如: 无法查找名字为bill的人;无法查找某个特定生日的人;这都不是最左数据列。也无法查找姓氏以某个字母结尾的人。 ( 这是mysql相关的特性甚至和版本相关)
721
731
722
- * 不能跳过索引的列。
732
+ ** 顺序的主键也有缺点: **
723
733
724
- 例如: 无法查找姓为Smith并且在某个特定日期出生的人。因为跳过了first_name。如果跳过了first_name,则mysql只能使用索引的第一列 。
734
+ 高并发下,按主键顺序插入会造成 ** 争用 ** 。主键的上界会成为 ** 热点 ** ,因为所有的插入都发生在这里,并发插入会导致间隙锁竞争 。
725
735
726
- * 如果查询中有某个列的范围查询,则其右边的列都无法使用索引查找 。
736
+ 另一个热点是 AUTO_INCREMENT 锁机制 。
727
737
728
- 例如: WHERE Last name= AND first name LIKE J%' AND dob = '1976-12-23'
729
738
730
- 这个查询只能用到索引的前两列,因为like是一个范围查找。
731
739
732
- 如果范围不大,可以用多个等于条件来代替
733
740
734
741
742
+ 建立索引的原则
735
743
744
+ 索引的使用
0 commit comments