前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >MySQL科学计数法展示解惑

MySQL科学计数法展示解惑

作者头像
GreatSQL社区
发布于 2023-02-22 02:23:06
发布于 2023-02-22 02:23:06
89600
代码可运行
举报
运行总次数:0
代码可运行

* GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。

一、问题引入
二、代码跟踪
三、总结

一、问题引入

今天遇到一个很奇怪的问题,在MySQL客户端输入,用不同科学计数法表示的数值,展示效果却截然不同:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> select 1e+14,1e+15;
+-----------------+-------+
| 1e+14           | 1e+15 |
+-----------------+-------+
| 100000000000000 |  1e15 |
+-----------------+-------+

为什么都是用科学计数法,一个是用完全展开的形式表示,另外一个却变成用科学计数法来表示?

二、代码跟踪

我们知道,在MySQL中解析这类科学计数法的标识token,是通过BISON来进行词法和语法解析的,并最终转成Item类型,Item构造初始化的堆栈如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#0  Item_float::init (this=0x7fffe844e8c0, str_arg=0xe7f312c00e0f8 <error: Cannot access memory at address 0xe7f312c00e0f8>, length=21845) at /home/greatdb/sql/item.cc:7216
#1  0x0000555559260095 in Item_float::Item_float (this=0x7fff2c00dad8, pos=..., str_arg=0x7fff2c00dac8 "1.234e-14", length=9) at /home/greatdb/sql/item.h:5237
#2  0x00005555592503af in MYSQLparse (YYTHD=0x7fff2c001040, parse_tree=0x7fffe84506b0) at /home/greatdb/sql/sql_yacc.yy:16616
#3  0x0000555558ea2410 in THD::sql_parser (this=0x7fff2c001040) at /home/greatdb/sql/sql_class.cc:3149
#4  0x0000555558fe8126 in parse_sql (thd=0x7fff2c001040, parser_state=0x7fffe8450990, creation_ctx=0x0) at /home/greatdb/sql/sql_parse.cc:7391
#5  0x0000555558fe1f16 in dispatch_sql_command (thd=0x7fff2c001040, parser_state=0x7fffe8450990, update_userstat=false) at /home/greatdb/sql/sql_parse.cc:5293
#6  0x0000555558fd7969 in dispatch_command (thd=0x7fff2c001040, com_data=0x7fffe8451b70, command=COM_QUERY) at /home/greatdb/sql/sql_parse.cc:1994
#7  0x0000555558fd5cd9 in do_command (thd=0x7fff2c001040) at /home/greatdb/sql/sql_parse.cc:1442
#8  0x00005555591fffc6 in handle_connection (arg=0x555560992ff0) at /home/greatdb/sql/conn_handler/connection_handler_per_thread.cc:307
#9  0x000055555ae2314b in pfs_spawn_thread (arg=0x5555608507a0) at /home/greatdb/storage/perfschema/pfs.cc:2899
#10 0x00007ffff77c3609 in start_thread (arg=<optimized out>) at pthread_create.c:477
#11 0x00007ffff76e8133 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

我们仔细看下面这个函数的实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void Item_float::init(const char *str_arg, uint length) {
  int error;
  const char *end_not_used;
  //经过观察,我们发现:不管是1e+14还是1e+15,这里把字符串转成浮点数,都是完全展开形式。
  //即1e+14展开为100000000000000
  //1e+15展开为1000000000000000
  //说明至少到目前为止,没有触发展示差异的原因。
  value = my_strntod(&my_charset_bin, str_arg, length, &end_not_used, &error);
  if (error) {
    char tmp[NAME_LEN + 1];
    snprintf(tmp, sizeof(tmp), "%.*s", length, str_arg);
    my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "double", tmp);
  }
  presentation.copy(str_arg, length);
  item_name.copy(str_arg, length);
  set_data_type(MYSQL_TYPE_DOUBLE);
  decimals = (uint8)nr_of_decimals(str_arg, str_arg + length);
  max_length = length;
  fixed = true;
}

接着我们继续查看Item处理结果的堆栈信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#0  my_gcvt (x=100000000000000, type=MY_GCVT_ARG_DOUBLE, width=342, to=0x7fffe844ec60 "\033", error=0x0) at /home/greatdb/strings/dtoa.cc:428
#1  0x00005555594ba4d5 in floating_point_to_text (value=100000000000000, decimals=31, gcvt_arg_type=MY_GCVT_ARG_DOUBLE, buffer=0x7fffe844ec60 "\033") at /home/greatdb/sql/protocol_classic.cc:3539
#2  0x00005555594ba53d in store_floating_point (value=100000000000000, decimals=31, zerofill=0, gcvt_arg_type=MY_GCVT_ARG_DOUBLE, packet=0x7fff2c003148) at /home/greatdb/sql/protocol_classic.cc:3558
#3  0x00005555594ba703 in Protocol_text::store_double (this=0x7fff2c005600, from=100000000000000, decimals=31, zerofill=0) at /home/greatdb/sql/protocol_classic.cc:3579
#4  0x0000555558aa2260 in Item::send (this=0x7fff2c021f90, protocol=0x7fff2c005600, buffer=0x7fffe844ef00) at /home/greatdb/sql/item.cc:7625
#5  0x0000555558ea1886 in THD::send_result_set_row (this=0x7fff2c001040, row_items=const mem_root_deque<Item*> & = {...}) at /home/greatdb/sql/sql_class.cc:2975
#6  0x0000555558ddbf7d in Query_result_send::send_data (this=0x7fff2c022c88, thd=0x7fff2c001040, items=const mem_root_deque<Item*> & = {...}) at /home/greatdb/sql/query_result.cc:109
#7  0x000055555912c6e3 in Query_expression::ExecuteIteratorQuery (this=0x7fff2c021408, thd=0x7fff2c001040) at /home/greatdb/sql/sql_union.cc:1282
#8  0x000055555912ca74 in Query_expression::execute (this=0x7fff2c021408, thd=0x7fff2c001040) at /home/greatdb/sql/sql_union.cc:1342
#9  0x0000555559069670 in Sql_cmd_dml::execute_inner (this=0x7fff2c022c50, thd=0x7fff2c001040) at /home/greatdb/sql/sql_select.cc:827
#10 0x00005555590689ec in Sql_cmd_dml::execute (this=0x7fff2c022c50, thd=0x7fff2c001040) at /home/greatdb/sql/sql_select.cc:580
#11 0x0000555558fe0324 in mysql_execute_command (thd=0x7fff2c001040, first_level=true) at /home/greatdb/sql/sql_parse.cc:4788
#12 0x0000555558fe24bb in dispatch_sql_command (thd=0x7fff2c001040, parser_state=0x7fffe8450990, update_userstat=false) at /home/greatdb/sql/sql_parse.cc:5393
#13 0x0000555558fd7969 in dispatch_command (thd=0x7fff2c001040, com_data=0x7fffe8451b70, command=COM_QUERY) at /home/greatdb/sql/sql_parse.cc:1994
#14 0x0000555558fd5cd9 in do_command (thd=0x7fff2c001040) at /home/greatdb/sql/sql_parse.cc:1442
#15 0x00005555591fffc6 in handle_connection (arg=0x555560992ff0) at /home/greatdb/sql/conn_handler/connection_handler_per_thread.cc:307
#16 0x000055555ae2314b in pfs_spawn_thread (arg=0x5555608507a0) at /home/greatdb/storage/perfschema/pfs.cc:2899
#17 0x00007ffff77c3609 in start_thread (arg=<optimized out>) at pthread_create.c:477
#18 0x00007ffff76e8133 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

我们发现不管是1e+14还是1e+15,最终都会进入到my_gcvt这个函数里面,此函数大概的功能是把一个浮点数类型转成对应的字符串类型输出。现在我们来看该函数的关键逻辑实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//1、x表示要转换的浮点数
//2、width表示展示宽度。具体来说:MySQL其实对数据库返回的每一个列的最大宽度是有要求的。如果浮点数是完全展开的情况,那就有可能超过这个展示的最大宽度,这里固定取值342
//3、to表示存放转换后浮点数x对应的字符数组指针
size_t my_gcvt(double x, my_gcvt_arg_type type, int width, char *to,
               bool *error) {
  int decpt, sign, len, exp_len;
  char *res, *src, *end, *dst = to, *dend = dst + width;
  char buf[DTOA_BUFF_SIZE];
  bool have_space, force_e_format;
  assert(width > 0 && to != nullptr);
 ...
 //这里主要是为了计算浮点数x的存储的小数点位置decpt。
 //比如100000000000000,得到的decpt为15
 //1000000000000000,得到的decpt为16
  res =
      dtoa(x, 4, type == MY_GCVT_ARG_DOUBLE ? width : std::min(width, FLT_DIG),
           &decpt, &sign, &end, buf, sizeof(buf));
 ...
 //have_space为真表示,浮点数完全展开的形式对应的长度,符合MySQL列展示最大宽度的要求。
 //下面这个判断的逻辑主要是说:
 //浮点数的展示宽度足够或者展示宽度不足的情况下,继续判断小数点位置decpt是否介于[-MAX_DECPT_FOR_F_FORMAT + 1,MAX_DECPT_FOR_F_FORMAT]之间。
 //跟踪一下代码的宏定义,很容易发现MAX_DECPT_FOR_F_FORMAT定义的长度就是15。
 //那这样就是说decpt介于[-14,15]之间的时候,也是符合要求的。
  if ((have_space ||
       /*
         Not enough space, let's see if the 'f' format provides the most number
         of significant digits.
       */
       ((decpt <= width &&
         (decpt >= -1 || (decpt == -2 && (len > 1 || !force_e_format)))) &&
        !force_e_format)) &&

      /*
        Use the 'e' format in some cases even if we have enough space for the
        'f' one. See comment for MAX_DECPT_FOR_F_FORMAT.
      */
      (!have_space || (decpt >= -MAX_DECPT_FOR_F_FORMAT + 1 &&
                       (decpt <= MAX_DECPT_FOR_F_FORMAT || len > decpt)))) {
  //如果符合这个条件的要求,那浮点数x就按照'f' format,即非科学计数法,完全展开形式处理。
  //1e+14的decpt取值为15,介于[-14,15]区间,故按照完全展开形式处理。
  ...
 }else{
  //否则浮点数x按照'e' format,即科学计数法表示。
  //1e+15的decpt取值为16,超出[-14,15]区间,故按照科学计数法形式处理。
  ...
 }
}
三、总结

经过代码的调用分析,发现最终的结论和输入数据的现象相符。当我们在使用MySQL过程中,遇到问题的时候,不要慌乱。可以尝试从源码分析的角度作为切入点,从根源上理解这种现象触发的原因,更能进一步加深我们对数据库运行机制的了解和掌握。

Enjoy GreatSQL :)


《零基础学习MySQL》视频课程

戳此小程序即可直达B站

https://www.bilibili.com/video/BV1Da411W7Va?spm_id_from=333.999.0.0&vd_source=ae1951b64ea7b9e6ba11f1d0bbcff0e4


文章推荐:


关于 GreatSQL

GreatSQL是由万里数据库维护的MySQL分支,专注于提升MGR可靠性及性能,支持InnoDB并行查询特性,是适用于金融级应用的MySQL分支版本。

GreatSQL社区官网: https://greatsql.cn/

Gitee: https://gitee.com/GreatSQL/GreatSQL

GitHub: https://github.com/GreatSQL/GreatSQL

Bilibili:

https://space.bilibili.com/1363850082/video

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-11-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GreatSQL社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
MySQL客户端显示binary字符代码改造
MySQL最新版本有一个新功能,在使用客户端的时候,最后加上--skip-binary-as-hex选项可以直接显示二进制值对应的字符串,不加该选项就可以按照原来的设置格式显示。先来看一下以下的varbinary的显示例子。
GreatSQL社区
2023/02/23
1K0
[转载] MySQL 浮点型的显示问题
出来的内容如下,我们看到浮点数1e-15用正常的数值来表示,1e-16用科学技术法来表示。
王文安@DBA
2021/06/21
3.2K0
[转载] MySQL 浮点型的显示问题
MySQL 存储过程运行的内存管理
MySQL的存储过程在运行过程中的内存管理跟table等运行时候是不一样的,它涉及多层内存管理,在开发时候如果不注意内存管理很容易造成内存泄露。接下来我用以下function的例子来说明,procedure的也是类似的,只是少了return result的过程。
GreatSQL社区
2023/02/23
1.7K0
MySQL中dd::columns表结构转table过程以及应用
MySQL的dd表是用来存放表结构和各种建表信息的,客户端建的表都存在mysql.table和mysql.columns表里,还有一个表mysql.column_type_elements比较特殊,用来存放SET和ENUM类型的字段集合值信息。看一下下面这张表的mysql.columns表和mysql.column_type_elements信息。为了缩短显示长度,这里只展示几个重要的值。
GreatSQL社区
2023/02/23
8560
MySQL源码分析之SQL函数执行
一条包含函数的SQL语句,在MySQL中会经过: 客户端发送,服务器连接,语法解析,语句执行的过程。
GreatSQL社区
2023/02/23
8710
如果MySQL事务中发生了网络异常?
在我们运维MySQL的时候,总会遇到各种情况导致程序和MySQL之间的会话异常中断,比如
用户1278550
2020/10/10
3.5K0
如果MySQL事务中发生了网络异常?
MySQL的match函数在sp中使用的BUG解析
在一次开发中在sp中使用MySQL PREPARE以后,使用match AGAINST语句作为prepare stmt的参数后,发现执行第二遍call会导致数据库crash,于是开始动手调查问题发生的原因。
GreatSQL社区
2023/08/11
2800
MySQL的match函数在sp中使用的BUG解析
MySQL:ERROR 1286 (42000): Unknown storage engine 'MyISAM'
我们环境中设置enforce_storage_engine为Innodb,sql_mode中设置了NO_ENGINE_SUBSTITUTION。
老叶茶馆
2020/06/24
1.1K0
从零开始学习MySQL调试跟踪(1)
有时为了跟踪故障需要调试MySQL/GreatSQL源码,本文介绍如何在Linux下构建MySQL/GreatSQL源码调试环境。
GreatSQL社区
2023/08/10
4970
从零开始学习MySQL调试跟踪(1)
从零开始学习MySQL调试跟踪(2)
程序运行过程中可能会异常终止或崩溃,OS会把程序挂掉时的内存状态记录下来,写入core文件,这就叫 coredump,通过gdb结合core文件可以方便地进行调试。
GreatSQL社区
2023/08/10
3290
从零开始学习MySQL调试跟踪(2)
MySQL中sp运行check表版本更新流程解析
MySQL的sp运行SQL语句需要执行2个步骤:prepare和execute。第一次执行的时候先执行prepare,进行相关语句parse、itemize、fix_fields等操作,然后才开始进行execute操作。等第二次再执行该sp的时候就直接运行execute而不需要再次进行重复的prepare操作,这样可以节省sp运行时候重复prepare的开销。但是,对于表操作就有一个问题产生,那就是如果执行第二遍的时候表的结构发生改变了,那么不进行reprepare而直接execute是会发生错误的。因此,本文章的目的在于寻找sp多次运行时候如何确认表版本更新并进行正确的操作。
GreatSQL社区
2023/02/22
1.1K0
MySQL报障之coredump收集处理流程
core文件是在程序出现异常退出时,保留的异常堆栈文件,能够帮助开发人员快速定位问题,故对于线上的程序,需要开启coredump功能。可以通过配置core-file或者coredumper开启coredump功能。
GreatSQL社区
2023/02/24
7490
MySQL慢查询记录原理和内容解析
本文并不准备说明如何开启记录慢查询,只是将一些重要的部分进行解析。如何记录慢查询可以自行参考官方文档:
用户2781897
2021/09/02
4.1K0
MySQL DBA如何利用strace/pstack/gdb来定位问题
strace是Linux环境下的一款程序调试工具,用来监察一个应用程序所使用的系统调用。 Strace是一个简单的跟踪系统调用执行的工具。在其最简单的形式中,它可以从开始到结束跟踪二进制的执行,并在进程的生命周期中输出一行具有系统调用名称,每个系统调用的参数和返回值的文本行。
老叶茶馆
2020/06/24
2.3K0
fsync操作
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/138197.html原文链接:https://javaforall.cn
全栈程序员站长
2022/09/04
4780
Percona 8.0.30中"show engine innodb status"导致coredump排查及分析
GreatSQL合并Percona-Server 8.0.30的Beta版测试中,QA报了一个crash的bug:
老叶茶馆
2023/09/01
2770
Percona 8.0.30中"show engine innodb status"导致coredump排查及分析
MySQL 的prepare使用中的bug解析过程
在一次开发中使用 MySQL PREPARE 以后,从 prepare 直接取 name 赋值给 lex->prepared_stmt_name 然后给 EXECUTE 用,发现有一定概率找不到 prepare stmt 的 name,于是开始动手调查问题发生的原因。
GreatSQL社区
2022/05/07
7160
时间精度引起MySQL主从不一致问题剖析
1. 主从数据不一致          近日接报某实例一个datetime字段主从数据不一致,其它数据暂未发现异常。第一反应可能是人为修改,如果用户有高权限帐号,是可以做到的,但检查所有帐号权限排除了这种可能。难道有黑客入侵?神经一下绷紧,仔细排查各种系统状态,很快也排除了这种可能。同时分析业务类型,有问题的值都是从机都是比主机少一秒,时间戳被改小一秒不能带来任何收益,被非法篡改的可能性基本排除。 2. 初步分析          对比数据发现从机比主机少一秒的数据经常出现,但主从复制状态一直正常,主机b
腾讯数据库技术
2018/11/29
2.6K0
时间精度引起MySQL主从不一致问题剖析
GreatSQL的sp中添加新的sp_instr引入的bug解析
GreatSQL社区
2024/05/11
1350
GreatSQL的sp中添加新的sp_instr引入的bug解析
利用GDB调试 MSQL
啃完O'reilly的《高性能mysql》、姜老师的《MySQL技术内幕》,再加上个2,3年的实战经验,就基本可以成为一名能独立处理问题的DBA了。但有些时候遇到些很刁钻的疑难杂症的话,那就束手无策了。
老叶茶馆
2020/09/07
2.9K0
利用GDB调试 MSQL
推荐阅读
相关推荐
MySQL客户端显示binary字符代码改造
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验