建索引的原则-以innodb为例

本文介绍了MySQL中InnoDB存储引擎的索引原理及其优化方法,包括B+Tree索引类型、建立索引的最佳实践和具体案例分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、写在前面      

随着开发、测试任务进入尾声,大家都在整理一些项目发布前的一些准备工作,其中一个重要的工作就是为之前写的一些sql语句建立索引,这高并发、高访问量的环境下是非常有必要的,建立一个好的索引能够极大地提高sql语句的查询效率,那么问题来了,到底什么是索引,怎样才能建立一个好的索引呢?本文以mysql Innodb存储引擎为例,结合实际的项目来看一下,如何建立一个好的而索引。

 

二、索引定义

MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。提取句子主干,就可以得到索引的本质:索引是数据结构。

我们知道,数据库查询是数据库的最主要功能之一,例如下面的SQL语句:

SELECT * FROMtest_table WHERE id = 99 ;可以从表test_table中获得id为99的数据记录。

我们都希望查询数据的速度能尽可能的快,因此数据库系统的设计者会从查询算法的角度进行优化。最基本的查询算法当然是顺序查找(linear search),遍历test_table然后逐行匹配id的值是否是99,这种复杂度为O(n)的算法在数据量很大时显然是糟糕的,好在计算机科学的发展提供了很多更优秀的查找算法,例如二分查找(binary search)、二叉树查找(binary tree search)等。如果稍微分析一下会发现,每种查找算法都只能应用于特定的数据结构之上,例如二分查找要求被检索数据有序,而二叉树查找只能应用于二叉查找树上,但是数据本身的组织结构不可能完全满足各种数据结构(例如,理论上不可能同时将两列都按顺序进行组织),所以,在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。

举上面的例子主要是为了简单说明地说明索引的作用,包括mysql Innodb在内的大部分数据库系统及文件系统并没有选择二叉树结构作为索引,而是采用了B-Tree或其变种B+Tree作为索引结构,这种索引结构可以最大限度地减少查找过程中磁盘I/O的存取次数,关于什么是B-Tree或B+Tree以及选择它们做数据库索引结构的原因,大家可以自行去学习。下面我们首先介绍下mysql Innodb引擎的两种B+Tree索引。

 

三、MysqlInnodb B+Tree索引

1.一种是主键索引,主键索引即聚集索引(Cluster Index),它不仅有主键,而且有主键所属的全部数据,所以在Innodb中,主键索引即数据;


2.一种是列值为Key,主键位置为Value即 (列值, 主键位置) 的非主键索引(SecondaryIndex)

 

Innodb属于索引组织表,所有的数据全部挂在主键叶子节点下。所以如果不能保证主键的插入顺序,那么会发生大量的主键节点分裂,产生大量的I/O操作。另外Innodb规定单个索引字段的长度不得超过768字节,否则截断超出长度不放入索引。

Innodb的非主键索引全部都指向主键索引,查找非主键索引无法获得整行数据,需要通过叶子节点的指针查到其主键索引的位置才能获得整行数据,所以主键索引必须设计得尽可能小,否则非主键索引将会非常的大。

 

四、建立索引的原则:

下面我们看一下建立一个好的索引需要遵循的原则,并结合具体的例子来做说明;

1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

2.=和in可以乱序,比如a = 1and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式。

3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录

4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ‘2015-08-14’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(‘2015-08-14’)。

5.尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。

6.在order by或者group by子句中,如果想通过索引来进行排序,所建索引列的顺序必须与order by或者group by子句的顺序一致,并且所有列的排序方向(倒序或者正序)都一样;如果查询关联多张表,则只有order by子句引用的字段全部来自第一张表时,才能利用索引来排序;order by或者group by语句与查询型语句的限制是一样的:需要满足索引的最左前缀原则;否则mysql就要执行排序操作,无法利用索引来排序;(有一种情况order by或者group by子句可以不满足最左前缀原则,就是其前导为常量的时候,如果where或者join对这些列指定了常量,就可以弥补索引的不足)。

 

五、举例

语句1:

select * from test_table where column1 = "XXX" and column2 = "YYY"  and status = "VALID";

语句2:

select * from test_table where column2 = "YYY" group by column1;

对于这两条语句,如果单独进行考虑的话,大家可能会建立两个索引,针对语句1建立(column1,colum2,status),针对语句2建立(column2,column1);如果综合考虑来看的话,其实一个索引就够了,即(column2,column1),这里没必要将status字段放到索引中,因为这个字段的区分度太差;

根据建立索引的原则2,语句1是可以走到这个索引的;

根据建立索引的原则1,语句2也是可以走到这个索引的;

索引不是越多越好,建立过多的索引会增加数据库内存或者磁盘的消耗,并且会影响到得插入、删除等操作的性能,索引在建立索引时要遵循索引建立的原则,通盘考虑;

<think>我们参考用户提供的引用内容来回答关于主键和索引的问题。根据引用[1]:主键在MySQL中会自动创一个唯一索引,不需要手动添加。根据引用[2]:在创表时,如果指定了主键,则自动创主键索引;也可以在创表后通过ALTERTABLE语句添加主键索引。根据引用[3]:主键是唯一的,不允许重复和空值;一个表只能有一个主键,但可以有多个索引(包括唯一索引、非唯一索引等)。同时,引用[3]也解释了聚集索引和非聚集索引的区别。根据引用[4]:非聚集索引的叶节点不包含数据,而是指向数据页的指针。在创非聚集索引时,需要注意如果表还没有聚集索引,则非聚集索引会使用堆结构,如果之后创了聚集索引,非聚集索引会被重。用户的问题有两个部分:1.主键是否自动创索引->是,主键会自动创唯一索引(在MySQL中,主键默认是聚集索引,除非指定为非聚集索引,但通常InnoDB表的主键是聚集索引)。2.数据库索引设置方法?->包括创表时设置主键(自动创索引)、创表后添加主键索引、以及创普通索引(唯一索引、非唯一索引)的方法。因此,我们将按照以下结构回答:-主键索引的自动创-索引的设置方法(包括主键索引和其他索引)注意:不同数据库系统实现可能不同,这里以MySQL(特别是InnoDB引擎)为。</think>### 主键与索引的关系及设置方法 #### 1. 主键是否自动创索引? **是的,主键会自动创索引**。在主流数据库系统(如 MySQL)中: - 当定义主键时,数据库会自动创**唯一聚集索引**(InnoDB 引擎)[^1][^3] - 主键索引确保: - 数据的唯一性(不允许重复值) - 数据的物理存储顺序(聚集索引- 快速检索能力 - 如创表时声明主键: ```sql CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, -- 自动创索引 name VARCHAR(50) ); ``` #### 2. 索引的设置方法 ##### (1) 主键索引设置 - **创表时指定**: ```sql CREATE TABLE flights ( flight_id INT PRIMARY KEY, -- 自动创主键索引 flight_number VARCHAR(10) ); ``` - **创表后添加**: ```sql ALTER TABLE flights ADD PRIMARY KEY (flight_id); -- 添加主键并自动创索引[^2] ``` ##### (2) 普通索引设置 | 索引类型 | 创方法 | 特点 | |----------------|--------------------------------------------------------------------------|----------------------------------------------------------------------| | **唯一索引** | `CREATE UNIQUE INDEX idx_name ON table(column);` | 列值必须唯一,允许空值 | | **非聚集索引** | `CREATE INDEX idx_departure ON flights(departure_city);` | 叶节点存储数据指针,不影响物理存储顺序[^4] | | **复合索引** | `CREATE INDEX idx_cities ON flights(departure_city, arrival_city);` | 多列组合索引,遵循最左匹配原则 | | **全文索引** | `CREATE FULLTEXT INDEX idx_description ON articles(content);` | 专用于文本搜索 | ##### (3) 索引管理操作 ```sql -- 查看索引 SHOW INDEX FROM flights; -- 删除索引 DROP INDEX idx_name ON flights; -- 修改索引 (需先删除再重) ALTER TABLE flights DROP INDEX idx_old; CREATE INDEX idx_new ON flights(new_column); ``` #### 3. 索引设计最佳实践 1. **选择合适列**: - 高频查询条件列(如 `WHERE flight_number='CA123'`) - 连接操作的外键列(如 `JOIN ON flight_id`) - 排序/分组列(如 `ORDER BY departure_time`) 2. **避免过度索引**: - 每增加一个索引会降低写操作性能 - 更新频繁的列不宜索引 3. **特殊场景优化**: ```sql -- 覆盖索引索引包含所有查询字段 CREATE INDEX idx_covering ON flights(flight_number, departure_time, price); -- 前缀索引:对长文本列前N字符索引 CREATE INDEX idx_name_prefix ON passengers(name(10)); ``` > **重要提示**:在 MySQL 的 InnoDB 引擎中,主键索引即聚集索引,决定了数据的物理存储顺序。若未显式定义主键,系统会使用第一个唯一非空索引替代,若无则创隐式行 ID 作为聚集索引[^3][^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值