首页 > 数据库 >MySQL索引

MySQL索引

时间:2024-07-27 16:27:55浏览次数:11  
标签:index 扇区 索引 IO MySQL 磁盘 主键

索引的引入

在数据库表中,查询某条数据记录通常就是遍历,遍历表中所有的数据,然后一条一条比对,因此注定它是O(N)的时间复杂度。由于数据库的数据是存储在磁盘上的,必然要进行大量IO数据的读取,冯诺依曼体系告诉我们,对磁盘数据的读取效率是远低于与内存中数据的,尽管存在局部性原理,O(N)的时间复杂度依旧决定会进行大量的磁盘交互,这就是查询效率慢的主要原因。

索引就是一种提高搜索效率的有效方式,不需要修改程序,不需要调用sql,只需要建立正确的index索引就能提高搜索的效率。mysql会在磁盘上构建特定的数据结果(红黑树),提高优化算法+减少IO次数的方式,提高读取的效率。

必然的,搜索效率的提升,必然导致创建特定数据结构空间的增加。对应的增加/删除/修改等操作会更加费时费力。

所以,索引是为了提升select的速度,这个量级通常是成千上万倍。优化体现在算法+磁盘IO。

 常见的索引:

  • 主键索引       (primary key)
  • 唯一键索引 (unique key)
  • 普通键索引 (index key)
  • 全文索引(fulltext)

索引的优势

在sql下创建一张海量数据表(8 000 000条数据)。对比这俩次查找的时间

  • 1、在没有索引的情况下查询工号:777777的员工
  • 2、为员工编号创建索引,继续查询工号:777777的员工

1)在没有索引的情况下,直接查找编号,耗时 1.67s秒

2)为empno创建索引,并且查询777777

alter table EMP add index(empno)

以empno建立索引,会消耗一定的时间,因为底层需要创建相应的数据结构。

创建完毕后,继续查询指定员工

几乎立马就查询到结果,这个速度整整快了几万倍。

见识到索引的优势后,就来学习一下索引的原理,以及如果设置索引。


 必备的知识

为了认识索引,必须用要从底层讲起,也必须了解一下数据的存储与读取。

认识磁盘

磁盘的物理结构

磁盘是多种结果组成的,我们只需要了解几个特定的结构即可。

  • 磁盘:数据被通过电信号再在磁盘上,磁盘是数据实际存储元件。
  • 磁头:用于读取数据
  • 磁头臂:带动磁头左右移到
  • 主轴:使磁盘转动

认识盘片

 磁道:以主轴为圆心,可以将一块盘片分割成许多同心圆,每一个同心圆称为一个磁道

扇区:从圆心出发,将磁道分为若干段,每一个小段,就是扇区。

  • 数据的交互是以扇区为单位的,通常来说一个扇区512字节
  • 不难发现靠近圆心的扇区的面积小于远离圆心的扇区面积,所以靠近圆心的扇区密度更大
  • 近年来,容量更大的磁盘用4K取代512字节,称为4K扇区。

扇区的定位

linux中的文件就是存储在扇区上的,简单来说,想要获取到磁盘上的指定数据,就必须定位到指定编号的扇区。

定位的方法就是CHS寻址法

先找到柱面---再找到磁头---最后定位扇区。

  • 磁盘是由许多盘片组成,每一个盘片是由一个磁头,磁头是双面的,即一个盘片的正反面都能存储数据。
  • 先定位到是哪一个磁道,因为一排磁道构成柱面形状,也就是找到柱面。---H
  • 找到在哪一个磁头上,就是定位磁头。---H
  • 定位到某一个盘面的磁道和扇区后,通过旋转轴旋转,就能找到指定的扇区。

但是系统定位磁盘是通过线性结构LBA,将每一个磁道展开,一个磁道是由对应编号的扇区,这个映射关系由OS替我们完成。

单次IO的大小

通过磁盘的物理结构,我们知道扇区的最小单位是512字节,有的是4K,那么OS与磁盘交互是以512字节为基础建立的吗?
实际上并不是,而是以4K进行交换

原因主要是:

  1. 物理内存是被划分为4KB的页框,磁盘也被划分为4KB的页帧,这样符合对齐。
  2. 如果读取依赖于磁盘的扇区的大小,那么磁盘和OS就是强相关,不利于硬件的更新换代。
  3. 局部性原理:请求某一个扇区的资源后,下一次请求大概率会在这个扇区附近,直接把相邻扇区的资源也进行IO,大概率会减少IO的次数。

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

连续访问:在某一次访问扇区后,下一次访问靠近该扇区,也就是磁头的转动几乎是不计算的。

随机访问:改变磁头、判断的位置,耗费大量的时间,俩次访问地址相差较大。

所以:连续访问是优于随机访问的。

MySQL与磁盘交互的基本单元

MySQL是建立在应用层上的服务,在客户端会通过OS请求磁盘的IO,完成数据的CURD。

我们可以把MySQL想象成直接和磁盘打交道,注定是要进行大量的磁盘IO,MySQL与磁盘交互的最小单位是16KB!这个交互叫做Page

交互的过程

MySQL进行CURD的操作,首先要找到磁盘上对应扇区的数据,将扇区上的数据加载到内存中,根据冯诺依曼体系结构,真正和磁盘打交道的OS,只有先把数据加载到OS上,在通过OS提供给mysqld,才能进行相应的CURD;

mysql如果对数据修改,就需要从借助OS把数据拷贝到磁盘中。

mysql进行的这一个交换的单位就是page。

为了提高效率,会在mysqld客户端上new一块空间,这个空间就是缓冲区。OS交给mysql客户端的数据被放到缓冲区中。mysql需要刷新到磁盘的数据,会先存在缓冲区,在交给OS,最后由OS进行数据的刷新。

这一个mysql客户端打开就被创建的内存级缓冲区叫做buffer poll,如果数据提前被缓存在bufferpoll中就无需再次进行磁盘IO,没有再去磁盘中寻找。

所以Buffer Poll减少IO的次数,提高效率。

 为什么Page的大小是16字节?

 为什么MySQL与磁盘的交互是16字节的Page呢?而不是按需加载,需要多少就IO多少?
假设有一个员工表,有id和name组成。往表中插入5条数据 id为1~5。

  • 如果是按需IO:我们需要查询编号为1的员工,那么就会进行一次IO,如果在查询编号为2的员工,第二次IO,同理会至多进行五次IO。
  • 如果是利用Page进行IO,第一次查询编号为1的员工,会将1~5的员工全部加载到Buffer poll中,后续查询 id=1,2,3,4,5的员工只需在内存中查找。
  • 根据局部性原理,下一次访问的数据,大概率会在相邻的位置上。

推导Page的结构

单个Page 

磁盘中存在大量的文件,一个文件管理一个或者多个Page,Page是什么?
Page是mysql与磁盘交互的基本单位,所以对Page的管理无非就是先描述,在组织。

  • Page的描述,Prev与Next指针,负责找到前一个Page和后一个Page。
  • 指针指向数据的存储。

注意:如果数据设置了主键,那么在page的记录是按照主键的顺序存放的。

一个page的大小是16字节,通常来说,单个page会存在大量的数据,就比如我们查找1之后,想要查找5,只能逐个遍历。因此页目录的引入,提高了查找的效率。

页目录:将单个page的数据进行分组(大部分是1-8条,也有1-4条),目录是一个指针,指向最小编号的起始位置。

  • 通过引入页目录之后,如果我们想查询id=5的员工,只需要查询3次,即跳转3次页目录(1,2,3) 

虽然页目录也会占用一定的存储空间,但是它能够在消耗非常少空间的情况下,极大的提高数据的查询效率,数据的记录越多,目录查询的效率就越显著。

就像我们有一本书,我们想查找某个单元的内容,就会先找到目录,通过目录跳转页码。

管理多个Page 

 随着数据量增大,单个Page必然无法存储下数据,也就会存在大量的page。

对大量page的管理就是利用链表数据结构,组织起来,就是一个双向链表!

如果在大量的page中查找指定的主键值(key),凭借链表的查找,时间复杂度是O(N)。

查询的方法,先在第一个page内查询,在往后的page跳转。这是O(N)的空间复杂度。

为了能够优化page的查找,减少IO的次数,为page页也增加目录。

page页目录的序号是以最小存放的那一页最小的编号起始。

这一个结果有一个特点:页page不存储数据,只存储对应page的地址。普通页中即存储数据记录,也存储少量的目录。

如果数据量再大,为了提高搜索速度,会在加一层页目录

这一个结构的特点完全符合B+树的特点

  • 叶子存储数据
  • 非叶子节点保存叶子节点的指针
  • 查找的时候,是从上往下查找,只要拿着key和page的编号比较,就能跳转到叶子中。在叶子节点中,继续通过叶子跳转。

 在mysql中,如果设置是索引,mysql就会自动在磁盘构建以page以基本的B+树,每一种索引键就构建一棵树。实际上,上图画的就是InnoDB的索引结构。

OS进行IO操作,读取对应的叶子节点放到bufferPoll缓冲区中,并不意味着整颗树都会被加载进缓冲区,只是一部分。

选择B+树的原因

为什么只有B+树行?

换句话就是为什么mysql选择B+树?而不是链表、普通搜索树、哈希等?
要解决这个问题,主要围绕着优化算法+减少磁盘IO次数。

链表?不行:线性遍历,IO次数非常大。

普通二叉搜索树?极端情况下会退化为线性链表 ,高IO

AVL和红黑树 ?AVL和红黑树是绝对平衡的树,时间复杂度是O(logN)。但是AVL和红黑树都是二叉树,就会导致如果数据很多,高度就会很高,形成一颗瘦高的树。如果一颗瘦高的树,每一个高度都会进行IO,会进行高度次IO操作,也是不够优秀的。

B树?B树是多叉树,但是它的存储方式是非叶子节点保存数据,就会导致它的高度比B+树高,IO的次数多于B+树,和B+树比起来不够优秀。

Hash?官方的mysql是支持哈希做索引的结构,只不过InnoDB和MyISAM存储引擎并不支持;因为哈希会存在严重的哈希碰撞,导致空间浪费。实际上,哈希也不利于数据的范围查找。

所以mysql选择B+树,完全是因为B+符合磁盘的IO,是一颗绝对矮胖的树,IO次数充分的少。并且能够将范围内的数据同时获取到。


聚簇索引 VS 非聚簇索引

  • 数据记录和索引放在一起的数据结构叫做聚簇索引,常见的有Innodb
  • 数据记录和索引分离的数据结构叫做聚簇索引,常见的有MyISAM

MyISAM 存储引擎-主键(普通)索引

之前推导的B+树是Innodb存储引擎的主键索引,而MyISAM同样采用B+树的的基本结构。

但是不同的是,Innodb的叶子节点存储key主键,而MyISAM的叶子节点存储指向记录的地址

MyISAM最大的特点就是索引page和数据page分离,也就是叶子节点没有数据,只有地址。

而Innodb是索引和page放在一起的,叶子节点存储数据记录。

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

普通索引和主键索引没有本质的区别,无非就是普通索引是可以重复的。

InnoDB的普通索引叶子节点保存的是主键索引的key,通过普通索引找到key后,在通过主键索引找到叶子的数据记录,这一过程就叫做回表查询。

为什么不给普通索引的叶子节点也上数据呢?因为太浪费空间了。


索引的操作

创建索引

主键索引

  • 在创建表的时候,附带哪一列是主键;
create table if not exists t1 ( 
    id int primary key auto_increment,
     name varchar(20) not null);

直接指定以id为主键primary key建立索引

  • 在创建表的最后,指明哪一列是主键;
mysql> create table if not exists t2(
    -> id int ,
    -> name varchar(20),
    -> primary key(id));
  • 表被创建后,添加某一列为主键
create table t3 (
     id int , 
     name varchar(20) not null);
Query OK, 0 rows affected (0.02 sec)

mysql> alter table t3 add primary key (id);

注意:关于主键索引的注意点

  1. 主键索引不能重复,不能为空null。
  2. 主键索引的效率最高,搜索速度最快。
  3. 一张表只能有一个主键索引。

唯一键索引

  • 在创建表的时候,指定某一列为唯一键
create table t1( id int primary key, 
    name varchar(20) unique );

  • 在创建表的最后,指定为唯一键
mysql> create table t2(
    -> id int ,
    -> name varchar(20),
    -> primary key(id),
    -> unique(name));

  • 表被创建后,在指定唯一键
mysql> create table t3( 
    id int , 
    name varchar(20));
mysql> alter table t3 add unique (name);

唯一键索引的特点:

  1. 一个表中可以有多个唯一键索引,唯一键可以为null。
  2. 如果给唯一键索引设置了not null ,等价于主键索引。

普通键索引

本质上唯一键索引也是普通索引,它们唯一的区别就是是否重复。

普通键索引的创建方式和主键、唯一键类似。

  • 在创建表的最后,指定为唯一键
mysql> create table t1(
    -> id int primary key,
    -> name varchar(20) ,
    -> index(name));

  • 表创建后,添加普通键 
 alter table t2 add index(name);

  • 表创建后,通过为表创建普通索引 
mysql> create table t3( id int, name varchar(20));
//创建名为index_name 的普通索引
mysql> create index index_name on t3(name);

 


查看索引

常用的查看索引的方式有三种

主要是:

  • desc
  • show index from 
  • show keys from 

例如:desc + 表名

对应的key这一列就是索引的情况,普通索引:mul 。唯一键索引:unl 。主键索引:pri。

show index from +表名

 show index from index_table\G;

一种索引方式就会创建一个B+树,每个B+树都会以表的形式显示出来。

show keys form +表名

得到的结果和show index from 是一样的


删除索引

 索引的删除主要有三种

  • alter table from 表名 primary key
alter table index_table drop primary key;

  • 删除其它索引

 alter table +表名 drop index (index_name) :index_name 就是show index form 中的key_name

mysql> alter table index_table drop index(qq);

  • drop index  索引名 on 表名
mysql> drop index name on index_table;


全文索引 

全文索引是针对文章内容关键字的快速检索。

全文索引只支持MyISAM,而且只支持中文。

主要用到FULLTEXT关键字和match匹配。

创建测试表

CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (title,body)
)engine=MyISAM;

插入一堆数据

INSERT INTO articles (title,body) VALUES
('MySQL Tutorial','DBMS stands for DataBase ...'),
('MySQL vs. YourSQL','In the following database comparison ...'),
('MySQL Security','When configured properly, MySQL ...');

查询关键字database;

普通的方法就是模糊匹配 like %

select * from articles where body like '%database%';

这是没有用到索引的,在大量数据下效率不高。

通过fulltext索引查询database

SELECT * FROM articles
-> WHERE MATCH (title,body) AGAINST ('database');

 

 创建索引的原则

  1. 查找频繁的字段要创建索引
  2. 在where不会出现的字段不应该创建索引
  3. 唯一性太差的字段,不应该被设为索引
  4. 索引应该尽量以数字为主

标签:index,扇区,索引,IO,MySQL,磁盘,主键
From: https://blog.csdn.net/m0_73299809/article/details/140670906

相关文章

  • 在 FastAPI 中更改来自 MySQL 的数据类型输入
    我的这一行有“serialize_response”错误:@app.get("/get-sensors/",response_model=List[Data])和这个:return{"status":"success","list":data}我该如何解决这个问题!我想获取字典类型的数据为了解决在FastAPI中更改来自MySQL的数据类型输入时遇到的......
  • DBA笔记-第七部分(percona toolkit工具添加索引 )
    为处理altertable的情况DBA常使用perconatoolkit工具下载工具地址:wgethttps://www.percona.com/downloads/percona-toolkit/3.1.0/binary/tarball/percona-toolkit-3.1.0_x86_64.tar.gz解压后cd到bin目录或者添加环境变量vim/etc/profile环境变量最下行添加你自己......
  • Chrome 版本 127 需要选择默认搜索引擎
    Chrome更新到版本127后,我的所有Selenium脚本都会引发错误,因为在启动浏览器时我总是必须选择默认搜索引擎。我使用ChromeDriver127.0.6533.72。有人遇到同样的问题吗?是的,Chrome127及其对应的ChromeDriver版本在首次启动时引入了选择默认搜索引擎的提示,这可......
  • MySQL索引详解full-text,b-tree,hash,r-tree
    一、MySQL索引类型mysql里目前只支持4种索引分别是:full-text,b-tree,hash,r-treeb-tree索引应该是mysql里最广泛的索引的了,除了archive基本所有的存储引擎都支持它.1.full-text索引full-text在mysql里仅有myisam支持它,而且支持full-text的字段只有char、varchar、text数据类型......
  • 在Pandas中 SQL操作:SQLAlchemy和PyMySQL的区别
    SQLAlchemy和PyMySQL的区别1.SQLAlchemy和PyMySQL简介SQLAlchemy是Python编程语言下的一款开源软件。它提供了SQL工具包和对象关系映射器(ORM)来进行数据库操作。SQLAlchemy可以与多种数据库系统进行交互,包括MySQL、PostgreSQL、SQLite等。PyMySQL是Python编程语言下的一个纯Pyt......
  • Python Pandas 使用 .loc 跨列级别多重索引
    我对python和pandas仍然很陌生,想知道是否有更好的方法来解决我遇到的索引问题。因为我看到人们在这个网站上做了非常巧妙的事情,超出了我通常可以从文档中收集到的内容,所以我想我会问——特别是因为我还在学习。我有一个包含多个列的DataFrame级别,级别0是“meta”和“r......
  • mysqldump: Got error: 1066: Not unique table/alias: 'act_evt_log' when using LOC
    先说解决办法:执行下面语句mysqldump-ushooter-p123123--single-transactionfd>fd.sql  lower_case_table_names区分大小写设置注意:此参数不可以动态修改,必须重启数据库 12341、参数含义:lower_case_table_names=1  表名存储在磁盘是小写的,但是比......
  • MySQL大框架总结
    1.DDL,DML,DQL,DCL的区别(由于DCL是关乎用户的,以下内容重点讲述数据库,表与数据的操作,所以对DCL不详细赘述)DDLDMLDQLDCL中文/英文数据库定义语言datadefinitionlanguage数据库操作语言datamanipulationlanguage数据库查询语言dataquerylanguage数据......
  • 索引结构—B+Tree索引、Hash索引、Full-Text(全文)索引、R-Tree(空间)索引
    一、概述在数据库系统中,索引是一种用于加快数据检索的数据结构。不同的索引结构适用于不同的查询场景和数据特性。索引按照不同角度可以划分不同类型的索引。按照数据结构可以划分B+Tree索引、Hash索引、FULLTEXT(全文)索引、R-Tree(空间)索引二、索引结构mysql的索引是作用于......
  • tpcc压力测试mysql和 ab压力测试云服务器
     mysql性能测试工具——tpcc-mysql在centos7.9上安装的下载源码包,解压安装#tarxftpcc-mysql-src.tar#cdtpcc-mysql/src#yum installgcc mysql-devel -y#make会生成两个二进制工具tpcc_load(提供初始化数据的功能)和tpcc_start(进行压力测试)[root@nfs-......