Python 识别 MySQL 中的冗余索引

文若6个月前技术文章152

前言

最近在搞标准化巡检平台,通过 MySQL 的元数据分析一些潜在的问题。冗余索引也是一个非常重要的巡检目,表中索引过多,会导致表空间占用较大,索引的数量与表的写入速度与索引数成线性关系(微秒级),如果发现有冗余索引,建议立即审核删除。

PS:之前见过一个客户的数据库上面竟然创建 100 多个索引!?当时的想法是 “他们在玩排列组合呢” 表写入非常慢,严重影响性能和表维护的复杂度。

脚本介绍

表结构

下方是演示的表结构:

CREATE TABLE `index_test03` (
 `id` bigint(20) NOT NULL AUTO_INCREMENT,
 `name` varchar(20) NOT NULL,
 `create_time` varchar(20) NOT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `uqi_name` (`name`),
 KEY `idx_name` (`name`),
 KEY `idx_name_createtime`(name, create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

MySQL 元数据

MySQL 可以通过 information_schema.STATISTICS 表查询索引信息:

SELECT * from information_schema.STATISTICS  where TABLE_SCHEMA = 'test02' and TABLE_NAME = 'index_test03';

TABLE_CATALOGTABLE_SCHEMATABLE_NAMENON_UNIQUEINDEX_SCHEMAINDEX_NAMESEQ_IN_INDEXCOLUMN_NAMECOLLATIONCARDINALITYSUB_PARTPACKEDNULLABLEINDEX_TYPECOMMENTINDEX_COMMENT
deftest02index_test030test02PRIMARY1idA0NULLNULL
BTREE

deftest02index_test030test02uqi_name1nameA0NULLNULL
BTREE

deftest02index_test031test02idx_name1nameA0NULLNULL
BTREE

deftest02index_test031test02idx_name_createtime1nameA0NULLNULL
BTREE

deftest02index_test031test02idx_name_createtime2create_timeA0NULLNULL
BTREE

脚本通过获得 STATISTICS 表中的索引信息来分析表中是否存在冗余索引,分析粒度为表级别。

DEMO 演示

需要使用 pandas 模块。

import pandas as pd

df_table_level = pd.read_csv('/Users/cooh/Desktop/sqlresult_6162675.csv')

table_indexes = df_table_level['INDEX_NAME'].drop_duplicates().tolist()

_indexes = list()
for index_name in table_indexes:
   index_info = {'index_cols': df_table_level[df_table_level['INDEX_NAME'] == index_name]['COLUMN_NAME'].tolist(),
                 'non_unique': df_table_level[df_table_level['INDEX_NAME'] == index_name]['NON_UNIQUE'].tolist()[0],
                 'index_name': index_name
                 }
   _indexes.append(index_info)

content = ''
election_dict = {i['index_name']: 0 for i in _indexes}

while len(_indexes) > 0:
   choice_index_1 = _indexes.pop(0)

   for choice_index_2 in _indexes:
       # 对比两个索引字段的个数,使用字段小的进行迭代
       min_len = min([len(choice_index_1['index_cols']), len(choice_index_2['index_cols'])])

       # 获得相似字段的个数据
       similarity_col = 0
       for i in range(min_len):
           # print(i)
           if choice_index_1['index_cols'][i] == choice_index_2['index_cols'][i]:
               similarity_col += 1

       # 然后进行逻辑判断
       if similarity_col == 0:
           # print('毫无冗余')
           pass
       else:
           # 两个索引的字段包含内容都相同,说明两个索引完全相同,接下来就需要从中选择一个删除
           if len(choice_index_1['index_cols']) == similarity_col and len(
                   choice_index_2['index_cols']) == similarity_col:
               # 等于 0 表示有唯一约束
               if choice_index_1['non_unique'] == 1:
                   content += '索引 {0} 与索引 {1} 重复, '.format(choice_index_2['index_name'], choice_index_1['index_name'])
                   election_dict[choice_index_1['index_name']] += 1
               elif choice_index_2['non_unique'] == 1:
                   content += '索引 {0} 与索引 {1} 重复, '.format(choice_index_1['index_name'], choice_index_2['index_name'])
                   election_dict[choice_index_2['index_name']] += 1
               else:
                   content += '索引 {0} 与索引 {1} 重复, '.format(choice_index_2['index_name'], choice_index_1['index_name'])
                   # 两个索引都是唯一索引
                   if choice_index_1['index_name'] != 'PRIMARY':
                       election_dict[choice_index_1['index_name']] += 1
                   elif choice_index_2['index_name'] != 'PRIMARY':
                       election_dict[choice_index_2['index_name']] += 1

           elif len(choice_index_1['index_cols']) == similarity_col and choice_index_1['non_unique'] != 0:
               content += '索引 {0} 与索引 {1} 重复, '.format(choice_index_2['index_name'], choice_index_1['index_name'])
               election_dict[choice_index_1['index_name']] += 1

           elif len(choice_index_2['index_cols']) == similarity_col and choice_index_2['non_unique'] != 0:
               content += '索引 {0} 与索引 {1} 重复, '.format(choice_index_1['index_name'], choice_index_2['index_name'])
               election_dict[choice_index_2['index_name']] += 1

# 通过索引类型来判断是否需要删除
redundancy_indexes = list()
for _k_name, _vote in election_dict.items():
   if _vote > 0:
       redundancy_indexes.append(_k_name)

content += '建议删除索引:{0}'.format(', '.join(redundancy_indexes))

print(content)

输出结果:

索引 uqi_name 与索引 idx_name 重复, 索引 idx_name_createtime 与索引 idx_name 重复, 建议删除索引:idx_name

SQL 查询冗余索引

MySQL 5.7 是可以直接通过 sys 元数据库中的视图来查冗余索引的,但是云上 RDS 用户看不到 sys 库。所以才被迫写这个脚本,因为实例太多了,一个一个看不现实。如果你是自建的 MySQL,就不用费那么大劲了,直接使用下面 SQL 来统计。

select * from sys.schema_redundant_indexes;

后记

删除索引属于高危操作,删除前需要多次 check 后再删除。上面是一个 demo 可以包装成函数,使用 pandas 以表为粒度传入数据,就可以嵌入到程序中。有问题欢迎评论沟通。


相关文章

域名购买及备案

域名购买及备案

购买域名域名控制台 -- 域名列表 -- 注册域名注意域名购买时候域名系统中填写的持有者单位名称需与提交审核证件中的单位名称严格完全一致的模板认证中企业/组织相关材料及填写要求可参考此文档https:...

Flink 状态管理

Flink 状态管理

一、  Flink 中的状态1、由一个任务维护,并且用来计算某个结果的所有数据,都属于这个任务的状态 2、可以认为状态就是一个本地变量,可以被任务的业务逻辑访问 3、Flink 会进行状态管理,包括状...

prometheus黑盒监控

prometheus黑盒监控

一.背景黑盒监控:主要关注的现象,一般都是正在发生的东西,例如出现一个告警,业务接口不正常,那么这种监控就是站在用户的角度能看到的监控,重点在于能对正在发生的故障进行告警。二.操作前了解相关配置和要求...

配置ranger后hive注册永久UDF

配置ranger后hive注册永久UDF

背景:由于有些场景在启用Ranger情况下,客户在分配权限时候对高权限有特别要求,尽可能给用户设置低权限,无法在ranger中设置用户为 is Role admin权限(hive管理员权限)操作流程:...

LINUX 安全运维-OpenSSH安全

LINUX 安全运维-OpenSSH安全

SSH 是建立在应用层基础上的安全协议,是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用 SSH协议可以有效防止远程管理过程中的信息泄露问题。SSH最初是UNIX系统上的一...

flume开启jmx加入grafana

部署flume_exporter需要go环境编译配置go环境首先下载go安装包Linux:https://golang.google.cn/dl/解压、添加环境变量tar -zxvf go*.tar....

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。