MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

MySQL中创建索引的语句与最佳实践

2022-12-116.9k 阅读

MySQL索引基础

在深入探讨MySQL中创建索引的语句与最佳实践之前,我们先来了解一下索引的基本概念。索引就像是一本书的目录,通过它可以快速定位到所需的数据。在MySQL中,索引是一种数据结构,它可以显著提高查询效率。当我们执行一条SQL查询语句时,如果相关列上有合适的索引,MySQL就可以避免全表扫描,从而快速定位到符合条件的数据行。

索引的类型

  1. 普通索引:这是最基本的索引类型,它没有任何限制。普通索引可以加速对数据的访问,适用于大多数查询场景。例如,在一个用户表中,我们可能会在username列上创建普通索引,以便快速查找特定用户名的用户记录。
  2. 唯一索引:唯一索引要求索引列的值必须唯一。这不仅可以提高查询效率,还能保证数据的完整性。比如,在用户表的email列上创建唯一索引,确保每个用户的邮箱地址是唯一的。
  3. 主键索引:主键索引是一种特殊的唯一索引,它的索引列值不仅必须唯一,而且不能为NULL。每个表只能有一个主键索引,它常用于标识表中的每一行数据。例如,在订单表中,order_id通常会被设置为主键索引。
  4. 全文索引:全文索引主要用于文本类型的列,如TEXTLONGTEXT。它可以进行更复杂的文本搜索,支持模糊匹配和自然语言搜索。例如,在一篇文章的内容列上创建全文索引,可以快速搜索包含特定关键词的文章。

创建索引的语句

创建普通索引

在MySQL中,我们可以使用CREATE INDEX语句来创建普通索引。语法如下:

CREATE INDEX index_name ON table_name (column_name);

例如,我们有一个employees表,包含first_namelast_namesalary等列。如果我们想在first_name列上创建普通索引,可以执行以下语句:

CREATE INDEX idx_first_name ON employees (first_name);

如果我们想在多个列上创建复合索引,可以在括号内列出多个列名,如下所示:

CREATE INDEX idx_name_salary ON employees (first_name, last_name, salary);

复合索引在查询时,如果查询条件包含复合索引中的列,并且顺序与索引列顺序一致,会大大提高查询效率。

创建唯一索引

创建唯一索引的语法与普通索引类似,只需将CREATE INDEX替换为CREATE UNIQUE INDEX。语法如下:

CREATE UNIQUE INDEX index_name ON table_name (column_name);

例如,在users表的phone_number列上创建唯一索引:

CREATE UNIQUE INDEX idx_phone_number ON users (phone_number);

这样就确保了phone_number列中的值是唯一的,同时也能加快对该列的查询速度。

创建主键索引

主键索引在创建表时通常就会一并定义。语法如下:

CREATE TABLE table_name (
    column1 datatype PRIMARY KEY,
    column2 datatype,
    ...
);

例如,创建一个products表,以product_id作为主键:

CREATE TABLE products (
    product_id INT AUTO_INCREMENT PRIMARY KEY,
    product_name VARCHAR(255),
    price DECIMAL(10, 2)
);

如果表已经创建好了,也可以使用ALTER TABLE语句来添加主键索引:

ALTER TABLE table_name ADD PRIMARY KEY (column_name);

比如,对于已经存在的orders表,我们想将order_id设置为主键:

ALTER TABLE orders ADD PRIMARY KEY (order_id);

创建全文索引

创建全文索引需要使用FULLTEXT关键字。语法如下:

CREATE FULLTEXT INDEX index_name ON table_name (column_name);

例如,在articles表的content列上创建全文索引:

CREATE FULLTEXT INDEX idx_content ON articles (content);

需要注意的是,全文索引只能用于InnoDBMyISAM存储引擎,并且只能在CHARVARCHARTEXT类型的列上创建。

索引创建的最佳实践

索引的选择

  1. 频繁查询的列:在经常出现在WHERE子句中的列上创建索引。例如,在一个订单表中,如果经常根据customer_id查询订单,那么在customer_id列上创建索引会显著提高查询效率。
-- 假设orders表结构如下
CREATE TABLE orders (
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    customer_id INT,
    order_date DATE,
    total_amount DECIMAL(10, 2)
);
-- 在customer_id列上创建索引
CREATE INDEX idx_customer_id ON orders (customer_id);
  1. 连接条件的列:当进行表连接操作时,连接条件中的列应该创建索引。比如,在orders表和customers表通过customer_id进行连接时,customers表的customer_id列和orders表的customer_id列都应该创建索引。
-- customers表结构
CREATE TABLE customers (
    customer_id INT AUTO_INCREMENT PRIMARY KEY,
    customer_name VARCHAR(255),
    contact_number VARCHAR(20)
);
-- 在customers表的customer_id列创建索引(虽然已经是主键索引,但为了说明连接条件索引的重要性)
CREATE INDEX idx_customers_customer_id ON customers (customer_id);
-- 前面已经在orders表的customer_id列创建了索引
  1. 排序的列:如果查询结果需要按某个列进行排序,在该列上创建索引可以提高排序效率。例如,在查询员工信息并按salary降序排列时,在salary列上创建索引会有帮助。
-- employees表结构
CREATE TABLE employees (
    employee_id INT AUTO_INCREMENT PRIMARY KEY,
    first_name VARCHAR(255),
    last_name VARCHAR(255),
    salary DECIMAL(10, 2)
);
-- 在salary列上创建索引
CREATE INDEX idx_salary ON employees (salary);

避免过度索引

虽然索引可以提高查询效率,但并不是索引越多越好。每个索引都会占用额外的存储空间,并且在插入、更新和删除数据时,MySQL需要同时更新相关的索引,这会增加操作的开销。因此,要避免在很少使用的列上创建索引。

例如,在一个用户表中,有一个registration_ip列,只有在统计特定IP注册的用户数量时才会用到,而且这种查询很少执行。在这种情况下,就不应该在registration_ip列上创建索引。

复合索引的使用

  1. 最左前缀原则:复合索引遵循最左前缀原则。也就是说,在使用复合索引时,查询条件必须从复合索引的最左边的列开始。例如,我们有一个复合索引idx_name_salaryfirst_name, last_name, salary),以下查询会使用到该索引:
SELECT * FROM employees WHERE first_name = 'John';
SELECT * FROM employees WHERE first_name = 'John' AND last_name = 'Doe';
SELECT * FROM employees WHERE first_name = 'John' AND last_name = 'Doe' AND salary > 50000;

而以下查询不会使用到该复合索引:

SELECT * FROM employees WHERE last_name = 'Doe';
SELECT * FROM employees WHERE salary > 50000;
  1. 选择性高的列在前:在创建复合索引时,应该将选择性高的列放在前面。选择性是指某列不同值的数量与总行数的比例,比例越高,选择性越高。例如,在employees表中,employee_id的选择性比department高,因为employee_id是唯一的,而department可能有重复值。所以在创建复合索引时,应该将employee_id放在前面。

索引的维护

  1. 定期重建索引:随着数据的不断插入、更新和删除,索引可能会变得碎片化,导致性能下降。定期重建索引可以优化索引结构,提高查询效率。在MySQL中,可以使用ALTER TABLE语句来重建索引。例如,对于orders表:
ALTER TABLE orders DROP INDEX idx_customer_id;
CREATE INDEX idx_customer_id ON orders (customer_id);
  1. 分析表:使用ANALYZE TABLE语句可以更新表的统计信息,使MySQL的查询优化器能够做出更准确的执行计划。例如:
ANALYZE TABLE employees;

索引对查询性能的影响示例

为了更直观地了解索引对查询性能的影响,我们通过实际的示例来进行说明。假设我们有一个products表,包含大量的数据。

-- 创建products表
CREATE TABLE products (
    product_id INT AUTO_INCREMENT PRIMARY KEY,
    product_name VARCHAR(255),
    category VARCHAR(100),
    price DECIMAL(10, 2),
    description TEXT
);
-- 插入大量测试数据(这里省略具体的插入语句)
  1. 无索引查询:首先,我们执行一个没有索引的查询,比如查找价格大于100的产品。
SELECT * FROM products WHERE price > 100;

在没有索引的情况下,MySQL需要全表扫描,随着数据量的增加,查询时间会显著增长。 2. 有索引查询:接下来,我们在price列上创建索引,然后再次执行相同的查询。

CREATE INDEX idx_price ON products (price);
SELECT * FROM products WHERE price > 100;

这次,由于有了索引,MySQL可以直接定位到符合条件的数据行,查询时间会大大缩短。

不同存储引擎下索引的特点

  1. InnoDB存储引擎
    • 聚簇索引:InnoDB的主键索引是聚簇索引,数据行存储在主键索引的叶子节点上。这意味着通过主键查询数据非常快,因为可以直接定位到数据行。例如,在users表中,如果user_id是主键,那么通过SELECT * FROM users WHERE user_id = 1查询时,InnoDB可以快速找到对应的用户记录。
    • 辅助索引:除主键索引外的其他索引称为辅助索引。辅助索引的叶子节点存储的是主键值,而不是数据行的物理地址。当通过辅助索引查询时,首先通过辅助索引找到主键值,然后再通过主键索引找到数据行。例如,在users表的email列上创建辅助索引,当执行SELECT * FROM users WHERE email = 'test@example.com'时,先通过email索引找到对应的主键值,再通过主键索引找到完整的用户记录。
  2. MyISAM存储引擎
    • 索引与数据分离:MyISAM的索引和数据是分开存储的。索引文件存储索引信息,数据文件存储实际的数据。例如,在products表中,product_id的索引存储在索引文件中,而产品的详细信息存储在数据文件中。
    • 不支持聚簇索引:MyISAM没有聚簇索引的概念,所有索引都是类似的结构,通过索引找到数据行的物理地址,然后从数据文件中读取数据。这使得在某些情况下,MyISAM的查询性能与InnoDB有所不同,特别是在主键查询方面,InnoDB通常会更快。

索引创建时的注意事项

  1. 数据类型匹配:在创建索引时,确保索引列的数据类型与查询条件中的数据类型一致。例如,如果索引列是INT类型,在查询时传入的参数也应该是INT类型。否则,MySQL可能无法使用索引。
  2. 索引长度:对于字符串类型的列,创建索引时可以指定索引长度。例如,在VARCHAR列上创建索引时,可以这样写:
CREATE INDEX idx_product_name ON products (product_name(50));

指定合适的索引长度可以减少索引占用的空间,同时也能保证一定的查询效率。一般来说,选择能够区分大部分数据的最短长度即可。 3. 事务与索引创建:在事务中创建索引时要谨慎。因为创建索引可能会占用大量资源,如果事务回滚,可能会导致不必要的性能开销。尽量在非事务环境中创建索引,或者确保事务的执行时间足够长,以避免因为事务超时等问题导致索引创建失败。

通过深入理解MySQL中创建索引的语句和最佳实践,我们可以有效地优化数据库查询性能,提高应用程序的响应速度。在实际应用中,需要根据具体的业务需求和数据特点,合理地创建和维护索引,以达到最佳的性能效果。同时,要不断关注MySQL版本的更新,因为新的版本可能会对索引的性能和功能进行优化和改进。