add brain

This commit is contained in:
2026-03-12 15:17:52 +07:00
parent fd9f558fa1
commit e7821a7a9d
355 changed files with 93784 additions and 24 deletions

View File

@@ -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
- 15 indexes per table is normal. 6+: audit for redundancy.
- Combine `performance_schema` data with `EXPLAIN` of frequent queries monthly.