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

社区内一哥们@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

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

  1. 整理C/C++中的64位整型

    本篇文章转载自:VC驿站:http://www.cctry.com/thread-277221-1-1.html 最近给大家录制讲解C++基础方面的教程,刚讲到数据类型这块.地址如下:http://w ...

  2. vc6 C/C++的64位整型

    :C/C++的64位整型 在C/C++中,64为整型一直是一种没有确定规范的数据类型.现今主流的编译器中,对64为整型的支持也是标准不一,形态各异.一般来说,64位 整型的定义方式有long long ...

  3. C/C++的64位整型

    在C/C++中,64为整型一直是一种没有确定规范的数据类型.现今主流的编译器中,对64为整型的支持也是标准不一,形态各异.一般来说,64位整型的定义方式有long long和__int64两种(VC还 ...

  4. 【转】C/C++的64位整型 不同编译器间的比较

    /为了和DSP兼容,TSint64和TUint64设置成TSint40和TUint40一样的数   //结果VC中还是认为是32位的,显然不合适   //typedef signed long int ...

  5. C/C++的64位整型 zz

    //为了和DSP兼容,TSint64和TUint64设置成TSint40和TUint40一样的数 //结果VC中还是认为是32位的,显然不合适 //typedef signed long int TS ...

  6. Intel 64/x86_64/IA-32/x86处理器 - SIMD指令集 - SSE扩展(9) - 64位整型指令(MMX指令集扩展)

    SSE 64-Bit SIMD Integer Instructions SSE扩展增加了几条64位组合的整型指令,这些指令操作MMX寄存器和64位的存储器操作数,这些指令可以看作是对MMX指令集的扩 ...

  7. mysql 64位整型_高性能MySQL笔记精简(整数和实数优化)

    一.优化数据类型的几个原则: 1. 更小的通常更好 a) 一般情况下应该尽量使用可以正确存储数据的最小值数据类型 b) 更小的通常更快,因为它占用更少的磁盘内存和cpu缓存,处理时需要的cpu周期更少 ...

  8. 关于64位整型int64_t

    1. 定义在头文件stdint.h中 2. 使用printf输出一个int64_t整数时,对于32位系统,应使用%lld,对于64位系统,应使用%ld.

  9. C++长整型(long long)64位整型耗时问题

    C++的long long型整型性能很差 今天刷OJ时偶然发现C++ long long整型数据居然耗时超过好几百毫秒,这对于时间敏感的编程题是无法忍受的,故记录此坑,留作纪念. 一.先上图 1.定义 ...

最新文章

  1. NSLog(@%@,类对象); 默认输出类名
  2. Qt Qwdget 汽车仪表知识点拆解4 另类进度条实现
  3. Linux的基础知识——mmap父子通信进程和匿名通信
  4. Pandas列百分数转化为小数
  5. 又拍云 php,GitHub - NoDurex/php-sdk: 又拍云存储PHP SDK
  6. 老男孩linux培训某节课前考试试题及答案分享 【转】
  7. java笔试题(题目+解析)
  8. 刘强东不是一个人,互联网寒冬真的来了
  9. PG-FP6烧录机1拖16上位机项目
  10. 10分钟带你进入Swagger的世界,快来看一看吧
  11. 可能是最有心的微信文章排版规范
  12. 从零开始学python的第14天
  13. html表单 多行输入文字,如何在HTML中创建多行文本输入(文本区域)?
  14. uni-app——如何获取页面容器的高度
  15. python3+selenium4自动化测试操作启动不同的浏览器-基础篇2
  16. 三元损失“In Defense of the Triplet Loss for Person Re-Identification”
  17. 机器学习笔记——支持向量机SMO算法完整版代码分析
  18. 106短信群发如何在移动信息时代下实现精准营销
  19. HTML手机签到转盘抽奖页面模板,手机端转盘抽奖代码分享
  20. VMware不识别U盘

热门文章

  1. PHP的html实现xpath解析,php用xpath解析html的代码实例讲解
  2. 一个账号可以登录几台机器_干货:一个PubMed账号可以有这么多用处!
  3. php抽象方法db,PHP笔记之抽象方法抽象类
  4. 公钥 私钥_区块链中私钥、公钥和钱包地址三者关系
  5. cxgrid 行合并单元格_合并单元格求和、计数、加序号
  6. 地图 插件 html 经纬度,如何往地图位置(经纬度)选择插件页面传递经纬度
  7. android音量键广播,音量控制键控制的音频流(setVolumeControlStream)描述
  8. 服务器修改地址,服务器修改管理地址
  9. mysql 5.5 主从同步问题_MySQL 5.5 主从复制异步、半同步以及注意事项详解
  10. 一秒执行一次_《一秒钟》:一贯的粗旷式抓大放小,张艺谋的自命题作业总是要观众自己再做一遍...