Redis里头浮点数咋存储和用,有啥坑和注意的地方分享一下
- 问答
- 2025-12-29 20:46:58
- 6
Redis本身没有专门为浮点数设计一个独立的数据类型,在Redis里头,浮点数主要是用两种方式来存储和操作的,这两种方式底层差别挺大,用的时候得留心。
第一种方式,也是最主要的方式,是把浮点数当成字符串来存。
当你使用最常见的字符串命令,SET 一个键值对,如果你设置的值看起来像个浮点数,SET temperature 36.5,那么Redis在内部就是把 "36.5" 这个字符串存起来,它不会去解析这里面是不是个数字,就当是一串字符。

那怎么对它们做计算呢?Redis提供了一组专门的命令,主要就是 INCRBYFLOAT,这命令很关键,它能识别出字符串里存的数字,并进行浮点数的加减法,比如你 INCRBYFLOAT temperature 0.5,它会先把 "36.5" 转换成数字36.5,加上0.5得到37.0,然后再把结果 "37"(注意,这里整数就不会带小数点了)存回字符串里去。
除了 INCRBYFLOAT,还有一些命令也能处理浮点数,HINCRBYFLOAT 用于哈希字段,ZADD 命令给有序集合成员设置分值(score)时,分值也是以双精度浮点数的形式来处理的,根据Redis官方文档(来源:Redis documentation on Data Types)的说明,有序集合的分值虽然是浮点数,但实际存储时Redis会尽力保证其精度。

第二种方式,是使用Redis的模块,比如RedisBloom。
有些Redis模块提供了真正的概率数据结构,比如布隆过滤器,在这些数据结构内部,它们可能会用纯C语言的双精度浮点数(double)类型来直接表示概率值之类的参数,但这属于模块自己的实现细节,和我们平时用Redis核心数据类型的体验不一样,我们平常说的Redis存浮点数,指的基本都是第一种方式。

接下来重点说说这么存有啥坑和要注意的地方。
最大的坑就是精度问题,因为Redis在处理浮点数计算时,底层用的是C语言的long double类型来进行中间运算,但最终存储回字符串时,默认会转换成17位小数精度的字符串表示,这听起来精度很高了,但只要是浮点数运算,就逃不开IEEE 754标准带来的经典精度损失问题。
举个例子(来源:基于IEEE 754浮点数运算的普遍问题),你可能执行 INCRBYFLOAT key 0.1,连续加十次,期望得到1.0,但实际结果可能是类似0.9999999999999999这样的数,如果你用这个值去做等于判断,比如判断是否等于1.0,那就会出错,这是所有编程语言中浮点数的通病,不是Redis独有的,但在Redis里因为你不能直接控制数据类型,所以更容易忽略。
那该怎么应对呢?
- 避免直接比较:不要直接用
GET拿到两个浮点数字符串后,在客户端用去判断是否相等,应该用计算后的差值,和一个极小的误差值(比如1e-10)比较,如果差值小于这个误差,就认为是相等的,或者,如果业务允许,可以考虑用Redis的位图功能来存。 - 考虑缩放成整数:这是最常用、最有效的办法,比如你要处理金额,别存
34元,而是存1234代表12.34元,单位是分,这样所有计算都在整数层面进行,完全没有精度问题,计算完后,需要展示的时候再除以100转换成元,这需要前期设计时规划好。 - 注意序列化:如果你是把一个复杂的对象(比如一个包含浮点数字段的JSON)整个序列化成字符串存到Redis里,那么Redis是完全不知道里面有个浮点数的,所有的精度问题都发生在你的应用程序序列化和反序列化的过程中,和Redis本身无关了,这时候精度问题得在你的程序代码里解决。
- 类型混淆风险:因为你存的是字符串,所以如果你不小心用错了命令,比如对同一个键使用了
INCRBY(整数加法)和INCRBYFLOAT(浮点数加法),会导致Redis报错,因为INCRBY要求原来的值必须能被解析成整数,所以要保持操作命令的一致性。 - 性能考量:虽然浮点数计算本身很快,但
INCRBYFLOAT这样的命令背后,是Redis先要把字符串解析成浮点数,计算完再序列化回字符串的过程,如果这个操作非常频繁,比如每秒几十万次,它肯定比纯整数的INCR命令开销要大一些(因为整数在Redis内部有优化表示),但对于绝大多数场景,这个差别可以忽略不计。
总结一下就是,Redis存浮点数本质上就是存字符串,用专门命令计算,你一定要清醒地意识到精度这个天坑,最好的避坑方法就是尽量用整数缩放来替代直接的浮点数存储和运算,如果非用不可,那在比较和显示时就要做好精度处理。
本文由歧云亭于2025-12-29发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://waw.haoid.cn/wenda/70861.html
