首页 > 数据库 >MySQL 添加主键减少磁盘空间的使用

MySQL 添加主键减少磁盘空间的使用

时间:2024-02-18 17:35:14浏览次数:24  
标签:INDEX name 磁盘空间 MySQL table SIZE 主键 row

测试使用的版本:MySQL 8.0.32

时至今日的 MySQL 8.3.0,默认都是不需要在定义表的时候定义主键。不过 Group Replication 和 Percona XtraDB Cluster (PXC) 缺省不支持没有主键的表。表缺少主键会有很多负面的性能影响,最大的确定是影响复制的速度。

今天,来简单说说使用主键的另一个原因:磁盘上的数据大小!

 

创建一个示例表:

> show create table t\G
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `a` bigint NOT NULL,
  `b` bigint DEFAULT NULL,
  KEY `a` (`a`),
  KEY `b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

>

插入条 10000000 记录

DELIMITER //

CREATE PROCEDURE InsertRandomData()
BEGIN
    DECLARE i INT DEFAULT 0;
    WHILE i < 10000000 DO
        INSERT INTO t (a, b)
        VALUES
            (i+1, FLOOR(RAND() * 4294967295));
        SET i = i + 1;
    END WHILE;
END //

DELIMITER ;

CALL InsertRandomData();

查看记录数和磁盘空间的占用情况

> select count(*) from t;
+----------+
| count(*) |
+----------+
| 10000000 |
+----------+
1 row in set (0.23 sec)

# ls -lh t.ibd
-rw-r----- 1 mysql mysql 936M Feb 17 18:13 t.ibd

磁盘使用的空间是 936M。

 

此时,表上没有主键,只创建了两个辅助索引。但是通过以下的查询,可以看到三个索引:

> select SPACE,INDEX_ID,i.NAME as index_name, t.NAME as table_name, CLUST_INDEX_SIZE, OTHER_INDEX_SIZE from information_schema.INNODB_INDEXES i JOIN information_schema.INNODB_TABLESPACES t USING(space) JOIN information_schema.INNODB_TABLESTATS ts WHERE t.NAME=ts.NAME AND t.NAME='abc/t'\G
*************************** 1. row ***************************
           SPACE: 27211
        INDEX_ID: 28360
      index_name: b
      table_name: abc/t
CLUST_INDEX_SIZE: 25340
OTHER_INDEX_SIZE: 30066
*************************** 2. row ***************************
           SPACE: 27211
        INDEX_ID: 28359
      index_name: a
      table_name: abc/t
CLUST_INDEX_SIZE: 25340
OTHER_INDEX_SIZE: 30066
*************************** 3. row ***************************
           SPACE: 27211
        INDEX_ID: 28358
      index_name: GEN_CLUST_INDEX
      table_name: abc/t
CLUST_INDEX_SIZE: 25340
OTHER_INDEX_SIZE: 30066
3 rows in set (0.17 sec)

>

这是因为多了一个 GEN_CLUST_INDEX 索引。

 

接下来,尝试将二级辅助索引改成主键

> select count(DISTINCT(a)) from t;
+--------------------+
| count(DISTINCT(a)) |
+--------------------+
|           10000000 |
+--------------------+
1 row in set (7.60 sec)

> alter table t add primary key(a), drop key a;
Query OK, 0 rows affected (53.84 sec)
Records: 0  Duplicates: 0  Warnings: 0

> show create table t\G
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `a` bigint NOT NULL,
  `b` bigint DEFAULT NULL,
  PRIMARY KEY (`a`),
  KEY `b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

>

再次查看磁盘空间的占用情况

# ls -lh t.ibd
-rw-r----- 1 mysql mysql 680M Feb 18 10:35 t.ibd

只有 680M 的大小。

> select SPACE,INDEX_ID,i.NAME as index_name, t.NAME as table_name, CLUST_INDEX_SIZE, OTHER_INDEX_SIZE from information_schema.INNODB_INDEXES i JOIN information_schema.INNODB_TABLESPACES t USING(space) JOIN information_schema.INNODB_TABLESTATS ts WHERE t.NAME=ts.NAME AND t.NAME='abc/t'\G
*************************** 1. row ***************************
           SPACE: 27212
        INDEX_ID: 28362
      index_name: b
      table_name: abc/t
CLUST_INDEX_SIZE: 26750
OTHER_INDEX_SIZE: 15864
*************************** 2. row ***************************
           SPACE: 27212
        INDEX_ID: 28361
      index_name: PRIMARY
      table_name: abc/t
CLUST_INDEX_SIZE: 26750
OTHER_INDEX_SIZE: 15864
2 rows in set (0.15 sec)

>

 

隐藏的(内部)聚簇索引(GEN_CLUST_INDEX)与生成的不可见主键(GIPK)

每个 InnoDB 表都有一个聚簇键,因此不定义一个聚簇键不会节省任何磁盘空间,有时甚至恰恰相反,如上文所示。因此,即使有问题的表的现有列都不是唯一的,最好还是添加另一个唯一列作为主键。内部的 GEN_CLUST_INDEX 不会暴露给 MySQL 上层,只有 InnoDB 引擎知道它,因此它对复制速度毫无用处。因此,显式主键是更好的解决方案。

不过,如果由于传统应用程序的问题而无法添加新的 PK 列,那么还是应该使用隐形主键来强制执行主键。这样,你就能获得性能上的优势,同时,这种变化对应用程序也是透明的。

 

让我们看看它在实践中是如何运行的:

> set sql_require_primary_key=1;

> create table nopk (a int);
ERROR 3750 (HY000): Unable to create or change a table without a primary key, when the system variable 'sql_require_primary_key' is set. Add a primary key to the table or unset this variable to avoid this message. Note that tables without a primary key can cause performance problems in row-based replication, so please consult your DBA before changing this setting.

> set sql_generate_invisible_primary_key=1;

> create table nopk (a int);

> show create table nopk\G
*************************** 1. row ***************************
       Table: nopk
Create Table: CREATE TABLE `nopk` (
  `my_row_id` bigint unsigned NOT NULL AUTO_INCREMENT /*!80023 INVISIBLE */,
  `a` int DEFAULT NULL,
  PRIMARY KEY (`my_row_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

> select * from nopk;
+------+
| a    |
+------+
|  100 |
+------+

因此,应用程序根本不知道新列的存在。但如果需要,我们仍然可以使用它,例如,轻松地将表读取或写入分割成可预测的块:

mysql > select my_row_id,a from nopk;
+-----------+------+
| my_row_id | a    |
+-----------+------+
|         1 |  100 |
+-----------+------+

对于缺少主键的现有表,在执行 sql_require_primary_key 变量之前,最好先启用 sql_generate_invisible_primary_key,然后使用逻辑转储和还原重新创建数据。简单的表优化不会添加隐形 PK。无论如何,对于传统应用程序来说,不可见 PK 应该是一个双赢的解决方案。

 

总之:

- 检查一下改变索引类型是否能节省磁盘空间也许是值得的!

- 如果由于应用程序限制而无法添加主键,可以考虑使用隐形主键!

标签:INDEX,name,磁盘空间,MySQL,table,SIZE,主键,row
From: https://www.cnblogs.com/abclife/p/18018953

相关文章

  • centos7.6 安装Mysql5.7
    #安装Mysqlwgethttp://dev.mysql.com/get/mysql57-community-release-el7-8.noarch.rpmyumlocalinstallmysql57-community-release-el7-8.noarch.rpmrpm--importhttps://repo.mysql.com/RPM-GPG-KEY-mysql-2022yumrepolistenabled|grep"mysql.*-community.*"......
  • mysqldump备份
    前言:mysqldump是日常比较常用的一个工具了,在对数据库进行导出工作时,经常会用到mysqldump。本篇文章将介绍mysqldump工具的使用方法并分享几点备份技巧。1.mysqldump使用简介mysqldump是MySQL系统自带的逻辑备份工具,主要用于转储数据库。它主要产生一系列的SQL语句,可......
  • MySQL字符串截取总结:Left()、Right()、Substring()、Substring_index()
    在实际的项目开发中有时会有对数据库某字段截取部分的需求,这种场景有时直接通过数据库操作来实现比通过代码实现要更方便快捷些,mysql有很多字符串函数可以用来处理这些需求,如Mysql字符串截取总结:left()、right()、substring()、substring_index()。一.从左开始截取字符串用法:le......
  • mysql创建数据库排序规则utf8_general_ci和utf8_unicode_ci区别
    在编程语言中,通常用unicode对中文字符做处理,防止出现乱码,那么在MySQL里,为什么大家都使用utf8_general_ci而不是utf8_unicode_ci呢?ci是caseinsensitive,即"大小写不敏感",a和A会在字符判断中会被当做一样的;bin是二进制,a和A会别区别对待。例如你运行:SELECT*FR......
  • 25个常见的python系统设计源码(python+mysql+vue)
    收集整理了25个常见的python系统设计源码。可以用于课程作业或者毕业设计。所有系统都带源码和文档。1.网上商城系统这是一个基于python+vue开发的商城网站,平台采用B/S结构,后端采用主流的Python语言进行开发,前端采用主流的Vue.js进行开发。整个平台包括前台和后台两个部分。......
  • 基于python+django+mysql的小区物业管理系统
    该系统是基于python+django开发的小区物业管理系统。适用场景:大学生、课程作业、毕业设计。学习过程中,如遇问题可以在github给作者留言。主要功能有:业主管理、报修管理、停车管理、资产管理、小区管理、用户管理、日志管理、系统信息。演示地址http://wuye.gitapp.cn/admin后......
  • 【Python】使用Python库中的pymysql执行SQL
    使用Python连接数据库在使用petl执行SQL之前,我们需要先连接到数据库。这里以MySQL为例,我们可以使用pymysql库来连接MySQL数据库。首先,安装pymysql库:pipinstallpymysql然后,编写以下代码来连接MySQL数据库:importpymysqlfrompetlimport*#连接数据库conn=pymysql.co......
  • Docker 安装 Mysql5.7 容器
    1、首先拉取mysql5.7镜像dockerpullmysql:5.72、查询是否下载完成 查询所有镜像dockerimages3、创建mysql容器并启动dockerrun-d\#-d后台运行 -p3306:3306\#端口号映射到主机的端口号前面的端口号可以更改--namemysql\#启动容器的名字-eMYS......
  • MySQL——模糊查询
    MySQL——模糊查询语法结构:select字段名from表名where字段名(需要查询的字段)like‘’;单引号内是需要模糊查询的内容,填写内容如下:匹配任意多个字符:%匹配任意一个字符:_例如:找出名字中含有h的:selectnamefrom表名wherenamelike‘%h%’例如:找出名字第二个字......
  • Spring Boot + MyBatis-Plus 实现 MySQL 主从复制动态数据源切换
    MySQL主从复制是一种常见的数据库架构,它可以提高数据库的性能和可用性。动态数据源切换则可以根据业务需求,在不同场景下使用不同的数据源,比如在读多写少的场景下,可以通过切换到从库来分担主库的压力。在本文中,我们将介绍如何在SpringBoot中实现MySQL动态数据源切换,使用My......