add brain
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
---
|
||||
title: Index Maintenance and Cleanup
|
||||
description: Index maintenance
|
||||
tags: mysql, indexes, maintenance, unused-indexes, performance
|
||||
---
|
||||
|
||||
# Index Maintenance
|
||||
|
||||
## Find Unused Indexes
|
||||
|
||||
```sql
|
||||
-- Requires performance_schema enabled (default in MySQL 5.7+)
|
||||
-- "Unused" here means no reads/writes since last restart.
|
||||
SELECT object_schema, object_name, index_name, COUNT_READ, COUNT_WRITE
|
||||
FROM performance_schema.table_io_waits_summary_by_index_usage
|
||||
WHERE object_schema = 'mydb'
|
||||
AND index_name IS NOT NULL AND index_name != 'PRIMARY'
|
||||
AND COUNT_READ = 0 AND COUNT_WRITE = 0
|
||||
ORDER BY COUNT_WRITE DESC;
|
||||
```
|
||||
|
||||
Sometimes you'll also see indexes with **writes but no reads** (overhead without query benefit). Review these carefully: some are required for constraints (UNIQUE/PK) even if not used in query plans.
|
||||
|
||||
```sql
|
||||
SELECT object_schema, object_name, index_name, COUNT_READ, COUNT_WRITE
|
||||
FROM performance_schema.table_io_waits_summary_by_index_usage
|
||||
WHERE object_schema = 'mydb'
|
||||
AND index_name IS NOT NULL AND index_name != 'PRIMARY'
|
||||
AND COUNT_READ = 0 AND COUNT_WRITE > 0
|
||||
ORDER BY COUNT_WRITE DESC;
|
||||
```
|
||||
|
||||
Counters reset on restart — ensure 1+ full business cycle of uptime before dropping.
|
||||
|
||||
## Find Redundant Indexes
|
||||
|
||||
Index on `(a)` is redundant if `(a, b)` exists (leftmost prefix covers it). Pairs sharing only the first column (e.g. `(a,b)` vs `(a,c)`) need manual review — neither is redundant.
|
||||
|
||||
```sql
|
||||
-- Prefer sys schema view (MySQL 5.7.7+)
|
||||
SELECT table_schema, table_name,
|
||||
redundant_index_name, redundant_index_columns,
|
||||
dominant_index_name, dominant_index_columns
|
||||
FROM sys.schema_redundant_indexes
|
||||
WHERE table_schema = 'mydb';
|
||||
```
|
||||
|
||||
## Check Index Sizes
|
||||
|
||||
```sql
|
||||
SELECT database_name, table_name, index_name,
|
||||
ROUND(stat_value * @@innodb_page_size / 1024 / 1024, 2) AS size_mb
|
||||
FROM mysql.innodb_index_stats
|
||||
WHERE stat_name = 'size' AND database_name = 'mydb'
|
||||
ORDER BY stat_value DESC;
|
||||
-- stat_value is in pages; multiply by innodb_page_size for bytes
|
||||
```
|
||||
|
||||
## Index Write Overhead
|
||||
Each index must be updated on INSERT, UPDATE, and DELETE operations. More indexes = slower writes.
|
||||
|
||||
- **INSERT**: each secondary index adds a write
|
||||
- **UPDATE**: changing indexed columns updates all affected indexes
|
||||
- **DELETE**: removes entries from all indexes
|
||||
|
||||
InnoDB can defer some secondary index updates via the change buffer, but excessive indexing still reduces write throughput.
|
||||
|
||||
## Update Statistics (ANALYZE TABLE)
|
||||
The optimizer relies on index cardinality and distribution statistics. After large data changes, refresh statistics:
|
||||
|
||||
```sql
|
||||
ANALYZE TABLE orders;
|
||||
```
|
||||
|
||||
This updates statistics (does not rebuild the table).
|
||||
|
||||
## Rebuild / Reclaim Space (OPTIMIZE TABLE)
|
||||
`OPTIMIZE TABLE` can reclaim space and rebuild indexes:
|
||||
|
||||
```sql
|
||||
OPTIMIZE TABLE orders;
|
||||
```
|
||||
|
||||
For InnoDB this effectively rebuilds the table and indexes and can be slow on large tables.
|
||||
|
||||
## Invisible Indexes (MySQL 8.0+)
|
||||
Test removing an index without dropping it:
|
||||
|
||||
```sql
|
||||
ALTER TABLE orders ALTER INDEX idx_status INVISIBLE;
|
||||
ALTER TABLE orders ALTER INDEX idx_status VISIBLE;
|
||||
```
|
||||
|
||||
Invisible indexes are still maintained on writes (overhead remains), but the optimizer won't consider them.
|
||||
|
||||
## Index Maintenance Tools
|
||||
|
||||
### Online DDL (Built-in)
|
||||
Most add/drop index operations are online-ish but still take brief metadata locks:
|
||||
|
||||
```sql
|
||||
ALTER TABLE orders ADD INDEX idx_status (status), ALGORITHM=INPLACE, LOCK=NONE;
|
||||
```
|
||||
|
||||
### pt-online-schema-change / gh-ost
|
||||
For very large tables or high-write workloads, online schema change tools can reduce blocking by using a shadow table and a controlled cutover (tradeoffs: operational complexity, privileges, triggers/binlog requirements).
|
||||
|
||||
## Guidelines
|
||||
- 1–5 indexes per table is normal. 6+: audit for redundancy.
|
||||
- Combine `performance_schema` data with `EXPLAIN` of frequent queries monthly.
|
||||
Reference in New Issue
Block a user