Skip to content

MongoDB Long 类型详解

本知识点承接《MongoDB数据类型概述》,后续延伸至《MongoDB数值计算》,建议学习顺序:MongoDB基础→数据类型概述→本知识点→数值计算

1. 概述

在MongoDB数据库中,Long类型(也称为Int64)是一种BSON数值类型(BSON type 0x12),用于存储64位有符号整数。Long类型的取值范围是-9,223,372,036,854,775,808到9,223,372,036,854,775,807(即-2^63到2^63-1),适合存储大范围的整数值。

在PHP中,我们使用MongoDB\BSON\Int64类来显式创建Long类型的值。PHP的整数类型在64位平台上会自动映射到Long类型,但在32位平台上需要显式使用Int64类来处理大整数。Long类型的主要应用场景包括:存储大ID、时间戳(毫秒级)、大计数器、文件大小、金额(分)等需要大范围整数的场景。

理解Long类型有助于开发者正确处理大整数数据,避免32位整数的溢出问题,确保数据的准确性和完整性。

2. 基本概念

2.1 语法

MongoDB Long类型在PHP中使用MongoDB\BSON\Int64类表示,其基本语法如下:

php
use MongoDB\BSON\Int64;

// 创建Int64对象
$int64 = new Int64(int|string $value);

// 参数说明:
// $value: 整数值或字符串表示的整数

Long取值范围

边界说明
最小值-9,223,372,036,854,775,808-2^63
最大值9,223,372,036,854,775,8072^63-1
存储8字节64位有符号整数

MongoDB数值类型对比

类型BSON码范围存储
Int320x10±2.1×10^94字节
Int64/Long0x12±9.2×10^188字节
Double0x01±1.8×10^3088字节
Decimal1280x1334位精度16字节

2.2 语义

Long类型在MongoDB中的语义主要体现在以下几个方面:

存储语义

  • 作为64位有符号整数存储
  • 类型码为0x12
  • 占用8字节存储空间

比较语义

  • 与其他数值类型进行数值比较
  • Long与Int32、Double比较时按数值比较
  • 在排序中按数值大小排序

类型转换语义

  • PHP 64位整数自动映射为Long
  • PHP 32位整数需要显式使用Int64
  • 字符串形式的整数可转换为Int64
php
<?php
require_once __DIR__ . '/vendor/autoload.php';

use MongoDB\BSON\Int64;
use MongoDB\Client;

echo "=== Long基本语义 ===\n\n";

// 1. 创建Int64对象
echo "1. 创建Int64对象:\n";
$int64 = new Int64(9223372036854775807);
echo "   类型: " . get_class($int64) . "\n";
echo "   值: " . $int64 . "\n";

// 2. JSON序列化
echo "\n2. JSON序列化:\n";
echo "   " . json_encode(['num' => $int64], JSON_PRETTY_PRINT) . "\n";

// 3. 取值范围
echo "\n3. 取值范围:\n";
echo "   最小值: " . new Int64("-9223372036854775808") . "\n";
echo "   最大值: " . new Int64("9223372036854775807") . "\n";

// 4. 类型比较
echo "\n4. 类型比较演示:\n";
$client = new Client('mongodb://localhost:27017');
$collection = $client->test->long_demo;
$collection->drop();

$collection->insertMany([
    ['name' => 'Long', 'value' => new Int64(100)],
    ['name' => 'PHP Int', 'value' => 100],
    ['name' => 'Double', 'value' => 100.0]
]);

$results = $collection->find(['value' => 100]);
echo "   查询 value = 100 的结果:\n";
foreach ($results as $doc) {
    echo "     - {$doc->name}\n";
}
?>

输出结果

=== Long基本语义 ===

1. 创建Int64对象:
   类型: MongoDB\BSON\Int64
   值: 9223372036854775807

2. JSON序列化:
   {
       "num": {
           "$numberLong": "9223372036854775807"
       }
   }

3. 取值范围:
   最小值: -9223372036854775808
   最大值: 9223372036854775807

4. 类型比较演示:
   查询 value = 100 的结果:
     - Long
     - PHP Int
     - Double

2.3 存储结构

Long类型在BSON中的存储结构:

┌─────────────────────────────────────────────────────┐
│                  BSON Long (Int64)                  │
├─────────────────────────────────────────────────────┤
│  Type (1 byte): 0x12                                │
│  Name (cstring): 字段名                              │
│  Value (int64): 64位有符号整数                       │
│    └─ 8 bytes, little-endian                        │
└─────────────────────────────────────────────────────┘

3. 基础用法

3.1 创建Long对象

php
<?php
require_once __DIR__ . '/vendor/autoload.php';

use MongoDB\BSON\Int64;

echo "=== 创建Long对象 ===\n\n";

// 方式1:直接实例化
$int64_1 = new Int64(42);
echo "1. 直接实例化: " . $int64_1 . "\n";

// 方式2:从字符串创建(推荐用于大整数)
$int64_2 = new Int64("9223372036854775807");
echo "2. 从字符串创建: " . $int64_2 . "\n";

// 方式3:负数
$int64_3 = new Int64(-100);
echo "3. 负数: " . $int64_3 . "\n";

// 方式4:边界值
$int64_min = new Int64("-9223372036854775808");
$int64_max = new Int64("9223372036854775807");
echo "4. 边界值: min={$int64_min}, max={$int64_max}\n";

// 获取值
echo "\n获取值:\n";
echo "  __toString(): " . $int64_1->__toString() . "\n";
echo "  (string)转换: " . (string)$int64_1 . "\n";
?>

3.2 Long与PHP整数的关系

php
<?php
require_once __DIR__ . '/vendor/autoload.php';

use MongoDB\BSON\Int64;
use MongoDB\Client;

echo "=== Long与PHP整数的关系 ===\n\n";

echo "PHP平台信息:\n";
echo "  PHP_INT_SIZE: " . PHP_INT_SIZE . " 字节\n";
echo "  PHP_INT_MAX: " . PHP_INT_MAX . "\n";

$client = new Client('mongodb://localhost:27017');
$collection = $client->test->long_php;
$collection->drop();

// PHP整数自动映射
$collection->insertMany([
    ['name' => 'small_int', 'value' => 100],
    ['name' => 'large_int', 'value' => 3000000000],
    ['name' => 'explicit_int64', 'value' => new Int64(100)],
    ['name' => 'very_large', 'value' => new Int64("9223372036854775800")]
]);

echo "\n存储结果分析:\n";
$results = $collection->find([]);
foreach ($results as $doc) {
    $type = gettype($doc->value);
    $class = is_object($doc->value) ? get_class($doc->value) : 'native';
    echo "  {$doc->name}: {$doc->value} (PHP类型: {$type}, 类: {$class})\n";
}
?>

3.3 Long在查询中的使用

php
<?php
require_once __DIR__ . '/vendor/autoload.php';

use MongoDB\BSON\Int64;
use MongoDB\Client;

echo "=== Long在查询中的使用 ===\n\n";

$client = new Client('mongodb://localhost:27017');
$collection = $client->test->long_query;
$collection->drop();

$collection->insertMany([
    ['name' => 'Item A', 'quantity' => new Int64(10)],
    ['name' => 'Item B', 'quantity' => new Int64(50)],
    ['name' => 'Item C', 'quantity' => new Int64(100)],
    ['name' => 'Item D', 'quantity' => new Int64(200)]
]);

// 精确查询
echo "精确查询 quantity = 50:\n";
$results = $collection->find(['quantity' => new Int64(50)]);
foreach ($results as $doc) {
    echo "  - {$doc->name}: {$doc->quantity}\n";
}

// 范围查询
echo "\n范围查询 quantity > 50:\n";
$results = $collection->find(['quantity' => ['$gt' => new Int64(50)]]);
foreach ($results as $doc) {
    echo "  - {$doc->name}: {$doc->quantity}\n";
}

// 排序
echo "\n排序查询(降序):\n";
$results = $collection->find([], ['sort' => ['quantity' => -1]]);
foreach ($results as $doc) {
    echo "  - {$doc->name}: {$doc->quantity}\n";
}
?>

4. 进阶用法

4.1 大ID生成器

php
<?php
require_once __DIR__ . '/vendor/autoload.php';

use MongoDB\BSON\Int64;
use MongoDB\Client;

echo "=== 大ID生成器 ===\n\n";

class LargeIdGenerator
{
    private $collection;
    
    public function __construct($collection)
    {
        $this->collection = $collection;
    }
    
    public function nextId(): string
    {
        $result = $this->collection->findOneAndUpdate(
            ['_id' => 'counter'],
            ['$inc' => ['value' => new Int64(1)]],
            [
                'returnDocument' => MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER,
                'upsert' => true
            ]
        );
        
        return (string)$result->value;
    }
    
    public function batchIds(int $count): array
    {
        $ids = [];
        for ($i = 0; $i < $count; $i++) {
            $ids[] = $this->nextId();
        }
        return $ids;
    }
}

$client = new Client('mongodb://localhost:27017');
$collection = $client->test->id_generator;
$collection->drop();

$generator = new LargeIdGenerator($collection);

echo "生成ID:\n";
for ($i = 1; $i <= 5; $i++) {
    $id = $generator->nextId();
    echo "  ID {$i}: {$id}\n";
}
?>

4.2 时间戳存储

php
<?php
require_once __DIR__ . '/vendor/autoload.php';

use MongoDB\BSON\Int64;
use MongoDB\Client;

echo "=== 时间戳存储 ===\n\n";

class TimestampStorage
{
    private $collection;
    
    public function __construct($collection)
    {
        $this->collection = $collection;
    }
    
    public function storeEvent(string $event): void
    {
        $microtime = microtime(true);
        $milliseconds = (int)($microtime * 1000);
        
        $this->collection->insertOne([
            'event' => $event,
            'timestamp' => new Int64($milliseconds),
            'created' => new MongoDB\BSON\UTCDateTime()
        ]);
    }
    
    public function getEventsAfter(string $timestamp): array
    {
        return $this->collection->find([
            'timestamp' => ['$gte' => new Int64($timestamp)]
        ])->toArray();
    }
    
    public function getLatestEvent(): ?array
    {
        $result = $this->collection->findOne(
            [],
            ['sort' => ['timestamp' => -1]]
        );
        return $result ? (array)$result : null;
    }
}

$client = new Client('mongodb://localhost:27017');
$collection = $client->test->event_timestamps;
$collection->drop();

$storage = new TimestampStorage($collection);

$storage->storeEvent('Event A');
$storage->storeEvent('Event B');
$storage->storeEvent('Event C');

echo "存储的事件:\n";
$events = $collection->find([], ['sort' => ['timestamp' => 1]]);
foreach ($events as $event) {
    $ts = (string)$event->timestamp;
    echo "  - {$event->event}: {$ts} ms\n";
}
?>

4.3 金额存储(分)

php
<?php
require_once __DIR__ . '/vendor/autoload.php';

use MongoDB\BSON\Int64;
use MongoDB\Client;

echo "=== 金额存储(分) ===\n\n";

class MoneyStorage
{
    private $collection;
    
    public function __construct($collection)
    {
        $this->collection = $collection;
    }
    
    public function deposit(string $account, int $cents): void
    {
        $this->collection->updateOne(
            ['account' => $account],
            ['$inc' => ['balance' => new Int64($cents)]],
            ['upsert' => true]
        );
    }
    
    public function withdraw(string $account, int $cents): bool
    {
        $result = $this->collection->findOneAndUpdate(
            ['account' => $account, 'balance' => ['$gte' => new Int64($cents)]],
            ['$inc' => ['balance' => new Int64(-$cents)]]
        );
        
        return $result !== null;
    }
    
    public function getBalance(string $account): string
    {
        $result = $this->collection->findOne(['account' => $account]);
        if (!$result) return '0.00';
        
        $cents = (int)(string)$result->balance;
        $dollars = $cents / 100;
        return number_format($dollars, 2);
    }
    
    public function transfer(string $from, string $to, int $cents): bool
    {
        $fromBalance = $this->collection->findOne(['account' => $from]);
        if (!$fromBalance || (int)(string)$fromBalance->balance < $cents) {
            return false;
        }
        
        $this->withdraw($from, $cents);
        $this->deposit($to, $cents);
        
        return true;
    }
}

$client = new Client('mongodb://localhost:27017');
$collection = $client->test->money_storage;
$collection->drop();

$money = new MoneyStorage($collection);

$money->deposit('user1', 10000);
$money->deposit('user2', 5000);

echo "初始余额:\n";
echo "  user1: $" . $money->getBalance('user1') . "\n";
echo "  user2: $" . $money->getBalance('user2') . "\n";

$money->transfer('user1', 'user2', 2000);

echo "\n转账后余额:\n";
echo "  user1: $" . $money->getBalance('user1') . "\n";
echo "  user2: $" . $money->getBalance('user2') . "\n";
?>

5. 实际应用场景

5.1 大计数器

php
<?php
require_once __DIR__ . '/vendor/autoload.php';

use MongoDB\BSON\Int64;
use MongoDB\Client;

echo "=== 大计数器 ===\n\n";

class LargeCounter
{
    private $collection;
    
    public function __construct($collection)
    {
        $this->collection = $collection;
    }
    
    public function increment(string $name, int $delta = 1): string
    {
        $result = $this->collection->findOneAndUpdate(
            ['name' => $name],
            [
                '$inc' => ['value' => new Int64($delta)],
                '$set' => ['updated' => new MongoDB\BSON\UTCDateTime()]
            ],
            [
                'returnDocument' => MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER,
                'upsert' => true
            ]
        );
        
        return (string)$result->value;
    }
    
    public function get(string $name): string
    {
        $result = $this->collection->findOne(['name' => $name]);
        return $result ? (string)$result->value : '0';
    }
    
    public function reset(string $name): void
    {
        $this->collection->updateOne(
            ['name' => $name],
            ['$set' => ['value' => new Int64(0)]]
        );
    }
}

$client = new Client('mongodb://localhost:27017');
$collection = $client->test->large_counters;
$collection->drop();

$counter = new LargeCounter($collection);

echo "计数器操作:\n";
echo "  初始: " . $counter->increment('total_requests') . "\n";
echo "  增加100: " . $counter->increment('total_requests', 100) . "\n";
echo "  增加1000: " . $counter->increment('total_requests', 1000) . "\n";
echo "  当前值: " . $counter->get('total_requests') . "\n";
?>

5.2 文件大小统计

php
<?php
require_once __DIR__ . '/vendor/autoload.php';

use MongoDB\BSON\Int64;
use MongoDB\Client;

echo "=== 文件大小统计 ===\n\n";

class FileSizeTracker
{
    private $collection;
    
    public function __construct($collection)
    {
        $this->collection = $collection;
    }
    
    public function addFile(string $path, int $bytes): void
    {
        $this->collection->insertOne([
            'path' => $path,
            'size' => new Int64($bytes),
            'added' => new MongoDB\BSON\UTCDateTime()
        ]);
    }
    
    public function getTotalSize(): string
    {
        $pipeline = [
            ['$group' => ['_id' => null, 'total' => ['$sum' => '$size']]]
        ];
        $result = $this->collection->aggregate($pipeline)->toArray();
        
        if (empty($result)) return '0';
        return (string)$result[0]->total;
    }
    
    public function formatBytes(int $bytes): string
    {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $i = 0;
        
        while ($bytes >= 1024 && $i < count($units) - 1) {
            $bytes /= 1024;
            $i++;
        }
        
        return round($bytes, 2) . ' ' . $units[$i];
    }
}

$client = new Client('mongodb://localhost:27017');
$collection = $client->test->file_sizes;
$collection->drop();

$tracker = new FileSizeTracker($collection);

$tracker->addFile('/path/file1.txt', 1024);
$tracker->addFile('/path/file2.txt', 1048576);
$tracker->addFile('/path/file3.txt', 1073741824);

echo "文件统计:\n";
$total = $tracker->getTotalSize();
echo "  总大小: " . $tracker->formatBytes((int)$total) . "\n";
?>

5.3 分布式ID生成

php
<?php
require_once __DIR__ . '/vendor/autoload.php';

use MongoDB\BSON\Int64;
use MongoDB\Client;

echo "=== 分布式ID生成 ===\n\n";

class DistributedIdGenerator
{
    private $collection;
    private $nodeId;
    
    public function __construct($collection, int $nodeId)
    {
        $this->collection = $collection;
        $this->nodeId = $nodeId;
    }
    
    public function generate(): string
    {
        $timestamp = (int)(microtime(true) * 1000);
        $sequence = $this->getSequence($timestamp);
        
        $id = ($timestamp << 22) | ($this->nodeId << 12) | $sequence;
        return (string)new Int64($id);
    }
    
    private function getSequence(int $timestamp): int
    {
        $result = $this->collection->findOneAndUpdate(
            ['_id' => $this->nodeId],
            [
                '$inc' => ['sequence' => new Int64(1)],
                '$set' => ['timestamp' => new Int64($timestamp)]
            ],
            [
                'returnDocument' => MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER,
                'upsert' => true
            ]
        );
        
        if ((string)$result->timestamp != (string)$timestamp) {
            $this->collection->updateOne(
                ['_id' => $this->nodeId],
                ['$set' => ['sequence' => new Int64(0)]]
            );
            return 0;
        }
        
        return (int)(string)$result->sequence % 4096;
    }
}

$client = new Client('mongodb://localhost:27017');
$collection = $client->test->distributed_ids;
$collection->drop();

$generator = new DistributedIdGenerator($collection, 1);

echo "生成分布式ID:\n";
for ($i = 1; $i <= 5; $i++) {
    $id = $generator->generate();
    echo "  ID {$i}: {$id}\n";
}
?>

6. 性能优化

6.1 索引与Long

php
<?php
require_once __DIR__ . '/vendor/autoload.php';

use MongoDB\BSON\Int64;
use MongoDB\Client;

echo "=== 索引与Long ===\n\n";

$client = new Client('mongodb://localhost:27017');
$collection = $client->test->long_index;
$collection->drop();

// 创建索引
$collection->createIndex(['user_id' => 1]);
$collection->createIndex(['score' => -1]);

// 插入数据
$data = [];
for ($i = 1; $i <= 10000; $i++) {
    $data[] = [
        'user_id' => new Int64($i),
        'score' => new Int64(rand(0, 1000000))
    ];
}
$collection->insertMany($data);

echo "插入10000条记录\n";

// 使用索引查询
echo "\n索引查询测试:\n";
$startTime = microtime(true);
$result = $collection->findOne(['user_id' => new Int64(5000)]);
$endTime = microtime(true);
echo "  查询user_id=5000: " . round(($endTime - $startTime) * 1000, 2) . " ms\n";

// 范围查询
$startTime = microtime(true);
$results = $collection->find(['score' => ['$gt' => new Int64(900000)]])->toArray();
$endTime = microtime(true);
echo "  查询score>900000: " . count($results) . " 条, " . round(($endTime - $startTime) * 1000, 2) . " ms\n";
?>

7. 安全注意事项

7.1 溢出检测

php
<?php
require_once __DIR__ . '/vendor/autoload.php';

use MongoDB\BSON\Int64;

echo "=== 溢出检测 ===\n\n";

class SafeInt64
{
    const MIN = "-9223372036854775808";
    const MAX = "9223372036854775807";
    
    public static function create($value): Int64
    {
        $strValue = (string)$value;
        
        if (bccomp($strValue, self::MIN) < 0 || bccomp($strValue, self::MAX) > 0) {
            throw new OverflowException("Value {$strValue} is out of Int64 range");
        }
        
        return new Int64($strValue);
    }
    
    public static function safeAdd(Int64 $a, Int64 $b): Int64
    {
        $result = bcadd((string)$a, (string)$b);
        return self::create($result);
    }
    
    public static function safeMultiply(Int64 $a, Int64 $b): Int64
    {
        $result = bcmul((string)$a, (string)$b);
        return self::create($result);
    }
}

// 测试
echo "安全创建:\n";
try {
    $int64 = SafeInt64::create("9223372036854775807");
    echo "  创建成功: {$int64}\n";
    
    $overflow = SafeInt64::create("9223372036854775808");
} catch (OverflowException $e) {
    echo "  溢出错误: " . $e->getMessage() . "\n";
}
?>

8. 常见问题与解决方案

问题1:Long和PHP整数有什么区别?

问题描述:PHP整数和MongoDB Long有什么不同?

回答内容

PHP整数根据平台可能是32位或64位,而Long固定为64位。

php
<?php
use MongoDB\BSON\Int64;

echo "=== Long vs PHP整数 ===\n\n";

echo "PHP整数大小: " . PHP_INT_SIZE . " 字节\n";
echo "PHP整数最大值: " . PHP_INT_MAX . "\n";

echo "\nLong范围:\n";
echo "  最小值: -9223372036854775808\n";
echo "  最大值: 9223372036854775807\n";

echo "\n区别:\n";
echo "  1. Long固定64位,PHP整数依赖平台\n";
echo "  2. Long可以存储更大的整数\n";
echo "  3. 在32位PHP上需要显式使用Int64\n";
?>

问题2:什么时候应该使用Long?

问题描述:什么场景下应该显式使用Long类型?

回答内容

当需要存储大整数、确保64位精度或与32位PHP平台兼容时使用Long。

php
<?php
use MongoDB\BSON\Int64;

echo "=== Long使用场景 ===\n\n";

echo "推荐使用Long:\n";
echo "  1. 存储大ID、时间戳等大整数\n";
echo "  2. 需要确保64位精度的场景\n";
echo "  3. 32位PHP平台处理大整数\n";
echo "  4. 金额(分)、文件大小等\n";

echo "\n示例:\n";
echo "  \$id = new Int64('9223372036854775807');\n";
echo "  \$timestamp = new Int64(microtime(true) * 1000);\n";
echo "  \$cents = new Int64(10000);\n";
?>

问题3:Long溢出会怎样?

问题描述:超出Long范围的值会发生什么?

回答内容

超出范围会抛出异常,需要使用Decimal128或字符串存储。

php
<?php
use MongoDB\BSON\Int64;

echo "=== Long溢出处理 ===\n\n";

// 正常范围
echo "正常范围:\n";
$normal = new Int64("9223372036854775807");
echo "  最大值: {$normal}\n";

// 超出范围
echo "\n超出范围:\n";
try {
    $overflow = new Int64("9223372036854775808");
} catch (InvalidArgumentException $e) {
    echo "  错误: " . $e->getMessage() . "\n";
}

echo "\n解决方案:\n";
echo "  1. 使用Decimal128存储超大数值\n";
echo "  2. 使用字符串存储精确数值\n";
echo "  3. 在应用层进行范围检查\n";
?>

9. 实战练习

练习1:实现大计数器

练习描述:创建一个支持大计数的计数器系统。

解题思路

  1. 使用Int64存储计数值
  2. 提供安全的增减操作
  3. 记录操作历史

参考代码

php
<?php
use MongoDB\BSON\Int64;
use MongoDB\Client;

class LargeCounterSystem
{
    private $collection;
    
    public function __construct($collection)
    {
        $this->collection = $collection;
    }
    
    public function increment(string $name, int $delta = 1): string
    {
        $result = $this->collection->findOneAndUpdate(
            ['name' => $name],
            ['$inc' => ['value' => new Int64($delta)]],
            ['upsert' => true, 'returnDocument' => MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER]
        );
        
        return (string)$result->value;
    }
    
    public function get(string $name): string
    {
        $result = $this->collection->findOne(['name' => $name]);
        return $result ? (string)$result->value : '0';
    }
}

echo "=== 大计数器系统 ===\n\n";

$client = new Client('mongodb://localhost:27017');
$collection = $client->test->large_counter_system;
$collection->drop();

$counter = new LargeCounterSystem($collection);
echo "计数: " . $counter->increment('total') . "\n";
echo "计数: " . $counter->increment('total', 1000000000) . "\n";
?>

练习2:实现分布式ID生成器

练习描述:创建一个分布式唯一ID生成器。

解题思路

  1. 使用时间戳、节点ID和序列号
  2. 确保ID唯一性
  3. 使用Int64存储

参考代码

php
<?php
use MongoDB\BSON\Int64;
use MongoDB\Client;

class SnowflakeIdGenerator
{
    private $collection;
    private $nodeId;
    
    public function __construct($collection, int $nodeId)
    {
        $this->collection = $collection;
        $this->nodeId = $nodeId;
    }
    
    public function generate(): string
    {
        $timestamp = (int)(microtime(true) * 1000);
        $sequence = $this->getNextSequence($timestamp);
        
        $id = ($timestamp << 22) | ($this->nodeId << 12) | $sequence;
        return (string)new Int64($id);
    }
    
    private function getNextSequence(int $timestamp): int
    {
        $result = $this->collection->findOneAndUpdate(
            ['_id' => $this->nodeId],
            [
                '$inc' => ['sequence' => new Int64(1)],
                '$set' => ['last_timestamp' => new Int64($timestamp)]
            ],
            ['upsert' => true, 'returnDocument' => MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER]
        );
        
        if ((string)$result->last_timestamp != (string)$timestamp) {
            $this->collection->updateOne(
                ['_id' => $this->nodeId],
                ['$set' => ['sequence' => new Int64(0)]]
            );
            return 0;
        }
        
        return (int)(string)$result->sequence % 4096;
    }
}

echo "=== Snowflake ID生成器 ===\n\n";

$client = new Client('mongodb://localhost:27017');
$collection = $client->test->snowflake_ids;
$collection->drop();

$generator = new SnowflakeIdGenerator($collection, 1);

echo "生成ID:\n";
for ($i = 1; $i <= 5; $i++) {
    $id = $generator->generate();
    echo "  ID {$i}: {$id}\n";
}
?>

10. 知识点总结

核心概念回顾

概念说明重要程度
BSON类型码0x12,64位有符号整数⭐⭐⭐
PHP类MongoDB\BSON\Int64⭐⭐⭐
取值范围-2^63 到 2^63-1⭐⭐⭐
存储空间8字节⭐⭐
比较语义数值比较⭐⭐
溢出处理抛出异常⭐⭐⭐

关键技能掌握

必须掌握

  1. Int64对象的创建
  2. 理解取值范围
  3. 溢出检测和处理
  4. 与PHP整数的区别

建议掌握

  1. 大ID生成
  2. 时间戳存储
  3. 金额存储(分)
  4. 分布式ID生成

最佳实践清单

创建Long

php
$int64 = new Int64("9223372036854775807");

安全操作

php
if (bccomp($value, "-9223372036854775808") >= 0 && bccomp($value, "9223372036854775807") <= 0) {
    $int64 = new Int64($value);
}

计数器

php
$collection->updateOne(['name' => 'counter'], ['$inc' => ['value' => new Int64(1)]]);

常见错误避免

错误正确做法
忽略溢出添加范围检查
32位平台问题使用字符串创建Int64
混用类型保持类型一致
精度丢失使用Int64而非Double

11. 拓展参考资料

官方文档

PHP驱动文档

相关章节