double ,FLOAT还是double(m,n)--深入解析MySQL数据库中双精度浮点数的使用

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 本文探讨了在MySQL中使用`float`和`double`时指定精度和刻度的影响。对于`float`,指定精度会影响存储大小:0-23位使用4字节单精度存储,24-53位使用8字节双精度存储。而对于`double`,指定精度和刻度对存储空间没有影响,但可以限制数值的输入范围,提高数据的规范性和业务意义。从性能角度看,`float`和`double`的区别不大,但在存储空间和数据输入方面,指定精度和刻度有助于优化和约束。

1 float, float(m), float(m,n)

在做MySQL开发时,使用到double变量时,有的开发会指定精度,写成这样double(10),或者这样double(10,1),前者指定数字的precision(翻译成精度,指数值中所有有效数字的数量),后者除了执行数值的precision之外,还指定数值的scale(翻译成范围或者是刻度,指定数值中小数点之后的位数)。也有的开发者不指定精度,直接写成double。这两种写法除了业务上的需要之外,还有别的区别吗?

这两种写法在性能上基本上没有什么差别,就是floatdouble相比,在性能上也没有很大的差别,因为在计算时,float(单精度浮点数)也是作为double来计算的(这里忽略因数据存储造成的性能差异,比如每个索引页存储的键的数量)。

除了性能因素外,这两者在存储上可能会有所不同,MySQL官方文档中有一段说到是否指定precisionfloat的影响:

For FLOAT, the SQL standard permits an optional specification of the precision (but not the range of the exponent) in bits following the keyword FLOAT in parentheses, that is, FLOAT. MySQL also supports.this optional precision specification, but the precision value in FLOAT, is used only to determine storage size. A precision from 0 to 23 results in a 4-byte single-precision FLOAT column. A precision from 24 to 53 results in an 8-byte double-precision DOUBLE column。

上面的官方文档说的时float,简单翻译一下

对于float,指定precision 只决定存储的大小,precision 值为0至23使用4字节单精度存储,precision 值为23至53 使用双精度8字节存储。

这段话应该怎么理解,用一个例子来说明一下比较直观:

mysql> create table t_f(id int, num1 float, numb2 float(12), num3 
                        float(26), num4 float(12, 1), num5 float(32, 1));
Query OK, 0 rows affected, 2 warnings (0.15 sec)
mysql> desc t_f;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int         | YES  |     | NULL    |       |
| num1  | float       | YES  |     | NULL    |       |
| numb2 | float       | YES  |     | NULL    |       |
| num3  | double      | YES  |     | NULL    |       |
| num4  | float(12,1) | YES  |     | NULL    |       |
| num5  | float(32,1) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
6 rows in set (0.00 sec)
mysql> insert into t_f values (1, 2222222222222222222222222,222222222,1111.234,1111111111,111111111);
Query OK, 1 row affected (0.02 sec)
mysql> select * from t_f;
+------+------------+-----------+----------+--------------+-------------+
| id   | num1       | numb2     | num3     | num4         | num5        |
+------+------------+-----------+----------+--------------+-------------+
|    1 | 2.22222e24 | 222222000 | 1111.234 | 1111111168.0 | 111111112.0 |
+------+------------+-----------+----------+--------------+-------------+
1 row in set (0.00 sec)

创建表时使用的float(12)类型,在用desc显示表的信息时显示为float,float(25),直接显示为double,可以看出,只指定精度,MySQL数据库会根据指定的精度选择float或者double类型。如果时同时指定精度和刻度呢,加一列看一下:

mysql> alter table t_f add salary float(10, 2);
Query OK, 0 rows affected, 1 warning (0.09 sec)
Records: 0  Duplicates: 0  Warnings: 1
------
mysql> alter table t_f add last_name varchar(10);
Query OK, 0 rows affected (0.09 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> desc t_f;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| id        | int         | NO   | PRI | NULL    |       |
| name      | varchar(10) | YES  |     | NULL    |       |
------------省略多行------------------------------------
| salary    | float(10,2) | YES  |     | NULL    |       |
| last_name | varchar(10) | YES  |     | NULL    |       |
+-----------+-------------+------+-----+---------+-------+
10 rows in set (0.00 sec)

在指定scale的情况下,用desc显示表的信息时就是我们创建表时的指定的类型。

2 double还是double(m),double(m,n)

mysql> create table t_d_n(id double(20,1), d1 double(20,4), d2 double(20,0));
Query OK, 0 rows affected, 3 warnings (0.14 sec)

上面的说法是针对float的,对double来说,指定scale对存储的影响则没有说明,下面通过MySQL数据文件的转储文件来验证一下。MySQL数据库的版本如下

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.32    |
+-----------+
1 row in set (0.00 sec)

使用验证表的信息如下

mysql> desc t_f;
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| id     | int         | NO   | PRI | NULL    |       |
| name   | varchar(10) | YES  |     | NULL    |       |
| salary | double      | YES  |     | NULL    |       |
| dept   | varchar(10) | YES  |     | NULL    |       |
| comm   | double(4,1) | YES  |     | NULL    |       |
| city   | varchar(8)  | YES  |     | NULL    |       |
+--------+-------------+------+-----+---------+-------+
--表里面有如下数据--
mysql> select * from t_f;
+----+------+----------------+------+-------+--------+
| id | name | salary         | dept | comm  | city   |
+----+------+----------------+------+-------+--------+
|  1 | aaaa |           1200 | bbb  | 120.1 | aaaaaa |
|  2 | cccc | 12000000000000 | ddd  | 128.1 | aaaaaa |
+----+------+----------------+------+-------+--------+
2 rows in set (0.00 sec)

从上面显示的表的数据里可以看出,每行数据都是以字符串’aaaaa’结束,两个数字列键都有字符串做分隔。这么做的原因,是因为MySQL Innodb引擎对行的数据存储是连续的,每一行的数据按照列的顺序依次存储。这样我们在寻找列的数据时,通过字符串可以很简单的找的数据,而不用关注MySQL Innodb存储的细枝末节,做复杂的推算。使用下面的命令获得这个表的数据文件的转储。

[root@dbserver test]# hexdump -C -v t_f.ibd >t_f.txt
####查找字符串’aaaaaa’,找到每行数据,字符a的ASCII码是61,字符b的ASCII码是62,我们在转储文件里查找连续的6个61,使用下面的命令
[root@dbserver test]grep '61 61 61 61 61 61' t_f.txt -B 1
####查找字符串’aaaaaa’,找到每行数据,字符a的ASCII码是61,字符b的ASCII码是62,我们在转储文件里查找连续的6个61,使用下面的命令
[root@dbserver test]grep '61 61 61 61 61 61' t_f.txt -B 1
----这个命令的输出如下所示:
000100f0  20 28 85 61 61 61 61 00  00 00 00 00 c0 92 40 62  | (.aaaa.......@b|
00010100  62 62 66 66 66 66 66 06  5e 40 61 61 61 61 61 61  |bbfffff.^@aaaaaa|
00010110  06 03 04 00 01 40 00 28  ff 56 80 00 00 02 00 00  |.....@.(.V......|
00010120  00 00 24 48 02 00 00 01  20 28 a5 63 63 63 63 00  |..$H.... (.cccc.|
00010130  00 80 79 ef d3 a5 42 64  64 64 33 33 33 33 33 03  |..y...Bddd33333.|
00010140  60 40 61 61 61 61 61 61  00 00 00 00 00 00 00 00  |`@aaaaaa........|

第一行数据以字符串’aaaa’开始,字符串’aaaaaa’结束,sal和comm列之间以3个字符b(62)隔开,十六进制转储如下:

20 28 85 61 61 61 61 00 00 00 00 00 c0 92 40 62 62 62 66 66 66 66 66 06 5e 40 61 61 61 61 61 61

从上面的转储可以看到,salary的值1200,comm的值120.1,都占了8个字节的存储空间

第二行以字符串’cccc’开始,以字符串’aaaaaa’结束,列之间以’dddd’隔开,十六进制转储如下:

63 63 63 63 00 00 80 79 ef d3 a5 42 64 64 64 33 33 33 33 33 03 60 40 61 61 61 61 61 61

同样,从上面的转储看到salary的值12000000000000,comm的值128.1 也都占用的8个字节的存储空间。

从上面简单的实验可以看到,写成double或者是double(m,n),占用的存储空间都是8个字节。从效率和占用的存储空间来说,这两种写法没有任何差别。

既然从性能和存储空间来说,这两种写法没有任何区别,MySQL为何还要提供double(m),和doulbe (m,n)两种写法,我觉得有两个原因,一个是适应用户的习惯用法,另一个是对数据的输入和显示进行限制,这个在下一小节讲。

3 从开发的角度看double和double(m),double(m,n)

CREATE TABLE `t_d_1` (
 `id` double NOT NULL,
 `name` varchar(10) DEFAULT NULL,
 `salary` double DEFAULT NULL,
 `dept` varchar(10) DEFAULT NULL,
 `comm` double(4,1) DEFAULT NULL,
 `city` varchar(8) DEFAULT NULL,
 PRIMARY KEY (`id`)
)
--要注意的是double类型在MySQL 8.0.32 版本中不支持只指定precision的写法,如double(20)
create table t_d_n(id int not null, d1 double,d2 double(20));
                    ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '))' at line 1
--象下面这样写就不会报错了
create table t_d_m(id int not null,d1 double,d2 double(20,1));
                    Query OK, 0 rows affected, 1 warning (0.31 sec)
--也不能在表中加入写成象double(20)形式的列
alter table t_d_m add d1 double(20);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1

写成double(precision,scale)的形式一是有形式或这规范上的意义,一眼可以看出这个数据的取值范围和精确度,另外一是一种约束,超过指定范围的数值会被拒绝。

mysql> create table t_f(id int, num4 double(4, 2));
Query OK, 0 rows affected, 1 warning (0.18 sec)
--形式不重要,但是输入的数值应该在取值范围之内
mysql> insert into t_f values (1, 12.321);
Query OK, 1 row affected (0.03 sec)

mysql> insert into t_f values (1, 99.99);
Query OK, 1 row affected (0.03 sec)
--超出取值范围,语句执行报错
mysql> insert into t_f values (1, 100.0);
ERROR 1264 (22003): Out of range value for column 'num4' at row 1

4 小结一下

从数据库性能的角度来看,double写成哪种形式基本没影响,对float来说,在1-23为之间,最好指定precision,这个会减少一般存储空间,进而对表及索引的性能会有一定的影响。但是从开发的角度看,数字后面加上precision和scale就十分有必要了,一方面可以限制数值的输入范围,一方面也有业务方面的意义。

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
22天前
|
SQL 关系型数据库 MySQL
深入解析MySQL的EXPLAIN:指标详解与索引优化
MySQL 中的 `EXPLAIN` 语句用于分析和优化 SQL 查询,帮助你了解查询优化器的执行计划。本文详细介绍了 `EXPLAIN` 输出的各项指标,如 `id`、`select_type`、`table`、`type`、`key` 等,并提供了如何利用这些指标优化索引结构和 SQL 语句的具体方法。通过实战案例,展示了如何通过创建合适索引和调整查询语句来提升查询性能。
120 9
|
2月前
|
SQL 数据挖掘 测试技术
南大通用GBase8s数据库:LISTAGG函数的解析
南大通用GBase8s数据库:LISTAGG函数的解析
|
2月前
|
SQL 存储 Oracle
南大通用GBase 8s数据库游标变量解析:提升数据库操作效率
南大通用GBase 8s 数据库游标变量解析:提升数据库操作效率
|
2月前
|
监控 关系型数据库 MySQL
MySQL自增ID耗尽应对策略:技术解决方案全解析
在数据库管理中,MySQL的自增ID(AUTO_INCREMENT)属性为表中的每一行提供了一个唯一的标识符。然而,当自增ID达到其最大值时,如何处理这一情况成为了数据库管理员和开发者必须面对的问题。本文将探讨MySQL自增ID耗尽的原因、影响以及有效的应对策略。
163 3
|
2月前
|
存储 关系型数据库 MySQL
MySQL 字段类型深度解析:VARCHAR(50) 与 VARCHAR(500) 的差异
在MySQL数据库中,`VARCHAR`类型是一种非常灵活的字符串存储类型,它允许存储可变长度的字符串。然而,`VARCHAR(50)`和`VARCHAR(500)`之间的差异不仅仅是长度的不同,它们在存储效率、性能和使用场景上也有所不同。本文将深入探讨这两种字段类型的区别及其对数据库设计的影响。
96 2
|
2月前
|
存储 关系型数据库 MySQL
PHP与MySQL动态网站开发深度解析####
本文作为技术性文章,深入探讨了PHP与MySQL结合在动态网站开发中的应用实践,从环境搭建到具体案例实现,旨在为开发者提供一套详尽的实战指南。不同于常规摘要仅概述内容,本文将以“手把手”的教学方式,引导读者逐步构建一个功能完备的动态网站,涵盖前端用户界面设计、后端逻辑处理及数据库高效管理等关键环节,确保读者能够全面掌握PHP与MySQL在动态网站开发中的精髓。 ####
|
2月前
|
SQL Java 数据库连接
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
|
16天前
|
存储 Oracle 关系型数据库
数据库传奇:MySQL创世之父的两千金My、Maria
《数据库传奇:MySQL创世之父的两千金My、Maria》介绍了MySQL的发展历程及其分支MariaDB。MySQL由Michael Widenius等人于1994年创建,现归Oracle所有,广泛应用于阿里巴巴、腾讯等企业。2009年,Widenius因担心Oracle收购影响MySQL的开源性,创建了MariaDB,提供额外功能和改进。维基百科、Google等已逐步替换为MariaDB,以确保更好的性能和社区支持。掌握MariaDB作为备用方案,对未来发展至关重要。
41 3
|
16天前
|
安全 关系型数据库 MySQL
MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!
《MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!》介绍了MySQL中的三种关键日志:二进制日志(Binary Log)、重做日志(Redo Log)和撤销日志(Undo Log)。这些日志确保了数据库的ACID特性,即原子性、一致性、隔离性和持久性。Redo Log记录数据页的物理修改,保证事务持久性;Undo Log记录事务的逆操作,支持回滚和多版本并发控制(MVCC)。文章还详细对比了InnoDB和MyISAM存储引擎在事务支持、锁定机制、并发性等方面的差异,强调了InnoDB在高并发和事务处理中的优势。通过这些机制,MySQL能够在事务执行、崩溃和恢复过程中保持
45 3
|
16天前
|
SQL 关系型数据库 MySQL
数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog
《数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog》介绍了如何利用MySQL的二进制日志(Binlog)恢复误删除的数据。主要内容包括: 1. **启用二进制日志**:在`my.cnf`中配置`log-bin`并重启MySQL服务。 2. **查看二进制日志文件**:使用`SHOW VARIABLES LIKE 'log_%';`和`SHOW MASTER STATUS;`命令获取当前日志文件及位置。 3. **创建数据备份**:确保在恢复前已有备份,以防意外。 4. **导出二进制日志为SQL语句**:使用`mysqlbinlog`
59 2

推荐镜像

更多