information_schema 数据库相当于一个数据字典,保存了表的元信息。
mysql> use information_schema;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+---------------------------------------+
| Tables_in_information_schema |
+---------------------------------------+
| CHARACTER_SETS |
| COLLATIONS |
| COLLATION_CHARACTER_SET_APPLICABILITY |
| COLUMNS |
| COLUMN_PRIVILEGES |
| ENGINES |
| EVENTS |
| FILES |
| GLOBAL_STATUS |
| GLOBAL_VARIABLES |
| KEY_COLUMN_USAGE |
| OPTIMIZER_TRACE |
| PARAMETERS |
| PARTITIONS |
| PLUGINS |
| PROCESSLIST |
| PROFILING |
| REFERENTIAL_CONSTRAINTS |
| ROUTINES |
| SCHEMATA |
| SCHEMA_PRIVILEGES |
| SESSION_STATUS |
| SESSION_VARIABLES |
| STATISTICS |
| TABLES |
| TABLESPACES |
| TABLE_CONSTRAINTS |
| TABLE_PRIVILEGES |
| TRIGGERS |
| USER_PRIVILEGES |
| VIEWS |
| INNODB_LOCKS |
| INNODB_TRX |
| INNODB_SYS_DATAFILES |
| INNODB_FT_CONFIG |
| INNODB_SYS_VIRTUAL |
| INNODB_CMP |
| INNODB_FT_BEING_DELETED |
| INNODB_CMP_RESET |
| INNODB_CMP_PER_INDEX |
| INNODB_CMPMEM_RESET |
| INNODB_FT_DELETED |
| INNODB_BUFFER_PAGE_LRU |
| INNODB_LOCK_WAITS |
| INNODB_TEMP_TABLE_INFO |
| INNODB_SYS_INDEXES |
| INNODB_SYS_TABLES |
| INNODB_SYS_FIELDS |
| INNODB_CMP_PER_INDEX_RESET |
| INNODB_BUFFER_PAGE |
| INNODB_FT_DEFAULT_STOPWORD |
| INNODB_FT_INDEX_TABLE |
| INNODB_FT_INDEX_CACHE |
| INNODB_SYS_TABLESPACES |
| INNODB_METRICS |
| INNODB_SYS_FOREIGN_COLS |
| INNODB_CMPMEM |
| INNODB_BUFFER_POOL_STATS |
| INNODB_SYS_COLUMNS |
| INNODB_SYS_FOREIGN |
| INNODB_SYS_TABLESTATS |
+---------------------------------------+
61 rows in set (0.00 sec)
查看索引信息。
mysql> select * from key_column_usage limit 3\G -- 显示了哪个索引使用了哪个列
*************************** 1. row ***************************
CONSTRAINT_CATALOG: def
CONSTRAINT_SCHEMA: burn_test
CONSTRAINT_NAME: PRIMARY
TABLE_CATALOG: def
TABLE_SCHEMA: burn_test
TABLE_NAME: Orders -- 表名
COLUMN_NAME: order_id -- 索引的名称
ORDINAL_POSITION: 1
POSITION_IN_UNIQUE_CONSTRAINT: NULL
REFERENCED_TABLE_SCHEMA: NULL
REFERENCED_TABLE_NAME: NULL
REFERENCED_COLUMN_NAME: NULL
*************************** 2. row ***************************
CONSTRAINT_CATALOG: def
CONSTRAINT_SCHEMA: burn_test
CONSTRAINT_NAME: product_name
TABLE_CATALOG: def
TABLE_SCHEMA: burn_test
TABLE_NAME: Orders_MV
COLUMN_NAME: product_name
ORDINAL_POSITION: 1
POSITION_IN_UNIQUE_CONSTRAINT: NULL
REFERENCED_TABLE_SCHEMA: NULL
REFERENCED_TABLE_NAME: NULL
REFERENCED_COLUMN_NAME: NULL
*************************** 3. row ***************************
CONSTRAINT_CATALOG: def
CONSTRAINT_SCHEMA: burn_test
CONSTRAINT_NAME: child_ibfk_1
TABLE_CATALOG: def
TABLE_SCHEMA: burn_test
TABLE_NAME: child
COLUMN_NAME: parent_id
ORDINAL_POSITION: 1
POSITION_IN_UNIQUE_CONSTRAINT: 1
REFERENCED_TABLE_SCHEMA: burn_test
REFERENCED_TABLE_NAME: parent
REFERENCED_COLUMN_NAME: id
3 rows in set (0.04 sec)
可以通过 STATISTICS 表的数据信息,分析索引的 Cardinality 数据。
--
-- 在 information_schema.STATISTICS 中记录了相关的信息
--
mysql> use information_schema;
Database changed
mysql> show create table STATISTICS\G
*************************** 1. row ***************************
Table: STATISTICS
Create Table: CREATE TEMPORARY TABLE `STATISTICS` (
`TABLE_CATALOG` varchar(512) NOT NULL DEFAULT '',
`TABLE_SCHEMA` varchar(64) NOT NULL DEFAULT '', -- 表所在的库
`TABLE_NAME` varchar(64) NOT NULL DEFAULT '', -- 表名
`NON_UNIQUE` bigint(1) NOT NULL DEFAULT '0',
`INDEX_SCHEMA` varchar(64) NOT NULL DEFAULT '',
`INDEX_NAME` varchar(64) NOT NULL DEFAULT '', -- 索引名
`SEQ_IN_INDEX` bigint(2) NOT NULL DEFAULT '0', -- 索引的序号
`COLUMN_NAME` varchar(64) NOT NULL DEFAULT '',
`COLLATION` varchar(1) DEFAULT NULL,
`CARDINALITY` bigint(21) DEFAULT NULL, -- 这里我们找到了Cardinality
`SUB_PART` bigint(3) DEFAULT NULL,
`PACKED` varchar(10) DEFAULT NULL,
`NULLABLE` varchar(3) NOT NULL DEFAULT '',
`INDEX_TYPE` varchar(16) NOT NULL DEFAULT '',
`COMMENT` varchar(16) DEFAULT NULL,
`INDEX_COMMENT` varchar(1024) NOT NULL DEFAULT ''
) ENGINE=MEMORY DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
比如我们查看 employees.salaries 表中的索引信息:
--
-- 之前我们可以通过 show index from table_name的方式查看索引
--
mysql> show index from employees.salaries\G
*************************** 1. row ***************************
Table: salaries
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 1 -- 索引序号为1
Column_name: emp_no
Collation: A
Cardinality: 286271 -- Cardinality值
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
*************************** 2. row ***************************
Table: salaries
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 2 -- 索引序号为2
Column_name: from_date
Collation: A
Cardinality: 2760952 -- Cardinality值
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
2 rows in set (0.00 sec)
--
-- 现在可以通过STATISTICS表查看某张表的信息
--
mysql> select * from STATISTICS where table_name='salaries'\G
*************************** 1. row ***************************
TABLE_CATALOG: def
TABLE_SCHEMA: employees
TABLE_NAME: salaries
NON_UNIQUE: 0
INDEX_SCHEMA: employees
INDEX_NAME: PRIMARY
SEQ_IN_INDEX: 1 -- 索引序号为1
COLUMN_NAME: emp_no
COLLATION: A
CARDINALITY: 286271 -- Cardinality值
SUB_PART: NULL
PACKED: NULL
NULLABLE:
INDEX_TYPE: BTREE
COMMENT:
INDEX_COMMENT:
*************************** 2. row ***************************
TABLE_CATALOG: def
TABLE_SCHEMA: employees
TABLE_NAME: salaries
NON_UNIQUE: 0
INDEX_SCHEMA: employees
INDEX_NAME: PRIMARY
SEQ_IN_INDEX: 2 -- 索引序号为2
COLUMN_NAME: from_date
COLLATION: A
CARDINALITY: 2760952 -- Cardinality值
SUB_PART: NULL
PACKED: NULL
NULLABLE:
INDEX_TYPE: BTREE
COMMENT:
INDEX_COMMENT:
2 rows in set (0.00 sec)
---
--- 可以看出,上面两个方法得到的Cardinality的值是相等
通过 information_schema 数据库可以方便的分析表的元信息。
检查表的索引创建的情况,判断该索引是否有创建的必要。
--
-- 1. 表的信息如table_schema, table_name, table_rows等
-- 在information_schema.TABLES中
--
mysql> show create table TABLES\G
*************************** 1. row ***************************
Table: TABLES
Create Table: CREATE TEMPORARY TABLE `TABLES` (
`TABLE_CATALOG` varchar(512) NOT NULL DEFAULT '',
`TABLE_SCHEMA` varchar(64) NOT NULL DEFAULT '', -- 表所在的库
`TABLE_NAME` varchar(64) NOT NULL DEFAULT '', -- 表名
`TABLE_TYPE` varchar(64) NOT NULL DEFAULT '',
`ENGINE` varchar(64) DEFAULT NULL,
`VERSION` bigint(21) unsigned DEFAULT NULL,
`ROW_FORMAT` varchar(10) DEFAULT NULL,
`TABLE_ROWS` bigint(21) unsigned DEFAULT NULL, -- 表的记录数
`AVG_ROW_LENGTH` bigint(21) unsigned DEFAULT NULL,
`DATA_LENGTH` bigint(21) unsigned DEFAULT NULL,
`MAX_DATA_LENGTH` bigint(21) unsigned DEFAULT NULL,
`INDEX_LENGTH` bigint(21) unsigned DEFAULT NULL,
`DATA_FREE` bigint(21) unsigned DEFAULT NULL,
`AUTO_INCREMENT` bigint(21) unsigned DEFAULT NULL,
`CREATE_TIME` datetime DEFAULT NULL,
`UPDATE_TIME` datetime DEFAULT NULL,
`CHECK_TIME` datetime DEFAULT NULL,
`TABLE_COLLATION` varchar(32) DEFAULT NULL,
`CHECKSUM` bigint(21) unsigned DEFAULT NULL,
`CREATE_OPTIONS` varchar(255) DEFAULT NULL,
`TABLE_COMMENT` varchar(2048) NOT NULL DEFAULT ''
) ENGINE=MEMORY DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
--
-- 2.information.STATISTICS 中存在 table_schema 和 table_name 信息
--
mysql> show create table STATISTICS\G
*************************** 1. row ***************************
Table: STATISTICS
Create Table: CREATE TEMPORARY TABLE `STATISTICS` (
`TABLE_CATALOG` varchar(512) NOT NULL DEFAULT '',
`TABLE_SCHEMA` varchar(64) NOT NULL DEFAULT '', -- 表所在的库
`TABLE_NAME` varchar(64) NOT NULL DEFAULT '', -- 表名
`NON_UNIQUE` bigint(1) NOT NULL DEFAULT '0',
`INDEX_SCHEMA` varchar(64) NOT NULL DEFAULT '',
`INDEX_NAME` varchar(64) NOT NULL DEFAULT '', -- 索引名
`SEQ_IN_INDEX` bigint(2) NOT NULL DEFAULT '0',
`COLUMN_NAME` varchar(64) NOT NULL DEFAULT '',
`COLLATION` varchar(1) DEFAULT NULL,
`CARDINALITY` bigint(21) DEFAULT NULL,
`SUB_PART` bigint(3) DEFAULT NULL,
`PACKED` varchar(10) DEFAULT NULL,
`NULLABLE` varchar(3) NOT NULL DEFAULT '',
`INDEX_TYPE` varchar(16) NOT NULL DEFAULT '',
`COMMENT` varchar(16) DEFAULT NULL,
`INDEX_COMMENT` varchar(1024) NOT NULL DEFAULT ''
) ENGINE=MEMORY DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
--
-- 3. 将TABLES 和 STATISTICS 表中的table_schema和table_name相关联
-- 通过Cardinality和table_rows 计算,即可得到对应索引名的选择性
--
--
-- 3.1 因为存在复合索引,所以我们要取出复合索引中seq最大的哪个值
-- 这样取出的cardinality值才是最大的
--
mysql> select
-> table_schema, table_name, index_name,
-> max(seq_in_index) -- 取出最大的seq号后,选出index_name等信息
-> from
-> STATISTICS
-> group by table_schema , table_name , index_name\G
-- -----------省略其他输出-----------
*************************** 10. row ***************************
table_schema: burn_test
table_name: test_index_2
index_name: idx_mul_ab -- 这个是上次测试复合索引建立的index
max(seq_in_index): 2 -- 取出了最大的seq
-- -----------省略其他输出-----------
--
-- 3.2得到了最大的seq,从而可以取出对应的cardinality
--
mysql> select
-> table_schema, table_name, index_name, cardinality
-> from
-> STATISTICS
-> where
-> (table_schema , table_name, index_name, seq_in_index) in
-> (select
-> table_schema, table_name,
-> index_name, max(seq_in_index)
-> from
-> STATISTICS
-> group by table_schema , table_name , index_name)\G
*************************** 1. row ***************************
table_schema: burn_test
table_name: Orders
index_name: PRIMARY
cardinality: 5
*************************** 2. row ***************************
table_schema: burn_test
table_name: Orders_MV
index_name: product_name
cardinality: 3
*************************** 3. row ***************************
table_schema: burn_test
table_name: child
index_name: par_ind
cardinality: 0
*************************** 4. row ***************************
table_schema: burn_test
table_name: parent
index_name: PRIMARY
cardinality: 1
*************************** 5. row ***************************
table_schema: burn_test
table_name: t4
index_name: PRIMARY
cardinality: 4
-- -----------省略其他输出-----------
--
-- 3.3 最后通过table_schema和table_name 让上述的信息和TABLES表进行关联
--
SELECT
t.TABLE_SCHEMA,t.TABLE_NAME,INDEX_NAME, CARDINALITY, TABLE_ROWS,
CARDINALITY/TABLE_ROWS AS SELECTIVITY -- 得到选择性
FROM
TABLES t, -- 查询的表一,TABLES
(
SELECT
table_schema,
table_name,
index_name,
cardinality
FROM STATISTICS
WHERE (table_schema,table_name,index_name,seq_in_index) IN (
SELECT
table_schema,
table_name,
index_name,
MAX(seq_in_index)
FROM
STATISTICS
GROUP BY table_schema , table_name , index_name )
) s -- 查询的表二,就是上面3.2的查询结果
WHERE
t.table_schema = s.table_schema -- 通过库关联
AND t.table_name = s.table_name -- 再通过表变量
AND t.table_schema = 'employees' -- 指定某一个库名
ORDER BY SELECTIVITY;
+--------------+--------------+------------+-------------+------------+------------+
| TABLE_SCHEMA | TABLE_NAME | index_name | cardinality | TABLE_ROWS | SELECTIVITY |
+--------------+--------------+------------+-------------+------------+------------+
| employees | dept_emp | dept_no | 8 | 330400 | 0.0000 |
| employees | salaries | PRIMARY | 286271 | 2760952 | 0.1037 |
| employees | dept_manager | dept_no | 9 | 24 | 0.3750 |
| employees | titles | PRIMARY | 296887 | 440887 | 0.6734 |
| employees | dept_emp | PRIMARY | 298761 | 330400 | 0.9042 |
| employees | titles | PRIMARY | 440166 | 440887 | 0.9984 |
| employees | salaries | PRIMARY | 2760952 | 2760952 | 1.0000 |
| employees | dept_manager | PRIMARY | 24 | 24 | 1.0000 |
| employees | titles | PRIMARY | 440887 | 440887 | 1.0000 |
| employees | departments | PRIMARY | 9 | 9 | 1.0000 |
| employees | employees | PRIMARY | 298124 | 298124 | 1.0000 |
| employees | dept_emp | PRIMARY | 330400 | 330400 | 1.0000 |
| employees | dept_manager | PRIMARY | 24 | 24 | 1.0000 |
| employees | departments | dept_name | 9 | 9 | 1.0000 |
+--------------+--------------+------------+-------------+------------+------------+
--
-- 通过最后一列的SELECTIVITY是否接近1,判断该索引创建是否合理
-- 注意:
-- Cardinality和table_rows的值,都是通过随机采样,预估得到的
-- 当analyze前后,Cardinality值相差较多,说明该索引是不应该被创建的(页上的记录数值分布不平均)
--
-- 推荐 SELECTIVITY 15% 以上是适合的
--
-- 索引使用情况
--
mysql> select * from x$schema_index_statistics limit 1\G
*************************** 1. row ***************************
table_schema: employees
table_name: employees
index_name: PRIMARY -- 索引名字
rows_selected: 300024 -- 读取的记录数
select_latency: 370177723990 -- 使用该索引读取时总的延迟时间370毫秒(单位是皮秒)
rows_inserted: 0 -- 插入的行数
insert_latency: 0
rows_updated: 0 -- 更新的行数
update_latency: 0
rows_deleted: 0
delete_latency: 0
1 row in set (0.00 sec)
-- 结合之前的SELECTIVITY和这里的数值,可以更好的判断索引是否合理
-- 重启后数据归0
索引是要排序的,建立索引越多,排序以及维护成本会很大,插入数据的速度会变慢,所以索引建立的多,不是仅仅是浪费空间,还会降低性能,增加磁盘 IO。
注意:MySQL 5.6 的版本 STATISTICS 数据存在问题,截止 5.6.28 仍然存在,官方定性为 Bug。在 MySQL 5.6 中使用 mysql.innodb_index_stats 得到索引的选择性(SELECTIVITY)。
通过 information_schema 数据库,找出用户的表中没有创建主键的表。
mysql> desc information_schema.TABLES; -- 表的元信息
+-----------------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+---------------------+------+-----+---------+-------+
| TABLE_CATALOG | varchar(512) | NO | | | |
| TABLE_SCHEMA | varchar(64) | NO | | | | -- DB 的名字
| TABLE_NAME | varchar(64) | NO | | | | -- TABLE的名字
| TABLE_TYPE | varchar(64) | NO | | | |
| ENGINE | varchar(64) | YES | | NULL | |
| VERSION | bigint(21) unsigned | YES | | NULL | |
| ROW_FORMAT | varchar(10) | YES | | NULL | |
| TABLE_ROWS | bigint(21) unsigned | YES | | NULL | |
| AVG_ROW_LENGTH | bigint(21) unsigned | YES | | NULL | |
| DATA_LENGTH | bigint(21) unsigned | YES | | NULL | |
| MAX_DATA_LENGTH | bigint(21) unsigned | YES | | NULL | |
| INDEX_LENGTH | bigint(21) unsigned | YES | | NULL | |
| DATA_FREE | bigint(21) unsigned | YES | | NULL | |
| AUTO_INCREMENT | bigint(21) unsigned | YES | | NULL | |
| CREATE_TIME | datetime | YES | | NULL | |
| UPDATE_TIME | datetime | YES | | NULL | |
| CHECK_TIME | datetime | YES | | NULL | |
| TABLE_COLLATION | varchar(32) | YES | | NULL | |
| CHECKSUM | bigint(21) unsigned | YES | | NULL | |
| CREATE_OPTIONS | varchar(255) | YES | | NULL | |
| TABLE_COMMENT | varchar(2048) | NO | | | |
+-----------------+---------------------+------+-----+---------+-------+
21 rows in set (0.00 sec)
mysql> desc information_schema.COLUMNS; -- 每个列的元信息
+--------------------------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------------------+---------------------+------+-----+---------+-------+
| TABLE_CATALOG | varchar(512) | NO | | | |
| TABLE_SCHEMA | varchar(64) | NO | | | | -- DB的名字
| TABLE_NAME | varchar(64) | NO | | | | -- TABLE的名字
| COLUMN_NAME | varchar(64) | NO | | | |
| ORDINAL_POSITION | bigint(21) unsigned | NO | | 0 | |
| COLUMN_DEFAULT | longtext | YES | | NULL | |
| IS_NULLABLE | varchar(3) | NO | | | |
| DATA_TYPE | varchar(64) | NO | | | |
| CHARACTER_MAXIMUM_LENGTH | bigint(21) unsigned | YES | | NULL | |
| CHARACTER_OCTET_LENGTH | bigint(21) unsigned | YES | | NULL | |
| NUMERIC_PRECISION | bigint(21) unsigned | YES | | NULL | |
| NUMERIC_SCALE | bigint(21) unsigned | YES | | NULL | |
| DATETIME_PRECISION | bigint(21) unsigned | YES | | NULL | |
| CHARACTER_SET_NAME | varchar(32) | YES | | NULL | |
| COLLATION_NAME | varchar(32) | YES | | NULL | |
| COLUMN_TYPE | longtext | NO | | NULL | |
| COLUMN_KEY | varchar(3) | NO | | | | -- 列的索引
| EXTRA | varchar(30) | NO | | | |
| PRIVILEGES | varchar(80) | NO | | | |
| COLUMN_COMMENT | varchar(1024) | NO | | | |
| GENERATION_EXPRESSION | longtext | NO | | NULL | |
+--------------------------+---------------------+------+-----+---------+-------+
21 rows in set (0.01 sec)
-- 这里使用TABLES中的table_schema,table_name 与 COLUMNS 中的table_schema,table_name做一次关联子查询
-- 不直接用COLUMNS中的table_schema和table_name是因为表中会含有多个列,
-- 这样COLUMNS表中的记录(Row)的table_schema和table_name会存在重复值,需要再次使用DISTINCT
-- 在MySQL中执行该语句
SELECT
table_schema, table_name
FROM
information_schema.TABLES --
WHERE
table_name NOT IN (
SELECT DISTINCT
TABLE_NAME
FROM
information_schema.COLUMNS
WHERE
COLUMN_KEY = 'PRI')
AND table_schema NOT IN ('mysql' , 'information_schema',
'sys', 'performance_schema');
+--------------+----------------------+
| table_schema | table_name |
+--------------+----------------------+
| burn_test | child |
| burn_test | comps_test1 |
| burn_test | comps_test2 |
| burn_test | t1 |
| burn_test | t2 |
| burn_test | t3 |
| burn_test | t5 |
| burn_test | test_1 |
| burn_test | test_222 |
| burn_test | test_ger1 |
| burn_test | test_ger2 |
| burn_test | test_ger3 |
| burn_test | test_index_1 |
| burn_test | test_index_2 |
| burn_test | test_left_join_1 |
| burn_test | test_left_join_2 |
| burn_test | test_proc_1 |
| burn_test | test_rank |
| burn_test | test_rank_2 |
| burn_test | test_union_1 |
| burn_test | test_union_2 |
| burn_test | view_rank |
| burn_test | view_rank_1 |
| dbt3 | time_statistics |
| employees | current_dept_emp |
| employees | dept_emp_latest_date |
+--------------+----------------------+
26 rows in set (0.06 sec)
-- 上述查询的结果就是数据库中没有主键的表
作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/ldtgin 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。