MySQL中冗余和重复索引的区别说明

MySQL允许在单个列上创建多个索引,无论是有意还是无意,MySQL需要单独维护这些重复索引,优化器在优化查询时也需要逐个考虑这会影响MySQL的性能

概念阐述

重复索引: 在相同的列上按照相同的顺序创建的相同类型的索引。应该避免创建这样的重复索引,发现之后也应该立即移除。

冗余索引: 两个索引按照相同的顺序覆盖了相同的列。

创建的原因

一般来说,我们有时候会在不经意间创建了重复索引,例如下面的例子:

CREATE TABLE test(
 ID INT NOT NULL PRIMARY KEY,
 A INT NOT NULL,
 B INT NOT NULL,
 UNIQUE(ID),
 INDEX(ID)
)ENGINE=InnoDB;

因为MySQL的唯一限制和主键限制都是通过索引实现的,所以事实上使用如上代码创建的表实际上会在ID列上创建3个索引。通常没有理由这样做,除非是在同一列上为了满足不同的查询需求创建不同类型的索引。比如KEY(col)和FULLTEXT KEY(col)两种索引。

冗余索引和重复索引有些不同。如果创建了索引(A,B),再创建索引(A)就是冗余索引,因为这只是前一个索引的前缀索引。但是如果创建了索引(B,A),则不是冗余索引,索引(B)也不是冗余索引,因为它不是索引(A,B)的最左前缀列。除此之外,不同类型的索引也不会是B树索引的冗余索引,无论覆盖的索引列是什么。

在大多数情况下都不需要使用冗余索引,应该尽可能拓展已有的索引而不是创建新的索引。但有时候出于性能的考虑,比如拓展已有的索引会使得其变得太大,从而影响其他使用该索引的查询的性能。

影响

创建冗余索引作为覆盖索引可以提高我们对于部分查询的QPS,但是存在两个索引也有缺点,即索引成本更高。

当表中的索引越来越多时,表的插入速度会变慢。一般而言,增加新索引将会导致INSERT、UPDATE、DELETE等操作的速度变慢,特别是当新增索引之后导致达到了内存的瓶颈的时候。

解决冗余索引和重复索引的方法很简单,删除这些索引就可以 ,但是首先要做的就是找出这样的索引。

补充:MySQL中重复索引和重复外键清理

MySQL允许在相同列上创建重复的索引,但这样做对数据库却是有害而无利的,需要定期检查此类重复索引以改善数据库性能。

可减少磁盘空间占用、减少磁盘IO、减少优化器优化查询时需要比较的索引个数、减少数据库维护冗余索引的各类开销、提高数据库性能(插入、更新、删除)

重复索引检测

pt-duplicate-key-checker:通过SHOW CREATE TABLE输出的表定义检测MySQL表中重复或者冗余的索引或外键

可以检测到的冗余/重复索引类型:若某个索引和另外某个索引以同样的顺序包含同样的列,或者该索引包含的列是另外某个索引的最左前缀列,则被认为是重复/冗余的索引。

默认情况下只在同类型的索引间(如BTREE索引)进行比较,不同类型的索引即使符合上述描述也不会被认为是重复/冗余,但这一行为可以通过参数改变。

除此之外,还可检测重复的外键,即引用的表和列均相同的外键。对于聚簇索引的表,在辅助索引后添加主键列的索引也被认为是冗余的,因为这种情况下,辅助索引末尾本身就包含有主键信息。

基本用法以及样例输出如下

[root@VM_8_180_centos packages]# pt-duplicate-key-checker A=utf8, F=/etc/my.cnf, h=localhost, u=root, P=3306 –ask-pass

样例输出:

# ########################################################################
# dcf.privilege
# ########################################################################
# Uniqueness of UQI_IDX_1 ignored because PRIMARY is a duplicate constraint
# UQI_IDX_1 is a duplicate of PRIMARY
# Key definitions:
#  UNIQUE KEY `UQI_IDX_1` (`privilege_id`),
#  PRIMARY KEY (`privilege_id`),
# Column types:
#   `privilege_id` varchar(50) collate utf8_bin not null comment '权限id'
# To remove this duplicate index, execute:
ALTER TABLE `dcf`.`privilege` DROP INDEX `UQI_IDX_1`;
# ########################################################################
# dcf.t_game_config
# ########################################################################
# Uniqueness of pkey ignored because PRIMARY is a duplicate constraint
# pkey is a duplicate of PRIMARY
# Key definitions:
#  UNIQUE KEY `pkey` (`pkey`)
#  PRIMARY KEY (`pkey`),
# Column types:
#   `pkey` bigint(20) not null auto_increment
# To remove this duplicate index, execute:
ALTER TABLE `dcf`.`t_game_config` DROP INDEX `pkey`;
# ########################################################################
# dcf.t_project_institution
# ########################################################################
# index_1 is a left-prefix of index_2
# Key definitions:
#  KEY `index_1` (`project_id`),
#  KEY `index_2` (`project_id`,`institution_id`,`delete_flag`)
# Column types:
#   `project_id` bigint(20) not null comment '项目id'
#   `institution_id` varchar(20) not null comment '机构id'
#   `delete_flag` tinyint(4) not null
# To remove this duplicate index, execute:
ALTER TABLE `dcf`.`t_project_institution` DROP INDEX `index_1`;
# ########################################################################
# dcf_commons.bank_cnaps
# ########################################################################
# idx is a duplicate of PRIMARY
# Key definitions:
#  KEY `idx` (`cnaps`)
#  PRIMARY KEY (`cnaps`),
# Column types:
#   `cnaps` varchar(255) not null comment '电子联行号'
# To remove this duplicate index, execute:
ALTER TABLE `dcf_commons`.`bank_cnaps` DROP INDEX `idx`;
# ########################################################################
# dcf_contract.customer_bank_account
# ########################################################################
# IDX_CUSTOMER_ID is a left-prefix of UQI_IDX_1
# Key definitions:
#  KEY `IDX_CUSTOMER_ID` (`customer_id`)
#  UNIQUE KEY `UQI_IDX_1` (`customer_id`,`account_no`,`branch_bank`,`account_type`,`account_name`) USING BTREE,
# Column types:
#   `customer_id` varchar(20) collate utf8_bin not null comment '客户id'
#   `account_no` varchar(40) collate utf8_bin default null comment '银行账号'
#   `branch_bank` varchar(100) collate utf8_bin default null comment '开户支行'
#   `account_type` tinyint(4) default null comment '账户类型:比如收款账户,还款账户等\n0-收款账户\n1-还款账户'
#   `account_name` varchar(100) collate utf8_bin default null comment '银行账户户名'
# To remove this duplicate index, execute:
ALTER TABLE `dcf_contract`.`customer_bank_account` DROP INDEX `IDX_CUSTOMER_ID`;
# ########################################################################
# dcf_contract.t_contract_account
# ########################################################################
# IDX_CONTRACT_ID is a left-prefix of t_contract_account_uq1
# Key definitions:
#  KEY `IDX_CONTRACT_ID` (`contract_id`)
#  UNIQUE KEY `t_contract_account_uq1` (`contract_id`,`account_type`),
# Column types:
#   `contract_id` bigint(20) not null comment '合同id'
#   `account_type` tinyint(4) not null comment '账户类 型:globalconstant.bankaccounttypec常数 \n0-收款账户\n1-还款账户 等'
# To remove this duplicate index, execute:
ALTER TABLE `dcf_contract`.`t_contract_account` DROP INDEX `IDX_CONTRACT_ID`;
......
......
# ########################################################################
# Summary of indexes
# ########################################################################
# Size Duplicate Indexes  173317386
# Total Duplicate Indexes 18
# Total Indexes      562

会给出重复/冗余类型、索引/外键定义、索引包含的列类型、移除重复/冗余索引/外键的SQL、最后会给出有关索引的统计信息。

重复索引删除

直接执行工具输出结果中的ALTER TABLE语句即可,但是执行前一定要仔细评估可能造成的影响。比如,表非常非常大的情况下可能造成主从复制延迟,又比如SQL中若包含索引提示的话直接删除索引可能导致报SQL语法错误,最好事先查一下是不是包含此类SQL(可通过general log或者tcpdump工具获取SQL并加以分析)

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • MySQL查询冗余索引和未使用过的索引操作

    MySQL5.7及以上版本提供直接查询冗余索引.重复索引和未使用过索引的视图,直接查询即可. 查询冗余索引.重复索引 select * sys.from schema_redundant_indexes; 查询未使用过的索引 select * from sys.schema_unused_indexes; 如果想在5.6和5.5版本使用,将视图转换成SQL语句查询即可 查询冗余索引.重复索引 select a.`table_schema`,a.`table_name`,a.`index_name

  • mysql重复索引与冗余索引实例分析

    本文实例讲述了mysql重复索引与冗余索引.分享给大家供大家参考,具体如下: 重复索引:表示一个列或者顺序相同的几个列上建立的多个索引. 冗余索引:两个索引所覆盖的列重叠 冗余索引在一些特殊的场景下使用到了索引覆盖,所以比较快. 场景 比如文章与标签表 +--+---+--+ | id | artid | tag | +--+---+--+ | 1 | 1 | PHP | | 2 | 1 | Linux | | 3 | 2 | MySQl | | 4 | 2 | Oracle | +--+---

  • 详解mysql中的冗余和重复索引

    mysql允许在相同列上创建多个索引,无论是有意还是无意,mysql需要单独维护重复的索引,并且优化器在优化查询的时候也需要逐个地进行考虑,这会影响性能. 重复索引是指的在相同的列上按照相同的顺序创建的相同类型的索引,应该避免这样创建重复索引,发现以后也应该立即删除.但,在相同的列上创建不同类型的索引来满足不同的查询需求是可以的. CREATE TABLE test( ID INT NOT NULL PRIMARY KEY, A INT NOT NULL, B INT NOT NULL, UNI

  • MySQL中冗余和重复索引的区别说明

    MySQL允许在单个列上创建多个索引,无论是有意还是无意,MySQL需要单独维护这些重复索引,优化器在优化查询时也需要逐个考虑这会影响MySQL的性能 概念阐述 重复索引: 在相同的列上按照相同的顺序创建的相同类型的索引.应该避免创建这样的重复索引,发现之后也应该立即移除. 冗余索引: 两个索引按照相同的顺序覆盖了相同的列. 创建的原因 一般来说,我们有时候会在不经意间创建了重复索引,例如下面的例子: CREATE TABLE test( ID INT NOT NULL PRIMARY KEY,

  • mysql中datetime字段建立索引并比较大小详解

    目录 1.问题背景 2.优化过程 补充:Mysql 时间Datetime 索引不生效问题 总结 1.问题背景 最近测试库查询一个表的数据,需要用到唯一的一个日期类型字段作为 where 的子查询(查询当天的数据),就正常写了个这样的 SQL,具体的表名我就不写了: # create_time 是 datetime 类型 select * from ${tablename} where date(create_time)='20220919' limit 20; 其中字段的值样本如下: 我知道我写

  • 你知道mysql中空值和null值的区别吗

    前言 最近发现带的小伙伴写sql对于空值的判断方法不正确,导致程序里面的数据产生错误,在此进行一下整理,方便大家以后正确的判断空值.以下带来示例给大家进行讲解. 建表 create table test (colA varchar(10) not null,colB varchar(10) null); 向test表中插入数据 插入colA为null的数据 insert into test values (null,1); 此时会报错,因为colA列不能为null. 插入colB为null的数据

  • MySQL中varchar和char类型的区别

    目录 前述 VARCHAR类型 VARCHAR适用情况 CHAR类型 测试 VARCHAR(5)与VARCHAR(200)的区别 总结 前述 VARCHAR和CHAR是两种最主要的字符串类型.不幸的是,很难精确地解释这些值是怎么存储在磁盘和内存中的,因为这跟存储引擎的具体实现有关.下面的描述假设使用的存储引擎是InnoDB和/或者MyISAM.如果使用的不是这两种存储引擎,请参考所使用的存储引擎的文档. 先看看VARCHAR和CHAR值通常在磁盘上怎么存储.请注意,存储引擎存储CHAR或者VAR

  • mysql中redo log和 binlog的区别

    想跟大家聊聊关于 mysql 中的两个小的知识点:redo log 和 binlog . redo log :InnoDB 存储引擎层方面的日志,所以如果你使用的存储引擎不是 InnoDB 的话,那就根本谈不上 redo log. binlog : MySQL Server 层记录的日志,所以不管是用的什么存储引擎,只要是 MySQL 都是会有 binlog 的存在,在做 MySQL 主从复制的时候,利用的就是 binlog. 接下来,我们就详细来看看它们都分别做了啥? redo log 为什么

  • 一文弄懂MySQL中redo log与binlog的区别

    目录 前言 1. 什么是redo log? 1.1 redo日志文件名 1.2 影响redo log参数 1.3 redo log大小怎么设置? 2. 什么是binlog 2.1 binlog文件名 2.2 影响binlog的参数 2.3 查看binlog 3. redo log与binlog的区别 总结 前言 MySQL中有六种日志文件,分别是:重做日志(redo log).回滚日志(undo log).二进制日志(binlog).错误日志(errorlog).慢查询日志(slow query

  • MySQL中处理各种重复的一些方法

    有时表或结果集包含重复的记录.有时它是允许的,但有时它需要停止重复的记录.有时它需要识别重复的记录从表中删除.本章将介绍如何防止发生在一个表中重复的记录如何删除已经存在的重复记录. 防止重复表中的发生: 可以使用PRIMARY KEY或UNIQUE索引的表上相应的字段来防止重复的记录.让我们举一个例子,下面的表中不包含这样的索引或主键,所以它会允许记录first_name和last_name重复 CREATE TABLE person_tbl ( first_name CHAR(20), las

  • MySQL中union和join语句使用区别的辨析教程

    union和join是需要联合多张表时常见的关联词,具体概念我就不说了,想知道上网查就行,因为我也记不准确. 先说差别:union对两张表的操作是合并数据条数,等于是纵向的,要求是两张表字段必须是相同的(Schema of both sides of union should match.).也就是说如果A表中有三条数据,B表中有两条数据,那么A union B就会有五条数据.说明一下union 和union all的差别,对于union如果存在相同的数据记录会被合并,而union all不会合

  • 详解MySQL中DROP,TRUNCATE 和DELETE的区别实现mysql从零开始

    不同点:  1. truncate和 delete只删除数据不删除表的结构(定义)      drop语句将删除表的结构被依赖的约束(constrain),触发器(trigger),索引(index); 依赖于该表的 存储过程/函数将保留,但是变为invalid状态. 2.delete语句是dml,这个操作会放到rollback segement中,事务提交之后才生效;如果有相应的trigger, 执行的时候将被触发.     truncate,drop是ddl, 操作立即生效,原数据不放到ro

随机推荐