MySQL索引(index)专题

本文介绍了MySQL索引的基础知识,包括索引的优缺点、不同类型如B-TREE、HASH、FULLTEXT、RTREE的特性,以及如何创建和管理索引。通过索引,可以显著提高数据检索速度、确保数据唯一性,但也可能导致额外的维护时间和空间占用。全文索引适用于文本模糊查询,而外键用于保持数据一致性。

1 MySQL索引简介

    索引(Index)是帮助MySQL高效获取数据的数据结构,它的存在形式是文件。索引能够帮助我们快速定位数据,它可以让mysql高效运行,大大提高mysql的查询(包括排序,分组)效率。
    Mysql目前主要有以下几种索引类型(按存储结构划分):FULLTEXT,HASH,BTREE,RTREE。
    对应存储引擎支持如下:

MyISAMBTREE,FULLTEXT,RTREE
InnodbBTREE,RTREE
MemoryHASH,BTREE
NDBBTREE,HASH,RTREE

    注:前面索引为存储引擎默认索引。
    MySQL索引在使用上一般有如下几种种类(按使用功能划分)
    普通索引(INDEX),唯一索引(UNIQUE INDEX),主键索引(PRIMARY KEY),全文索引(FULLTEXT INDEX),外键索引(FOREIGN KEY),组合索引。

2 索引的优缺点

    2.1 索引的优点

    大大加快数据的检索速度,这也是创建索引的最主要的原因。
    创建唯一性索引,保证数据库表中每一行数据的唯一性。
    加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
    在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
    通过使用索引,可以在查询的过程中使用优化隐藏器。

    2.2 索引的缺点

    创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
    当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度。
    建立索引会占用磁盘空间的索引文件,尤其大表上创建了多种组合索引,索引文件的会膨胀很快。

3 索引创建

    3.1 建表时创建索引

    3.1.1 创建普通索引

CREATE TABLE index_normal (
    c1 int default NULL,
    c2 varchar(30) default NULL,
    c3 date default NULL,
    INDEX normal (c3)
) engine=innodb;
    查看表结构

mysql> show create table index_normal \G
*************************** 1. row ***************************
       Table: index_normal
Create Table: CREATE TABLE `index_normal` (
  `c1` int(11) DEFAULT NULL,
  `c2` varchar(30) COLLATE utf8_bin DEFAULT NULL,
  `c3` date DEFAULT NULL,
  KEY `normal` (`c3`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)
     注:若不显示指定normal为关键字,key会指定c3为关键字。

    插入语句并查看执行计划

mysql> insert into index_normal values (1,'testing partitions','1995-07-17');
mysql> explain select * from index_normal where c3 = 1995-07-17 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: index_normal
         type: ref
possible_keys: normal
          key: normal
      key_len: 4
          ref: const
         rows: 1
        Extra: Using where
    3.1.2 创建唯一索引

    一个表中可以有多个唯一性索引,唯一性索引列允许空值。

CREATE TABLE index_unique (
    c1 int default NULL,
    c2 varchar(30) default NULL,
    c3 date default NULL,
    UNIQUE INDEX uniqueIDX (c1)
) engine=innodb;

    查看表结构

mysql> show create table index_unique \G
*************************** 1. row ***************************
       Table: index_unique
Create Table: CREATE TABLE `index_unique` (
  `c1` int(11) DEFAULT NULL,
  `c2` varchar(30) COLLATE utf8_bin DEFAULT NULL,
  `c3` date DEFAULT NULL,
  UNIQUE KEY `uniqueIDX` (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)
    3.1.3 创建主键索引

    一个表中只能有一个主键索引,主键索引列不允许空值。因为一个表只可能有一个PRIMARY KEY索引,因此不需要指定索引名。

CREATE TABLE index_primary (
    c1 int NOT NULL auto_increment,
    c2 varchar(30) default NULL,
    c3 date default NULL,
    primary key (c1)
) engine=innodb auto_increment=10;
    可以设定主键自增长auto_increment,及自增长开始点auto_increment=10
    查看表结构

mysql> show create table index_primary \G
*************************** 1. row ***************************
       Table: index_primary
Create Table: CREATE TABLE `index_primary` (
  `c1` int(11) NOT NULL AUTO_INCREMENT,
  `c2` varchar(30) COLLATE utf8_bin DEFAULT NULL,
  `c3` date DEFAULT NULL,
  PRIMARY KEY (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)

    3.1.4 创建全文索引

    MySQL只有MyISAM存储引擎支持FULLTEXT索引,并且类类型为CHAR、TEXT、VARCHAR。

CREATE TABLE index_fulltext (
    c1 int default NULL,
    c2 varchar(300) default NULL,
    c3 date default NULL,
    FULLTEXT INDEX fulltextIDX (c2)
) engine=myisam;
    查看表结构

mysql> show CREATE TABLE index_fulltext \G
*************************** 1. row ***************************
       Table: index_fulltext
Create Table: CREATE TABLE `index_fulltext` (
  `c1` int(11) DEFAULT NULL,
  `c2` varchar(300) COLLATE utf8_bin DEFAULT NULL,
  `c3` date DEFAULT NULL,
  FULLTEXT KEY `fulltextIDX` (`c2`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin

     3.1.5 创建外键索引

    保持数据一致性,完整性,主要目的是控制存储在外键表中的数据。 使两张表形成关联,外键只能引用外表中的列的值!

    外键语法规则

[CONSTRAINT 外键名] FOREIGN KEY [id] (index_col_name, ...)
    REFERENCES tbl_name (index_col_name, ...)
    [ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}]
    [ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}]
    该语法可以在 CREATE TABLE 和 ALTER TABLE 时使用,如果不指定CONSTRAINT symbol,MYSQL会自动生成一个名字。
    ON DELETE、ON UPDATE表示事件触发限制,可设参数:
    ① RESTRICT(限制外表中的外键改动,默认值)
    ② CASCADE(跟随外键改动)
    ③ SET NULL(设空值)
    ④ SET DEFAULT(设默认值)
    ⑤ NO ACTION(无动作,默认的)

    创建外键实例

CREATE TABLE index_foreign(
id int(11),
name varchar(20),
CONSTRAINT foreignKEY foreign key(id) references index_primary(c1) 
on delete cascade on update cascade) engine=innodb;<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span>

    查看表结构

mysql> show create table index_foreign \G
*************************** 1. row ***************************
       Table: index_foreign
Create Table: CREATE TABLE `index_foreign` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(20) COLLATE utf8_bin DEFAULT NULL,
  KEY `foreignKEY` (`id`),
  CONSTRAINT `index_foreign_ibfk_1` FOREIGN KEY (`id`) REFERENCES `index_primary` (`c1`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

    注:有外键约束的表必须是innodb型,因为在MySQL中只有InnoDB类型的表才支持外键。

            不能删除有外键约束的父表,否则会报错:ERROR 1217 (23000): Cannot delete or update a parent row: a foreign key constraint fails

            MySQL只允许向当前表中加入外键表(参照表)中已有的数据列。

            在外键关系中,字段的数据类型必须相似。

            外键表中数据删除,当前表中相关数据也会被级联删除,反之则不会。

    3.2  已存在的表上通过ALTER TABLE创建索引

    3.2.1 基本语法

ALTER TABLE table_name ADD INDEX index_name (column_name);
ALTER TABLE table_name ADD UNIQUE index_name (column_name);
ALTER TABLE table_name ADD PRIMARY KEY (column_name);
ALTER TABLE table_name ADD FULLTEXT index_name (column_name);

ALTER TABLE table_name ADD CONSTRAINT foreignKEY FOREIGN KEY (index_column_name) 
REFERENCES foreign_table_name (index_column_name) ON DELETE CASCADE ON UPDATE CASCADE ;

    3.2.2 创建测试表

CREATE TABLE index_alter_normal (
    c1 int default NULL,
    c2 varchar(30) default NULL,
    c3 date default NULL
) engine=innodb;
CREATE TABLE index_alter_unique (
    c1 int default NULL,
    c2 varchar(30) default NULL,
    c3 date default NULL
) engine=innodb;
CREATE TABLE index_alter_primary (
    c1 int NOT NULL,
    c2 varchar(30) default NULL,
    c3 date default NULL
) engine=innodb;
CREATE TABLE index_alter_fulltext (
    c1 int default NULL,
    c2 varchar(300) default NULL,
    c3 date default NULL
) engine=myisam;
CREATE TABLE index_foreign(
id int(11),
name varchar(20) 
)engine=innodb;

    3.2.3 添加索引

ALTER TABLE index_alter_normal ADD INDEX normal (c3);
ALTER TABLE index_alter_unique ADD UNIQUE uniqueIDX (c1);
ALTER TABLE index_alter_primary ADD PRIMARY KEY (c1);
ALTER TABLE index_alter_fulltext ADD FULLTEXT fulltextIDX (c2);

ALTER TABLE index_foreign ADD CONSTRAINT foreignKEY FOREIGN KEY (id) 
REFERENCES index_primary (c1) ON DELETE CASCADE ON UPDATE CASCADE ;

    注:在已存在的表上添加主键只能通过alter table的方法,不能直接create primary key

    若添加主键为自增主键,先增加主键,在添加自增长,最后添加增长起始点

    添加主键为自增长,并设置从10开始自增:

mysql> Alter table index_alter_primary change c1 c1 int(11) not null auto_increment;
mysql> Alter table index_alter_primary  auto_increment=10;

    3.3  已存在的表上通过CREATE INDEX创建索引

    主键及外键的添加不能通过此方法,只能通过上面alter table的方法。

    3.3.1 基本语法

CREATE INDEX index_name ON table_name (column_name);
CREATE UNIQUE INDEX index_name ON table_name (column_name);
CREATE FULLTEXT INDEX index_name ON table_name (column_name);

    3.3.2 创建测试表

CREATE TABLE index_create_normal (
    c1 int default NULL,
    c2 varchar(30) default NULL,
    c3 date default NULL
) engine=innodb;
CREATE TABLE index_create_unique (
    c1 int default NULL,
    c2 varchar(30) default NULL,
    c3 date default NULL
) engine=innodb;
CREATE TABLE index_create_fulltext (
    c1 int default NULL,
    c2 varchar(300) default NULL,
    c3 date default NULL
) engine=myisam;

    3.3.3 添加索引

CREATE INDEX normal ON index_create_normal (c3);
CREATE UNIQUE INDEX uniqueIDX ON index_create_unique (c1);
CREATE FULLTEXT INDEX fulltextIDX ON index_create_fulltext (c2);

4 索引查看

mysql> show index from table_name;
mysql> show key from table_name;
    例如:

mysql> show index from index_normal \G
*************************** 1. row ***************************
        Table: index_normal
   Non_unique: 1
     Key_name: normal
 Seq_in_index: 1
  Column_name: c3
    Collation: A
  Cardinality: 2
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment: 
Index_comment: 
1 row in set (0.00 sec)

mysql> show index from index_fulltext \G
*************************** 1. row ***************************
        Table: index_fulltext
   Non_unique: 1
     Key_name: fulltextIDX
 Seq_in_index: 1
  Column_name: c2
    Collation: NULL
  Cardinality: NULL
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: FULLTEXT
      Comment: 
Index_comment: 
1 row in set (0.00 sec)
    注:其中普通索引,唯一索引,主键索引类型为btree,全文索引类型为fulltext
5 删除索引

    删除语法:

drop index index_name on table_name ;
alter table table_name drop index index_name ;
alter table table_name drop primary key ;
    注:在前面的两条语句中,都删除了table_name中的索引index_name。而在最后一条语句中,只在删除PRIMARY KEY索引中使用,因为一个表只可能有一个PRIMARY KEY索引,因此不需要指定索引名。 如果主键为自增长,删除主键前需要先改变自增长为普通字段。

    例如:

mysql> drop index uniqueIDX on index_unique;
mysql> alter table index_normal drop index ;
mysql> alter table index_fulltext drop index fulltextIDX;

mysql> alter table index_primary change c1 c1 int(11);//删除自增长
mysql> alter table index_primary drop PRIMARY KEY;//删除主键
mysql> alter table index_foreign drop FOREIGN KEY index_foreign_ibfk_1;//删除外键索引
    注:删除外键语法如下:
    ALTER TABLE table-name DROP FOREIGN KEY key-id;
    其中:key-id可通过show create table查看,紧跟在CONSTRAINT之后的那个参数就是。

6 几种索引对比

    6.1 索引类型对比(按存储结构划分)

    6.1.1 B-TREE索引

    B-Tree索引加速了数据访问。从B-Tree根开始,借助中间节点页的上界和下界值,可以快速搜寻到叶子页层,最终找到含有需要找的值的叶子页(或者确定无法找到需要的数据),找到对应的叶子页后可以通过相应的指针直接找到数据表中对应的数据行。这样存储引擎不会扫描整个表得到需要的数据。同时B-Tree索引通常意味着索引中数据保存时有序的,可以利用B-Tree索引来加速排序。

    6.1.2 HASH索引

    哈希索引建立在哈希表的基础上,它只对使用了索引的每一列的精确查找有用。对于每一行,存储引擎计算出了被索引列的哈希码,它是一个较小的值(可能对具有相同索引值的不同行计算出的哈希值不同)。索引中包含哈希码和对应指向数据行的指针。 

    hash索引由于其结构,所以在每次查询的时候直接一次到位,不像b-tree那样一点点的前进。所以hash索引的效率高于b-tree。hash表在处理较小数据量时具有无可比拟的素的优势,所以hash索引很适合做缓存(内存数据库)。

    6.1.3 FULLTEXT索引

    FULLTEXT即为全文索引,目前只有MyISAM引擎支持。其可以在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不过目前只有 CHAR、VARCHAR ,TEXT 列上可以创建全文索引。值得一提的是,在数据量较大时候,现将数据放入一个没有全局索引的表中,然后再用CREATE INDEX创建FULLTEXT索引,要比先为一张表建立FULLTEXT然后再将数据写入的速度快很多。
    它的出现是为了解决WHERE name LIKE “%word%"这类针对文本的模糊查询效率较低的问题。在没有全文索引之前,这样一个查询语句是要进行遍历数据表操作的,可见,在数据量较大时是极其的耗时的。

    6.1.4 R-TREE索引

    RTREE在mysql很少使用,仅支持geometry数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种。
    相对于BTREE,RTREE的优势在于范围查找。

    6.2 索引类型对比(按使用功能划分)

    6.2.1 普通索引

    这是最基本的索引类型,而且它没有唯一性之类的限制。

    6.2.2 唯一索引

    索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。

    6.2.3 主键索引

    主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”,且不允许有空值。如果你曾经用过AUTO_INCREMENT类型的列,你可能已经熟悉主键之类的概念了。主键一般在创建表的时候指定,例如“CREATE TABLE tablename ( [...], PRIMARY KEY (列的列表) ); ”。但是,我们也可以通过修改表的方式加入主键,例如“ALTER TABLE tablename ADD PRIMARY KEY (列的列表); ”。每个表只能有一个主键。 (主键相当于聚合索引,是查找最快的索引)。

    6.2.4 全文索引

    MySQL只有MyISAM存储引擎支持FULLTEXT索引,并且类类型为CHAR、TEXT、VARCHAR。详细参考同上。

    6.2.5 外键索引

    如果表A的主关键字是表B中的字段,则该字段称为表B的外键,表A称为主表,表B称为从表。外键是用来实现参照完整性的,不同的外键约束方式将可以使两张表紧密的结合起来,特别是修改或者删除的级联操作将使得日常的维护工作更加轻松。


**************************************************************************************************
*  转载请注明出处:http://blog.csdn.net/jesseyoung/article/details/38037543  *
**************************************************************************************************

<think>好的,用户现在想提升MySQL技术能力,特别是查询性能和编写能力。我需要结合之前的对话内容和用户提供的引用资料来给出有效的建议。 首先回顾之前的对话,用户之前询问了一个关于按科室统计处方数量并添加排名的SQL问题,我给出了详细的解决方案,包括窗口函数的使用、性能优化建议如索引和分区表,以及扩展应用如百分比占比和多维度排名。用户现在的问题更广泛,想全面提升MySQL技术,尤其是查询性能和编写能力。 接下来,看看用户提供的引用资料。引用1提到了10种优化方法,包括升级版本、调整缓冲区、索引设计、查询优化、并发控制、分区、日志设置等。引用2强调了性能优化的重要性,引用3详细讨论了SELECT语句优化,包括索引、覆盖索引、EXPLAIN命令、避免SELECT *等。引用4是一个实战案例,展示如何通过索引、查询重写等解决性能问题。 用户的需求是提升技术能力,所以需要从基础到进阶的结构化建议。可能需要涵盖索引优化、查询语句优化、执行计划分析、性能监控、实战练习等。还要注意用户可能对窗口函数、分区表等已有一定了解,可以适当深入。 另外,用户之前的回答中提到了索引策略和分区表,所以在这次回答中可以扩展这些点,比如如何选择合适的索引、避免索引滥用等。同时,用户之前的问题涉及窗口函数,可以建议学习高级窗口函数的使用场景。 还要考虑用户可能是在医疗行业处理处方数据,数据量可能较大,因此分区、分库分表、缓存策略等高级优化方法可能相关。此外,用户可能需要实际案例来练习,可以推荐一些实战资源或练习方法。 需要确保回答结构清晰,分点明确,结合理论和实践,引用之前的例子和资料中的内容,比如使用EXPLAIN命令、避免SELECT *,以及引用中的优化案例。同时,要推荐学习资源帮助用户持续提升,比如官方文档、书籍、在线课程等。 最后,生成相关问题时要围绕提升查询性能和编写能力,可能涉及索引选择、执行计划分析、事务优化、锁机制、分库分表等,确保问题有针对性,帮助用户进一步深入学习。</think>根据您希望提升MySQL查询性能与编写能力的需求,结合您之前接触的窗口函数、排名统计等场景,我将从实战角度为您梳理系统性提升路径,并整合关键优化技术(引用内容已融合到解决方案中)。 --- ### 一、查询性能提升核心路径 #### 1. **索引优化(性能基石)** - **场景对比优化** 在您之前的处方统计场景中,`DEPT_NAME`和`OPEN_TIME`是高频过滤条件: ```sql -- 组合索引创建(过滤条件+分组字段) CREATE INDEX idx_filter_group ON PUB_PRESCRIPTION ( HERBAL_DECOCT_CODE, VISIT_TYPE_NAME, OPEN_TIME, DEPT_NAME ); ``` - **效果**:索引覆盖`WHERE`条件与`GROUP BY`,减少全表扫描[^3] - **避免索引失效** 注意`OPEN_TIME`的范围查询可能导致索引中断: ```sql -- 错误示例:函数操作导致索引失效 SELECT * FROM table WHERE YEAR(OPEN_TIME) = 2025 -- 正确写法:保持原始字段比较 SELECT * FROM table WHERE OPEN_TIME BETWEEN '2025-01-01' AND '2025-12-31' ``` #### 2. **查询语句重构技巧** - **减少数据扫描量** 在您之前的处方排名查询中,避免使用`SELECT *`: ```sql -- 优化前(全字段读取) SELECT * FROM PUB_PRESCRIPTION WHERE... -- 优化后(仅读取必要字段) SELECT DEPT_NAME, OPEN_TIME FROM PUB_PRESCRIPTION WHERE... ``` 数据量减少可降低I/O负载,提升缓存效率[^3] - **窗口函数深度优化** 针对您使用的`RANK()`排名场景: ```sql -- 添加过滤条件减少窗口计算量 SELECT * FROM ( SELECT DEPT_NAME, COUNT(*) AS cnt, RANK() OVER (ORDER BY COUNT(*) DESC) AS ranking FROM PUB_PRESCRIPTION WHERE OPEN_TIME > '2025-01-01' GROUP BY DEPT_NAME ) subquery WHERE ranking <= 10 ``` --- ### 三、进阶性能调优方法 #### 3. **执行计划分析(EXPLAIN实战)** 以您科室排名的查询为例: ```sql EXPLAIN SELECT DEPT_NAME, COUNT(*), RANK() OVER (ORDER BY COUNT(*) DESC) FROM PUB_PRESCRIPTION WHERE HERBAL_DECOCT_CODE = '2' GROUP BY DEPT_NAME; ``` - **关键指标解读**: - `type: index` 表示索引扫描 - `rows: 152` 预估扫描行数 - `Extra: Using index` 使用覆盖索引[^4] #### 4. **服务器参数调优** 针对医疗系统高频统计场景: ```ini # my.cnf配置示例 innodb_buffer_pool_size = 系统内存的70%-80% # 提升InnoDB缓存命中率 tmp_table_size = 64M # 避免临时表落盘 sort_buffer_size = 4M # 优化GROUP BY排序性能 ``` --- ### 四、能力提升训练体系 #### 5. **系统性学习路线** 1. **基础能力** - 索引原理(B+树结构、索引选择性计算) - 事务隔离级别与锁机制(解决医疗数据并发冲突) 2. **中级技能** - 执行计划深度解读(EXPLAIN FORMAT=JSON) - 慢查询日志分析工具(pt-query-digest) 3. **高阶实战** - 分库分表设计(按科室或时间分片) - 分布式事务解决方案(XA事务、Seata) #### 6. **实战训练建议** - **模拟医疗数据场景**: ```sql -- 创建10万条测试数据 CREATE TABLE test_prescriptions AS SELECT FLOOR(RAND()*100) AS dept_id, NOW() - INTERVAL FLOOR(RAND()*365) DAY AS open_time, CASE WHEN RAND()>0.5 THEN '2' ELSE '1' END AS herbal_code FROM information_schema.tables t1, information_schema.tables t2 LIMIT 100000; ``` - **优化挑战**:尝试在不使用窗口函数的情况下实现科室排名功能 --- ### 五、推荐学习资源 1. **官方文档** [MySQL 8.0 Reference Manual](https://dev.mysql.com/doc/refman/8.0/en/) - 窗口函数章节 2. **书籍推荐** 《高性能MySQL(第4版)》- 索引设计与查询优化专题 3. **在线实验** [SQL Murder Mystery](https://mystery.knightlab.com/) - 通过破案游戏练习复杂查询 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值