MongoDB中关于64位整型存储解决方案

为什么80%的码农都做不了架构师?>>>   hot3.png

社区内一哥们@smcboy 提出关于php中操作MongoDB存储整数问题,找到点资料花点时间翻译过来,是个很好的学习方式。@红薯 那篇讨论我的修改回复,仍然没有更新可恶啊~!!说实话我就是高一英语水平 为了这篇文章我算是绞尽脑汁,翻译了大半天,累死我了。科学精神可贵、可贵!!

在我当前项目中大量是MongoDB,正在从传统RDBMS过度到key-value存储。Facebook中用户标识UserID使用64位Int数据类型存储,杯具的是 MongoDB的PHP驱动只支持32位整型数据,导致UserID被截断无法处理Facebook用户信息。

MongoDB数据采用BSON(Binary JSON)文档型存储,BSON有两种整型数据类型,1、32位有符号整型数据(INT); 2、64位有符号型整型数据(LONG)。由于PHP不支持大于8个字节整数,所以MongoDB PHP驱动只支持32位有符号整型数据存储。然而这样不是绝对的,在C类型 long 为64位平台上,PHP仍然可以正常支持64位整型数据; 除了在Windowns上,其他平台上C中long类型总是32位。


当PHP中整型存储到MongoDB中,PHP驱动会采用最低兼容原则用32位进行转换存储到MongoDB文档中。下面是测试案例(测试平台为 64位):

$m = new Mongo();
$c = $m->selectCollection('test', 'inttest');
$c->remove(array());
//插入大于32位数据
$c->insert(array('number' => 1234567890123456));$r = $c->findOne();
echo $r['number'], "\n";

输出:

int(1015724736)
二进制解析:
1234567890123456 = 1000110001011010101001111001000101010111010110000001015724736 =                      111100100010101011101011000000

上面可以看出数据已被截断,这显然不是我想要的。为了解决这个问题,从PHP中存储到MongoDB,我们可以采用原生的PHP整型数据。注意!不是去修改MongoDB相关驱动程序,而在PHP中配置一个简单参数 mongo.native_long ,从而避免大量应用程序改动。当 mongo.native_long 参数开启之后,我们可以看到如下不同的结果:

代码:

ini_set('mongo.native_long', 1);
$c->insert(array('number' => 1234567890123456));$r = $c->findOne();
var_dump($r['number']);
输出:

int(1234567890123456)

在64位平台中,PHP程序中配置mongo.native_long 允许使用完整64位整型存储到MongoDB,本例中这种方式存储到MongoDB中类型为BSON LONG, 如果未开启此配置则类型为BSON INT类型。该配置对从MongoDB读取数据到PHP中同样有效。如果关闭该配置,当从MongoDB取出数据时PHP驱动会把 BSON LONG 类型转换为PHP的double类型,造成精度损失。下面看个例子:

ini_set('mongo.native_long', 1);	//开启配置
$c->insert(array('number' => 12345678901234567));ini_set('mongo.native_long', 0);	//关闭配置
$r = $c->findOne();
var_dump($r['number']);
输出:

float(1.2345678901235E+16)
在32位平台中 mongo.native_log 参数配置不起任何作用,仍然会以BSON INT 类型存储。
然而当该配置开启时从Mongo中取出 BSON LONG类型数据,MongoCursorException 会提示关于精度损失问题。
MongoCursorException: Can not natively represent the long 1234567890123456 on this platform
当该配置关闭时 BSON LONG 数据,为了兼容PHP会把 BSON INT 转成float类型

尽管在64位平台上可以使用该配置mongo.native_long达到支持64位整型的目的,但是并没有提供32平台上的解决方案,去防止BSON LONG 数据的精度丢失问题,仅仅不负责任的抛出一个精度丢失的异常信息( 详情)。

工作中使用64位整位还是比较靠谱的,俺自己添加了两个类库 MongoInt32 和 MongoInt64,这两个类简单的封装了用字符串表示数字。使用方式: 

$int32 = new MongoInt32("32091231");
$int64 = new MongoInt64("1234567980123456");
使用该对象可以像正常使用插入、更新、查询等操作
例如:
$m = new Mongo();
$c = $m->selectCollection('test', 'inttest');
$c->remove(array());$c->insert(array('int32' => new MongoInt32("1234567890"),'int64' => new MongoInt64("12345678901234567"),
));$r = $c->findOne();
var_dump($r['int32']);
var_dump($r['int64']);
输出结果: 

int(1234567890)
float(1.2345678901235E+16)
可以看到对返回结果没任何改变。BSON INT类型仍然是 int型,BSON LONG 类型变为 double类型。如果我启用 mongo.native_long 配置,通过MongoInt64类库转换,在64位平台上,PHP中获取 BSON LONG 会返回正确int型,在32位平台上MongoCursorException会抛出提示信息。

为了在32位平台中,从MongoDB内取出 64位整型数据,需要配置另一个参数 mongo.long_as_object ,开启后,BSON LONG取出后以一个MongoInt64对象返回。
案例:
$m = new Mongo();
$c = $m->selectCollection('test', 'inttest');
$c->remove(array());$c->insert(array('int64' => new MongoInt64("12345678901234567"),
));ini_set('mongo.long_as_object', 1);
$r = $c->findOne();
var_dump($r['int64']);
echo $r['int64'], "\n";
echo $r['int64']->value, "\n";
输出:

object(MongoInt64)#7 (1) {["value"]=>string(17) "12345678901234567"
}
12345678901234567
12345678901234567
MongoInt32和MongoInt64 类基于对象的__toString()实现,所以返回的value值可以直接进行 echo,你只能获取一个整型字符串,所以请意识到MongoDB是类型敏感的,不会用对待字符串的方式对待数字,数字就是数字。
案例(64位平台):
ini_set('mongo.native_long', 1);$m = new Mongo();
$c = $m->selectCollection('test', 'inttest');
$c->remove(array());$nr = "12345678901234567";
$c->insert(array('int64' => new MongoInt64($nr)));$r = $c->findOne(array('int64' => $nr)); // $nr is a string here
var_dump($r['int64']);
$r = $c->findOne(array('int64' => (int) $nr));
var_dump($r['int64']);
输出:

NULL
int(12345678901234567)

下面列出关于不同的参数启用状态,整型转换情况:

PHP to  MongoDB (32位系统)

From PHP

Stored in Mongo

native_long=0

native_long=1

1234567

INT(1234567)

INT(1234567)

123456789012

FLOAT(123456789012)

FLOAT(123456789012)

MongoInt32("1234567")

INT(1234567)

INT(1234567)

MongoInt64("123456789012")

LONG(123456789012)

LONG(123456789012)

PHP to  MongoDB (64位系统):

From PHP

Stored in Mongo

native_long=0

native_long=1

1234567

INT(1234567)

LONG(1234567)

123456789012

garbage

LONG(123456789012)

MongoInt32("1234567")

INT(1234567)

INT(1234567)

MongoInt64("123456789012")

LONG(123456789012)

LONG(123456789012)

Mongo to PHP (32位系统)

Stored in Mongo

Returned to PHP as

long_as_object=0

long_as_object=1

native_long=0

native_long=1

INT(1234567)

int(1234567)

int(1234567)

int(1234567)

LONG(123456789012)

float(123456789012)

MongoCursorException

MongoInt64("123456789012")

Mongo to PHP (64位系统):

Stored in Mongo

Returned to PHP as

long_as_object=0

long_as_object=1

native_long=0

native_long=1

INT(1234567)

int(1234567)

int(1234567)

int(1234567)

LONG(123456789012)

float(123456789012)

int(123456789012)

MongoInt64("123456789012")

总结:
综上所述可以看到想获得64位的支持还是很棘手的,如果你只需要在64为平台上运行代码,我们推荐使用 mongo.native_long=1 配置参数。当整数存储到MongoDB,取出是仍然是整型数据,从而达到支持64位的目的。

如果你丫就是想要在32位平台(包含Windows 64位上的PHP),你没办法使用得到可靠的整型数据,必须使用MongoInt64 类来实现。这也会带来其他问题,如:你必须在初始化的时候处理字符串类型的数字。也要注意MongoDB Shell 将所有的数字作为float浮点型数据处理,这并不能代表64位整型数字,相反将作为浮点型数字。所有不要在shell模式下进行数据修改,这样会导致类型转换!!

案例:

$m = new Mongo();
$c = $m->selectCollection('test', 'inttest');
$c->remove(array());$c->insert(array('int64' => new MongoInt64("123456789012345678")));
MongoDB Shell模式下:
$ mongo
MongoDB shell version: 1.4.4
url: test
connecting to: test
type "help" for help
> use test
switched to db test
> db.inttest.find()
{ "_id" : ObjectId("4c5ea6d59a14ce1319000000"), "int64" : { "floatApprox" : 123456789012345680, "top" : 28744523, "bottom" : 2788225870 } }
当我们通过驱动获取支持64位数据,可以得到靠谱的结果:
ini_set('mongo.long_as_object', 1);
$r = $c->findOne();
var_dump($r['int64']);
输出:

object(MongoInt64)#7 (1) {["value"]=>string(18) "123456789012345678"
}

这个新函数方式将会在  mongo 1.0.9 release 版本中推出,可以通过PRCL  pecl install mongo 获取。

剩下的就靠命运了,祝你好运。

翻译:OSC民工

原文链接:http://derickrethans.nl/64bit-ints-in-mongodb.html

转载于:https://my.oschina.net/kisswu/blog/122338

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/547985.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

滑动窗口--单调队列

给定一个大小为 n≤106 的数组。 有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。 你只能在窗口中看到 k 个数字。 每次滑动窗口向右移动一个位置。 以下是一个例子: 该数组为 [1 3 -1 -3 5 3 6 7],k 为 3。 窗口位置 最小值…

CentOS7 源码编译安装MySQL8.0.15 shell脚本

使用MySQL8无需像MySQL5那样需要Boost依赖,和经过35-55分钟的等待编译完成,直接解压即可使用,方便快捷! 1,环境: 操作系统 CentOS Linux release 7.6.1810 (Core) 64位 服务器环境 “腾讯云”服务器…

切割图形_泉州泡沫景观字切割机厂家

泉州泡沫景观字切割机厂家 jz4rw0qv泉州泡沫景观字切割机厂家 巨源线条切割机同步带型结构合理、性能、精密度高、、操作简便、价格合理,比同行业同款机床更高,是原有同步带型泡沫切割机的替代产品。自动编程使用计算机利用配合切割机应用,只…

你的搜索其实很糟糕?

为什么80%的码农都做不了架构师?>>> 日期:2013-3-27 来源:GBin1.com 尽管你非常擅长搜索,但是很多时候搜索内容和你想要的并不吻合。事实上,用户体验专家Jakob Nielsen认为大多数人都非常的不擅长搜索。…

Element Tree型控件

效果 前端 <template><div class"app-container"><el-inputplaceholder"输入关键字进行过滤"、<! -- 双向绑定-- >v-model"filterText"></el-input><el-tree ref"tree":data"subjectList"…

快速根据注释生成接口文档网页工具——Apidoc的使用教程

环境&#xff1a; 操作系统 CentOS Linux release 7.6.1810 (Core) 64位 服务器环境 “腾讯云”服务器 1&#xff0c;安装Node.js的npm工具环境&#xff1a; 如有不懂&#xff0c;请看我的博客&#xff1a;CentOS7 源码编译安装NodeJS 最新版本 2&#xff0c;npm环境搭…

频段表_5G频段范围之:频段3.3GHz-4.2GHz (n77,n78)

本文版权归“5G通信(tongxin5g)”和5G哥所有&#xff0c;未经授权&#xff0c;请勿转载比起以前的移动通信网络&#xff0c;5G探索的新频谱范围包括&#xff1a;3.3GHz-4.2GHz&#xff0c;4.4GHz-5.0GHz&#xff0c;24.25-29.5 GHz今天主要看频段3.3GHz-4.2GHz在3GPP中&#xf…

Signals Slots(Qt5)

>Signal-Slot的作用是对象间的通信; Signals-Slots机制是Qt的核心特性, 也可能是Qt和其他大多数框架提供的特性不同的部分; 介绍 >GUI编程中, 当我们改变了一个widget,经常希望另一个widget能被通知到; 通常我们希望各种对象间能互相通信. Example: 用户点击了CLOSE按钮,…

公开说说别人看不到_当听到别人在说自己坏话时,心里是什么感受?

人有优点也有缺点这世界上&#xff0c;没有人的性格可以做到十全十美。没有任何一个人从头到尾都是完美无缺的。一个人自从慢慢的长大后&#xff0c;在不断的社交活动中&#xff0c;就会慢慢的观察别人身上的优点或者缺点了。很奇怪&#xff0c;人的这种能力和本领好像是不需要…

CentOS 7 利用Docker搭建Showdoc文档管理系统

1&#xff0c;系统环境 a&#xff0c;操作系统 CentOS Linux release 7.6.1810 (Core) 64位 b&#xff0c;确保Docker环境已经安装&#xff0c;具体教程请看 CentOS 安装docker Docker部署Showdoc官方教程&#xff1a;https://www.showdoc.cc/help?page_id65610 2&…

制作安装媒体来部署额外域控制器

cmd&#xff1a;1. ntdsutil2. activate instance ntds3. ifm4. create [full|rodc] d:\InstallMedia5. quit6. quit转载于:https://blog.51cto.com/babylater/1182185

求最大公约数和最小公倍数

// 求两数的最大公约数 默认 a>bpublic static int func(int a,int b){return a % b0?b:func(b,a%b);}// 求两数的最小公倍数 默认 a>bpublic static int func2(int a,int b){int m a * b;return m / func(a,b);}

深度学习attention原理_深度学习Anchor Boxes原理与实战技术

深度学习Anchor Boxes原理与实战技术目标检测算法通常对输入图像中的大量区域进行采样&#xff0c;判断这些区域是否包含感兴趣的目标&#xff0c;并调整这些区域的边缘&#xff0c;以便更准确地预测目标的地面真实边界框。不同的模型可能使用不同的区域采样方法。在这里&#…

Linux利用nginx-gridfs搭建部署Nginx和MongoDB文件服务器,支持用户密码验证!

nginx-gridfs是一个nginx的扩展模块&#xff0c;用于支持直接访问MongoDB的GridFS文件系统上的文件并提供 HTTP 访问 1&#xff0c;安装nginx&#xff0c;下载好安装包nginx和nginx-gridfs&#xff0c;此次安装采用nginx1.12.2. mkdir -p /data/soft/nginx-mongodb/ # 创建…

安装Exchange 2010 时报错UserMailbox 必须强制使用 Database

安装Exchange 2010 时报错"UserMailbox 必须强制使用 Database"故障描述&#xff1a;在原有Exchange Server 2010 SP1的环境安装新的服务器上报"UserMailbox 必须强制使用 Database。 属性名称: Database"错&#xff0c;详细如下&#xff1a;错误:运行&quo…

三行代码生成验证码并转换成base64

使用 Hutool 工具类 import cn.hutool.captcha.CaptchaUtil; import cn.hutool.captcha.LineCaptcha; import cn.hutool.core.io.FileUtil; import cn.hutool.core.lang.Console; import sun.misc.BASE64Encoder;import java.io.File; import java.io.FileInputStream; import…

android 垂直的开关_安卓布局:如何让这两个按钮水平垂直居中

代码&#xff1a; 代码&#xff1a;android:id"id/linearLayout1" android:layout_width"wrap_content" android:layout_height"wrap_content" android:layout_alignParentBottom"true" android:layout_alignParentLeft"true&quo…

docker 创建容器报: Error response from daemon: C: drive is not shared.

报错 C:\Program Files\Docker\Docker\Resources\bin\docker.exe: Error response from daemon: C: drive is not shared. Please share it in Docker for Windows Settings. See C:\Program Files\Docker\Docker\Resources\bin\docker.exe run --help.这时候我们需要绑定盘符…

CentOS 7 搭建swagger Api文档管理系统

1&#xff0c;系统环境 a&#xff0c;操作系统 CentOS Linux release 7.6.1810 (Core) 64位 b&#xff0c;安装Node.js的npm工具环境&#xff1a; # Node 官网已经把 linux 下载版本更改为已编译好的版本了&#xff0c;我们可以直接下载解压后使用&#xff1a; wget http…

【Jectpack 基础】Jetpack Compose 文本居中的实现方法

在Jetpack Compose中&#xff0c;实现文本居中显示是一个常见的需求。本文将介绍一种简单而有效的方法&#xff0c;通过使用Compose的Modifier和Alignment属性来轻松实现文本的居中显示。 问题背景&#xff1a; 在开发中&#xff0c;我们经常需要将文本在其容器中水平和垂直方…