Skip to content

Commit ff0a886

Browse files
committed
2021/5/26
1 parent 58af0e6 commit ff0a886

9 files changed

+91
-68
lines changed

mac mysql 环境搭建.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,12 @@ sudo rm -rf /var/db/receipts/com.mysql.*
6868

6969
2020-09-14T04:27:48.651023Z 1 [Note] A temporary password is generated for root@localhost: tP?!z_6Y;MX,
7070

71-
If you lose this password, please consult the section How to Reset the Root Password in the MySQL reference manual.
71+
If you lose this password, please consult the section How to Reset the Root Password in the MySQL reference manual.
72+
73+
74+
75+
76+
77+
vi ~/.bash_profile
78+
79+
source ~/.bash_profile

算法与数据结构.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,12 @@ T(n) = 2*T(n/2) + n
544544

545545
当 T(n/2^k)=T(1) 时,也就是 n/2^k=1,我们得到 k=log2n 。我们将 k 值代入上面的公式,得到 T(n)=Cn+nlog2n 。如果我们用大 O 标记法来表示的话,T(n) 就等于 O(nlogn)。所以归并排序的时间复杂度是 O(nlogn)。
546546

547+
548+
549+
tips : **T(n/2^k)=T(1) n/2^k 表示分解后的数据规模 2^k 表示把数据规模分解到1时的分解次数,故算法完成,数据规模分解到1。**
550+
551+
552+
547553
归并排序的执行效率与要排序的原始数组的有序程度无关,所以其时间复杂度是非常稳定的,不管是最好情况、最坏情况,还是平均情况,时间复杂度都是 O(nlogn)。
548554

549555
* 空间复杂度 O(n) (内存空间的占用并不随着程序的执行一直增长,而是可重复使用的,所以和时间复杂度的分析不一样)
59.7 KB
Loading
Loading
Loading
Loading
Loading
Loading

Mysql 实战.md renamed to 高性能Mysql.md

Lines changed: 76 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -551,13 +551,14 @@ select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx
551551
数据库端:
552552

553553
1. 监控 information_schema.Innodb_trx 表,设置长事务阈值,超过就报警 / 或者 kill;
554-
555554
2. Percona 的 pt-kill 这个工具不错,推荐使用;
556-
557555
3. 在业务功能测试阶段要求输出所有的 general_log,分析日志行为提前发现问题;
558-
559556
4. 如果使用的是 MySQL 5.6 或者更新版本,把 innodb_undo_tablespaces 设置成 2(或更大的值)。如果真的出现大事务导致回滚段过大,这样设置后清理起来更方便。
560557

558+
559+
560+
561+
561562
### 4.索引
562563

563564

@@ -570,9 +571,15 @@ select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx
570571

571572

572573

573-
>
574-
>
575-
> 为了让一个查询尽量少地读磁盘,就必须让查询过程访问尽量少的数据块。那么,我们就不应该使用二叉树,而是要使用“N 叉”树。这里,“N 叉”树中的“N”取决于数据块的大小。
574+
575+
576+
下面只说明 innoDB的 B-tree 索引
577+
578+
#### **4.1 索引存储结构**
579+
580+
581+
582+
> 为了让一个查询尽量少地读磁盘,就必须让查询过程访问尽量少的数据块。那么,我们就不应该使用二叉树,而是要使用“N 叉”树。这里,“N 叉”树中的“N”**取决于数据块的大小**
576583
>
577584
> 以 InnoDB 的一个整数字段索引为例,这个 N 差不多是 1200。这棵树高是 4 的时候,就可以存 1200 的 3 次方个值,这已经 17 亿了。考虑到树根的数据块总是在内存中的,一个 10 亿行的表上一个整数字段的索引,查找一个值最多只需要访问 3 次磁盘。其实,树的第二层也有很大概率在内存中,那么访问磁盘的平均次数就更少了。
578585
@@ -594,142 +601,144 @@ N 叉树由于在读写上的性能优点,以及适配磁盘的访问模式,
594601

595602

596603

597-
#### **最左匹配原则**
604+
> 为什么使用B树 ?
598605
599-
​ 当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;
606+
系统读取内存上的数据时是非常快的,主存存取的时间仅仅与存取次数呈线性关系,不存在机器操作,效率非常快。
600607

601-
​ 但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性
608+
系统读取磁盘上的数据时,磁盘I/O存在机械运动损耗,需要磁盘头去寻道,效率不高
602609

603-
最左前缀可以是联合索引的最左 N 个字段,也可以是字符串索引的最左 M 个字符
610+
索引一般以文件形式存储在磁盘上
604611

605-
#### InnoDB 的索引模型
612+
将一个B树的节点(包括索引页节点、数据节点)大小设为等于一个页,这样每个节点只需要一次 磁盘I/O就可以完全载入。
606613

614+
为了减少访问磁盘的次数,树要矮壮一些,树的高度不能太高,红黑树相对来说较高,多叉树,像B树非常合适。像上面分析的可以有1000+的叉,三层就可以存储百万以上的数据。搜索只需要最多只需要3次磁盘I/O
607615

608616

609-
每一个索引名称在 InnoDB 里面对应一棵 B+ 树。
610617

618+
> InnoDB (聚集索引) 与 MyISAM (非聚集索引)的区别 ?
611619
612620

613621

622+
Innodb使用的是聚集索引(**聚簇索引**),将主键作为索引,行数据存储在主键的索引树的叶子节点上。将数据存储与索引放到了一起。
614623

615-
例子:
624+
id 为主键有主键索引 name 为字段 有name字段的辅助索引
616625

617-
```mysql
626+
* 查询时用 where id = '1001' 直接查找这棵树的主键索引树,返回行数据。
627+
* 查询时用 where name = 'xxx' ,先搜寻name自己的辅助索引,辅助索引存储的是主键索引的值,拿到主键的值之后,再重新查询主键的索引树,拿到行数据。这个操作叫做 "回表" , 需要查询两棵树。
618628

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%;" />
625630

626-
<img src="Mysql 实战.assets/dcda101051f28502bd5c4402b292e38d.png" alt="dcda101051f28502bd5c4402b292e38d" style="zoom:67%;" />
627631

628-
R1~R5 的 (ID,k) 值分别为 (100,1)、(200,2)、(300,3)、(500,5) 和 (600,6)
629632

633+
MyISAM 使用的是非聚集索引,非聚集索引的两颗B+树没有什么区别,节点的结构完全一致,只是存储内容不同,主键的索引树的叶子节点存储的是主键,辅助索引树叶子节点存储的是辅助键。
630634

635+
行数据存储在独立的地方,辅助键检索树不需要通过主键去拿行数据,不会有"回表"的情况。
631636

632-
#### 索引类型
637+
<img src="高性能Mysql.assets/image-20210526155244876.png" alt="image-20210526155244876" style="zoom:50%;" />
633638

634-
* 主键索引 (聚簇索引 clustered index)
635-
* 非主键索引
636639

637640

641+
<img src="高性能Mysql.assets/image-20210526155455519.png" alt="image-20210526155455519" style="zoom:50%;" />
638642

639-
> 基于主键索引和普通索引的查询有什么区别?
640643

641-
* 如果语句是 select * from T where ID=500,即主键查询方式,则只需要搜索 ID 这棵 B+ 树;
642644

643-
* 如果语句是 select * from T where k=5,即普通索引查询方式,则需要先搜索 k 索引树,得到 ID 的值为 500,再到 ID 索引树搜索一次。这个过程称为**回表**
645+
二级索引同辅助索引
644646

645647

646648

647-
基于非主键索引的查询需要多扫描一棵索引树,在应用中应该尽量使用主键查询。
649+
**聚集索引的特点**
648650

651+
* 唯一性 数据和索引放在一起,所以一个表仅有一个聚簇索引。
652+
* 表中行的物理顺序和索引中行的物理顺序相同。数据行 按照一定的顺序排列,并且自动维护这个顺序(按照主键索引的顺序排列);
653+
* 聚集索引默认是主键,如果表中没有主键,InnoDB会选择唯一且非空的索引代替,如果没有这样的索引,InnoDB会隐式定义一个主键。
649654

650655

651-
* **自增主键** 都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂。用整型做主键,则只要 4 个字节,如果是长整型(bigint)则是 8 个字节。
652-
* **业务主键** 往往不容易保证有序插入,这样写数据成本相对较高。
653656

657+
**聚集索引的优点**
654658

659+
* 因为数据行是按照主键顺序排列的,所以可以把相关的数据保存在一起。
655660

661+
例如实现电子邮箱的 时候,可以根据用户id来聚集数据,这样只需要从磁盘读取少量的数据页就能获得某个用户的全部邮件。如果没有使用聚集索引,则每封邮件都可能导致一次磁盘I/O。
656662

663+
* 数据访问更快。因为数据和索引保存在同一棵B-Tress中。
657664

658-
```mysql
659-
# 重建索引 k
660-
alter table T drop index k;
661-
alter table T add index(k);
665+
* 覆盖索引索引扫描的查询可以直接使用页节点中的主键值。
662666

663-
# 重建主键索引
664667

665-
alter table T drop primary key;
666-
alter table T add primary key(id);
667-
```
668668

669+
**聚集索引的缺点**
669670

671+
* 对于在磁盘上的数据,聚簇索引极大的提高了效率。但是如果数据在内存中的话,数据的有序对访问效率就没那么重要了,聚集所以也就没什么优势了。因为内存根据地址直接返回数据,而磁盘需要一个磁头读取的时间。
672+
* 插入速度依赖于插入顺序。按照主键自增插入是最快的方法,但是如果不按照主键顺序插入数据,要移动后面的数据,如果页满了无法插入也会有**页分裂**的情况,影响性能。
673+
* 更新聚簇索引列的代价很高,InnoDB将每个被更新的行移动到新的位置。也可能会导致**页分裂**的问题。当行的主键值要求必须将这一行插入到某个已满的页中时,存储引擎会将该页分裂成两个页面来容纳该行,导致表占用更多的磁盘空间。
674+
* 辅助索引(二级索引)的叶子节点保存了行的主键列,主键列比较大的话,辅助索引也会比较占空间。
675+
* 二级索引访问需要两次索引查找,**回表**问题。
670676

671677

672678

673-
#### **索引下推**:
674679

675-
以市民表的联合索引(name, age)为例。如果现在有一个需求:检索出表中“名字第一个字是张,而且年龄是 10 岁的所有男孩”
676680

677-
```mysql
681+
**MyISAM数据分布**
682+
683+
MyISAM的数据分布很简单,按照数据的插入顺序存储在磁盘上。MyISAM的辅助索引(二级索引)和主键索引并无区别。
684+
685+
686+
687+
![MyISAM数据分布](高性能Mysql.assets/MyISAM数据分布.png)
678688

679-
mysql> select * from tuser where name like '张%' and age=10 and ismale=1;
680-
```
681689

682-
<img src="Mysql 实战.assets/89f74c631110cfbc83298ef27dcd6370.jpg" alt="89f74c631110cfbc83298ef27dcd6370" style="zoom:67%;" />
683690

684-
最左匹配,所以这个语句在搜索索引树的时候,只能用 “张”,找到第一个满足条件的记录 ID3。
685691

686-
然后判断其他条件是否满足。
687692

688-
MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
693+
**InnoDB 数据分布**
689694

690-
<img src="Mysql 实战.assets/b32aa8b1f75611e0759e52f5915539ac.jpg" alt="b32aa8b1f75611e0759e52f5915539ac" style="zoom:67%;" />
695+
![image-20210526200518391](高性能Mysql.assets/image-20210526200518391.png)
691696

692-
​ 图3**无索引下推的情况**
697+
InnorDB的聚簇索引,就是整张表。
693698

694-
<img src="Mysql 实战.assets/76e385f3df5a694cc4238c7b65acfe1b.jpg" alt="76e385f3df5a694cc4238c7b65acfe1b" style="zoom:67%;" />
699+
聚簇索引的每一个叶子节点都包含了
695700

696-
​ 图4**有索引下推的情况**
701+
* 主键值
702+
* 事务ID
703+
* 回滚指针 (用于事务和mvcc)
704+
* 剩余的数据列 (例子中是col2)
697705

698706

699707

700-
​ 图 3 中,在 (name,age) 索引里面我特意去掉了 age 的值,这个过程 InnoDB 并不会去看 age 的值,只是按顺序把“name 第一个字是’张’”的记录一条条取出来回表。因此,需要回表 4 次。图 4 跟图 3 的区别是,InnoDB 在 (name,age) 索引内部就判断了 age 是否等于 10,对于不等于 10 的记录,直接判断并跳过。在我们的这个例子中,只需要对 ID4、ID5 这两条记录回表取数据判断,就只需要回表 2 次
708+
InnorDB (辅助索引)二级索引的叶子节点存储的不是 “行指针”,而是主键值,以此去聚簇索引搜寻行数据
701709

702710

703711

704-
#### **覆盖索引**
712+
> innorDB 为什么建议自定义自增主键?
705713
706-
#### 查询只需要访问索引,而不需要访问数据行
714+
如果正在使用的 InnorDB表 没有什么数据需要聚集,那么可以定义一个代理键作为主键
707715

716+
这种主键的数据应该和业务无关,最简单的是使用 AUTO_INCREMENT 自增列。可以保证数据行是按顺序写入。
708717

718+
最好避免随机的(不连续且值的范围非常大)聚簇索引。
709719

710-
<img src="Mysql 实战.assets/image-20210524195201757.png" alt="image-20210524195201757" style="zoom:50%;" />
720+
从性能的角度,使用UUID作为索引很糟糕,使得聚簇索引的插入变得完全随机。数据没有任何聚集特性。
711721

712-
对于表中的每一行数据,索引包括了 last_name、first_name、dob列的值
722+
使用UUID作为主键插入数据时,因为新行的主键值不一定比之前插入大,所以InnoDB无法简单地总是把新行插入到索引的最后,而是需要寻找新的位置,很有可能是中间位置。这会导致:
713723

714-
<img src="Mysql 实战.assets/image-20210524194929591.png" alt="image-20210524194929591" style="zoom:50%;" />
724+
* 写入的目标页可能已经从缓存中移除,或者还没有加载到缓存,那就只能从磁盘去拿,导致大量的磁盘I/O
725+
* 写入是乱序的,InnoDB不得不频繁地做页分裂操作,以便为新的行分配空间,页分裂会导致大量的数据移动。
726+
* 由于页分裂,页会变得稀疏,最终会有数据碎片。
715727

716-
#### B-Tree 索引的限制:
728+
**tips**: 在把这些随机值载入到聚簇索引后,也许需要一次 **OPTIMIZE TABLE** 重建表的底层优化这些页。
717729

718-
* 如果不是按照索引的最左列开始查找,则无法使用索引。
719730

720-
例如: 无法查找名字为bill的人;无法查找某个特定生日的人;这都不是最左数据列。也无法查找姓氏以某个字母结尾的人。 ( 这是mysql相关的特性甚至和版本相关)
721731

722-
* 不能跳过索引的列。
732+
**顺序的主键也有缺点:**
723733

724-
例如: 无法查找姓为Smith并且在某个特定日期出生的人。因为跳过了first_name。如果跳过了first_name,则mysql只能使用索引的第一列
734+
高并发下,按主键顺序插入会造成 **争用**。主键的上界会成为 **热点**,因为所有的插入都发生在这里,并发插入会导致间隙锁竞争
725735

726-
* 如果查询中有某个列的范围查询,则其右边的列都无法使用索引查找
736+
另一个热点是 AUTO_INCREMENT 锁机制
727737

728-
例如: WHERE Last name= AND first name LIKE J%' AND dob = '1976-12-23'
729738

730-
这个查询只能用到索引的前两列,因为like是一个范围查找。
731739

732-
如果范围不大,可以用多个等于条件来代替
733740

734741

742+
建立索引的原则
735743

744+
索引的使用

0 commit comments

Comments
 (0)