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

简介: 本文探讨了在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就十分有必要了,一方面可以限制数值的输入范围,一方面也有业务方面的意义。

相关文章
|
4天前
|
存储 运维 安全
云上金融量化策略回测方案与最佳实践
2024年11月29日,阿里云在上海举办金融量化策略回测Workshop,汇聚多位行业专家,围绕量化投资的最佳实践、数据隐私安全、量化策略回测方案等议题进行深入探讨。活动特别设计了动手实践环节,帮助参会者亲身体验阿里云产品功能,涵盖EHPC量化回测和Argo Workflows量化回测两大主题,旨在提升量化投研效率与安全性。
云上金融量化策略回测方案与最佳实践
|
6天前
|
人工智能 自然语言处理 前端开发
从0开始打造一款APP:前端+搭建本机服务,定制暖冬卫衣先到先得
通义灵码携手科技博主@玺哥超carry 打造全网第一个完整的、面向普通人的自然语言编程教程。完全使用 AI,再配合简单易懂的方法,只要你会打字,就能真正做出一个完整的应用。
6012 18
|
18天前
|
人工智能 自动驾驶 大数据
预告 | 阿里云邀您参加2024中国生成式AI大会上海站,马上报名
大会以“智能跃进 创造无限”为主题,设置主会场峰会、分会场研讨会及展览区,聚焦大模型、AI Infra等热点议题。阿里云智算集群产品解决方案负责人丛培岩将出席并发表《高性能智算集群设计思考与实践》主题演讲。观众报名现已开放。
|
10天前
|
自然语言处理 数据可视化 API
Qwen系列模型+GraphRAG/LightRAG/Kotaemon从0开始构建中医方剂大模型知识图谱问答
本文详细记录了作者在短时间内尝试构建中医药知识图谱的过程,涵盖了GraphRAG、LightRAG和Kotaemon三种图RAG架构的对比与应用。通过实际操作,作者不仅展示了如何利用这些工具构建知识图谱,还指出了每种工具的优势和局限性。尽管初步构建的知识图谱在数据处理、实体识别和关系抽取等方面存在不足,但为后续的优化和改进提供了宝贵的经验和方向。此外,文章强调了知识图谱构建不仅仅是技术问题,还需要深入整合领域知识和满足用户需求,体现了跨学科合作的重要性。
|
6天前
|
人工智能 容器
三句话开发一个刮刮乐小游戏!暖ta一整个冬天!
本文介绍了如何利用千问开发一款情侣刮刮乐小游戏,通过三步简单指令实现从单个功能到整体框架,再到多端优化的过程,旨在为生活增添乐趣,促进情感交流。在线体验地址已提供,鼓励读者动手尝试,探索编程与AI结合的无限可能。
|
1月前
|
存储 人工智能 弹性计算
阿里云弹性计算_加速计算专场精华概览 | 2024云栖大会回顾
2024年9月19-21日,2024云栖大会在杭州云栖小镇举行,阿里云智能集团资深技术专家、异构计算产品技术负责人王超等多位产品、技术专家,共同带来了题为《AI Infra的前沿技术与应用实践》的专场session。本次专场重点介绍了阿里云AI Infra 产品架构与技术能力,及用户如何使用阿里云灵骏产品进行AI大模型开发、训练和应用。围绕当下大模型训练和推理的技术难点,专家们分享了如何在阿里云上实现稳定、高效、经济的大模型训练,并通过多个客户案例展示了云上大模型训练的显著优势。
|
10天前
|
Cloud Native Apache 流计算
PPT合集|Flink Forward Asia 2024 上海站
Apache Flink 年度技术盛会聚焦“回顾过去,展望未来”,涵盖流式湖仓、流批一体、Data+AI 等八大核心议题,近百家厂商参与,深入探讨前沿技术发展。小松鼠为大家整理了 FFA 2024 演讲 PPT ,可在线阅读和下载。
3543 10
PPT合集|Flink Forward Asia 2024 上海站
|
3天前
|
弹性计算 运维 监控
阿里云云服务诊断工具:合作伙伴架构师的深度洞察与优化建议
作为阿里云的合作伙伴架构师,我深入体验了其云服务诊断工具,该工具通过实时监控与历史趋势分析,自动化检查并提供详细的诊断报告,极大提升了运维效率和系统稳定性,特别在处理ECS实例资源不可用等问题时表现突出。此外,它支持预防性维护,帮助识别潜在问题,减少业务中断。尽管如此,仍建议增强诊断效能、扩大云产品覆盖范围、提供自定义诊断选项、加强教育与培训资源、集成第三方工具,以进一步提升用户体验。
612 242
|
23天前
|
人工智能 自然语言处理 前端开发
100个降噪蓝牙耳机免费领,用通义灵码从 0 开始打造一个完整APP
打开手机,录制下你完成的代码效果,发布到你的社交媒体,前 100 个@玺哥超Carry、@通义灵码的粉丝,可以免费获得一个降噪蓝牙耳机。
5954 16
|
5天前
|
消息中间件 人工智能 运维
12月更文特别场——寻找用云高手,分享云&AI实践
我们寻找你,用云高手,欢迎分享你的真知灼见!
504 37