0
点赞
收藏
分享

微信扫一扫

算法-滑动窗口-串联所有单词的子串

微笑沉默 2023-08-23 阅读 46

文章目录

一、索引的概念与对比测试

  • 数据库表中存储的数据都是以记录为单位的,如果在查询数据时直接一条条遍历表中的数据记录,那么查询的时间复杂度将会是 O ( N ) 。
  • 索引的价值在于提高海量数据的检索速度,只要执行了正确的创建索引的操作,查询速度就可能提高成百上千倍。当一张表创建索引后,在数据库底层就会为表中的数据记录构建特定的数据结构,后续在查询表中数据时就能通过查询该数据结构快速定位到目标数据。
  • 索引虽然提高了数据的查询速度,但在一定程度上也会降低数据增删改的效率,因为这时在对表中的数据进行增删改操作时,除了需要进行对应的增删改操作之外,可能还需要对底层建立的数据结构进行调整维护。

常见索引分为:

  • 对比不加索引和加索引的效率差距

构建一个8000000条记录的数据:

mysql> select COUNT(*) from EMP;
+----------+
| COUNT(*) |
+----------+
|  8000000 |
+----------+
1 row in set (3.84 sec)

查找一个数据:

mysql> select * from EMP where empno=998877;
+--------+--------+----------+------+---------------------+---------+--------+--------+
| empno  | ename  | job      | mgr  | hiredate            | sal     | comm   | deptno |
+--------+--------+----------+------+---------------------+---------+--------+--------+
| 998877 | FRqhLk | SALESMAN | 0001 | 2023-08-17 00:00:00 | 2000.00 | 400.00 |    425 |
+--------+--------+----------+------+---------------------+---------+--------+--------+
1 row in set (4.80 sec)

mysql> select * from EMP where empno=998877;
+--------+--------+----------+------+---------------------+---------+--------+--------+
| empno  | ename  | job      | mgr  | hiredate            | sal     | comm   | deptno |
+--------+--------+----------+------+---------------------+---------+--------+--------+
| 998877 | FRqhLk | SALESMAN | 0001 | 2023-08-17 00:00:00 | 2000.00 | 400.00 |    425 |
+--------+--------+----------+------+---------------------+---------+--------+--------+
1 row in set (4.77 sec)

mysql> select * from EMP where empno=998877;
+--------+--------+----------+------+---------------------+---------+--------+--------+
| empno  | ename  | job      | mgr  | hiredate            | sal     | comm   | deptno |
+--------+--------+----------+------+---------------------+---------+--------+--------+
| 998877 | FRqhLk | SALESMAN | 0001 | 2023-08-17 00:00:00 | 2000.00 | 400.00 |    425 |
+--------+--------+----------+------+---------------------+---------+--------+--------+
1 row in set (4.80 sec)

可以看到时间为4.8左右

添加索引:

mysql> alter table EMP add index(empno);
Query OK, 0 rows affected (25.24 sec)
Records: 0  Duplicates: 0  Warnings: 0

再次查找相同数据:

mysql> select * from EMP where empno=998877;
+--------+--------+----------+------+---------------------+---------+--------+--------+
| empno  | ename  | job      | mgr  | hiredate            | sal     | comm   | deptno |
+--------+--------+----------+------+---------------------+---------+--------+--------+
| 998877 | FRqhLk | SALESMAN | 0001 | 2023-08-17 00:00:00 | 2000.00 | 400.00 |    425 |
+--------+--------+----------+------+---------------------+---------+--------+--------+
1 row in set (0.01 sec)

mysql> select * from EMP where empno=998877;
+--------+--------+----------+------+---------------------+---------+--------+--------+
| empno  | ename  | job      | mgr  | hiredate            | sal     | comm   | deptno |
+--------+--------+----------+------+---------------------+---------+--------+--------+
| 998877 | FRqhLk | SALESMAN | 0001 | 2023-08-17 00:00:00 | 2000.00 | 400.00 |    425 |
+--------+--------+----------+------+---------------------+---------+--------+--------+
1 row in set (0.00 sec)

可以看到几乎检测不到查询时耗费的时间。
根本原因就是,给员工工号创建索引后再根据员工工号来查询数据,这时就能够直接通过底层建立的数据结构来快速定位到目标数据,从而提高数据的检索速度,这就是索引的价值。

二、磁盘

2.1 磁盘与盘片的物理结构

  • 磁盘
    在这里插入图片描述
    磁盘通过磁头充放电,完成盘面南北极的调转,即二进制数据的写入。
  • 盘片

在这里插入图片描述
在这里插入图片描述

  • 磁道: 磁盘表面被分为许多同心圆,每个同心圆称为一个磁道,每个磁道都有一个编号,最外面的是0磁道。
  • 扇区: 每个磁道被划分成若干个扇区,每个扇区的存储容量为512字节,每个扇区都有一个编号。

2.2 磁盘的寻址

在这里插入图片描述
定位扇区时采用CHS寻址方式

总结:先通过H确定数据所在的盘面,再通过C确定数据所在的磁道,最后通过S定位到目标扇区。

2.3 磁盘的随机访问与连续访问

随机访问: 本次IO所给出的扇区地址与上次IO给出的扇区地址不连续,磁头在两次IO操作之间需要做比较大的移动动作才能找到目标扇区进行IO。
连续访问: 本次IO所给出的扇区地址与上次IO给出的扇区地址是连续的,磁头很快就能找到目标扇区进行IO。

因为连续访问中的连续指的是访问的扇区地址的连续,由于连续访问不需要过多的定位,因此效率比较高。

三、MySQL与磁盘交互的基本单位

MySQL 作为一款应用软件,可以想象成一种特殊的文件系统。它有着更高的IO场景,所以,为了提高基本的IO效率, MySQL 进行IO的基本单位是 16KB (后面统一使用 InnoDB 存储引擎讲解)

mysql> show global status like 'innodb_page_size';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| Innodb_page_size | 16384 |
+------------------+-------+
1 row in set (0.03 sec)

在这里插入图片描述

四、索引的理解

4.1 观察主键索引现象

创建一张表,把id设置成主键。

mysql> create table if not exists user(
    -> id int unsigned primary key,
    -> age int unsigned not null,
    -> name varchar(20) not null
    -> );
Query OK, 0 rows affected (0.04 sec)

插入一批数据:

mysql> insert into user values (3,8,'杨过'), (4,16,'小龙女'), (2,26,'黄蓉'), (5,36,'郭靖'), (1,15,'欧阳锋');
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> select * from user;
+----+-----+-----------+
| id | age | name      |
+----+-----+-----------+
|  1 |  15 | 欧阳锋    |
|  2 |  26 | 黄蓉      |
|  3 |   8 | 杨过      |
|  4 |  16 | 小龙女    |
|  5 |  36 | 郭靖      |
+----+-----+-----------+
5 rows in set (0.00 sec)

插入的时候是无序的,但是我们可以发现这里的id是排过序的。
这里要先知道page的数据结构:

4.2 MySQL对page的管理方式

MySQL与磁盘交互的基本单位是page(大小16KB)
MySQL一定会存在大量的page,所以操作系统一定要管理起来:

4.2.1 单page情况

比方说现在有很厚的一本书,怎么能让我们快速查找到我们想要的内容呢?答案是通过增加目录结构,虽然会占用保存数据的空间,但是大大增加了搜索速度。也就是空间换时间

在这里插入图片描述
现在要查找id=4位置就可以直接从目录2开始查询,不需要从头开始遍历了。

4.2.2 多page情况

MySQL每一页的大小只有16k,随着数据量的增加,就需要引入多个页来存储。
在这里插入图片描述
但是这样page之间也需要进行线性遍历(磁盘数据加载到内存,增加了IO次数)。

所以也可以对每个page再引入page的目录。

在这里插入图片描述
上图同样使用了page的方式来管理页目录,每个page中不包含有效数据,只包含对应page中的始末页目录,所以一个“一级目录”可以管理上千个page。

那么问题又来了,如果底层的page很多,势必会造成一级目录的数量变多,那么我们对一级目录的遍历不是又变成了线性遍历了吗?再套一层:
在这里插入图片描述

仔细看图会发现这就是一颗B+树。

细节问题:

总结 :

  • Page分为目录页和数据页。目录页只放各个下级Page的最小键值。
  • 查找的时候,自定向下找,只需要加载部分目录页到内存,即可完成算法的整个查找过程,大大减少了IO次数。

4.2.3 B+树与其他数据结构对比

链表和普通的二叉树搜索树(可能会退化成链表)自然不用说,效率比较低。

  • AVL和红黑树

AVL和红黑树虽然是平衡结构,但是它是“瘦高”状,路上节点越多,说明要进行更多次的IO(从磁盘加载进内存),层越低,MySQL与磁盘进行IO的次数就越少,所以B+树更加优秀。

  • 哈希表

官方的索引实现方式中MySQL是支持HASH的,只不过InnoDB和MyISAM存储引擎并不支持。哈希表的优点就是它的时间复杂度是 O ( 1 ) O(1)O(1) 的,但哈希表也有一个缺点就是不利于进行数据的范围查找

  • B树

B+树是非叶子节点不存数据,并且所有叶子节点用链表连接起来。
而B树每个page即包含目录项又包含数据,而且所有的叶子节点并没有链接起来。

既然每个page要包含数据,那么含有的目录项就减少了,那么这颗树就会变成“瘦高”状态,需要更多次IO,其次不利于范围查找数据,需要重新遍历B树。

4.2.4 聚簇索引 VS 非聚簇索引

4.2.4.1 MyISAM 存储引擎-主键索引

MyISAM 引擎同样使用B+树作为索引结果,和上一节讲的innodb存储引擎不一样的是,MyISAM叶节点的data域并不存放数据,而是存放的是数据的地址。下图为 MyISAM表的主索引, Col1 为主键。

在这里插入图片描述

4.2.4.2 MyISAM存储引擎 - 普通索引结构

MyISAM存储引擎的普通索引采用的也是B+树结构,与主键索引唯一不同的地方就是普通索引的B+树中的键值可以重复
因此一张表可能会同时存在多个B+树结构,但由于MyISAM存储引擎的B+树叶子结点中,存储的是对应的数据记录的地址,因此有效数据只会存储一份。

在这里插入图片描述

4.2.4.3 InnoDB存储引擎 - 普通索引结构

InnoDB存储引擎的普通索引采用的也是B+树结构,但普通索引的B+树中的键值可以重复,并且B+树的叶子结点中存储的不是数据记录,而是对应数据记录的主键值。
当根据普通索引查询数据时,会先查找普通索引对应的B+树找到目标记录的主键值,然后再查找主键索引对应的B+树找到目标记录,这个过程就叫做回表查询。

在这里插入图片描述
为何InnoDB针对这种辅助(普通)索引的场景,不给叶子节点也附上数据呢?数据主键索引有,没必要保存两份,不然太浪费空间了。

总结一下:

五、索引的操作

5.1 创建索引

5.1.1 创建主键索引

1️⃣ 建立表时就指明主键

mysql> create table user1(id int primary key, name varchar(20));
Query OK, 0 rows affected (0.05 sec)

2️⃣ 在创建表的最后,指定某列或某几列(复合主键)为主键索引

mysql> create table user2(id int, name varchar(20), primary key(id));
Query OK, 0 rows affected (0.03 sec)

3️⃣ 创建表以后再添加主键

mysql> create table user3(id int, name varchar(20));
Query OK, 0 rows affected (0.08 sec)

mysql> alter table user3 add primary key(id);
Query OK, 0 rows affected (0.07 sec)
Records: 0  Duplicates: 0  Warnings: 0

主键索引的特点如下:

5.1.2 创建唯一索引

1️⃣ 建表时指明唯一键

mysql> create table user4(id int primary key, name varchar(20) unique);
Query OK, 0 rows affected (0.03 sec)

2️⃣ 在创建表的最后,指定某列或某几列为唯一索引

mysql> create table user5(id int primary key, name varchar(20), unique(name));
Query OK, 0 rows affected (0.03 sec)

3️⃣ 创建表以后再添加唯一键

mysql> create table user6(id int primary key, name varchar(20));
Query OK, 0 rows affected (0.04 sec)

mysql> alter table user6 add unique(name);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

唯一索引的特点:

5.1.3 创建普通索引/复合索引

1️⃣ 建表时指明普通索引

mysql> system clear;
mysql> create table user8(id int primary key,
    -> name varchar(20),
    -> email varchar(20),
    -> index(name));
Query OK, 0 rows affected (0.04 sec)

2️⃣ 创建完表以后指定某列为普通索引

mysql> create table user9(id int primary key, 
    -> name varchar(20),
    -> email varchar(20),
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql> alter table user9 add index(name);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

3️⃣ 创建表以后再创建一个自定义名字的普通索引

mysql> create table user10(id int primary key,
    -> name varchar(20),
    -> email varchar(20));
Query OK, 0 rows affected (0.03 sec)

# 创建一个索引名为 idx_name 的索引
mysql> create index idx_name on user10(name);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

创建的复合索引其实是一颗B+索引,会发现name和email的普通键一样,复合索引的作用在于指定多个字段构建一颗B+树,如果需要高频的通过name找到email的操作,就可以构建复合索引,这样就避免了回表查询,通过索引找另一个索引的方式叫索引覆盖。

mysql> alter table user1 add index(name, email);
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc user1;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   | PRI | NULL    |       |
| name  | varchar(20) | YES  | MUL | NULL    |       |
| email | varchar(20) | NO   |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

mysql> show index from user1\G
*************************** 1. row ***************************
        Table: user1
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: id
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: 
   Index_type: BTREE
      Comment: 
Index_comment: 
*************************** 2. row ***************************
        Table: user1
   Non_unique: 1
     Key_name: name
 Seq_in_index: 1
  Column_name: name
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment: 
Index_comment: 
*************************** 3. row ***************************
        Table: user1
   Non_unique: 1
     Key_name: name
 Seq_in_index: 2
  Column_name: email
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: 
   Index_type: BTREE
      Comment: 
Index_comment: 
3 rows in set (0.00 sec)

可以看到Key_name值一样,用的是同一颗B+树。

5.2 查询索引

show keys from 表名;# 方式一
show index from 表名;# 方式二
desc 表名;# 方式三,这种方式显示出来的信息比较简略

mysql> show keys from user1;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user1 |          0 | PRIMARY  |            1 | id          | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| user1 |          1 | name     |            1 | name        | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| user1 |          1 | name     |            2 | email       | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec)

5.3 删除索引

  • 删除主键索引
alter table 表名 drop primary key;
  • 删除非主键索引
# 方法一
alter table 表名 drop index 索引名

# 方法二
drop index 索引名 on 表名

5.4 索引创建原则

  • 比较频繁作为查询条件的字段应该创建索引。
  • 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件。
  • 更新非常频繁的字段不适合创建索引。
  • 不会出现在where子句中的字段不应该创建索引。
举报

相关推荐

0 条评论