序列化是 PHP 应用中将数据结构转换为可存储或可传输格式的核心机制。无论是 Session 存储、缓存写入,还是消息队列通信,序列化都扮演着关键角色。PHP 内置的 serialize() 与 igbinary 扩展是两种最常见的实现方案,二者在底层设计上存在显著差异。本文将从数据结构、内存布局、编码策略和性能特征四个维度进行深度对比。
一、PHP serialize 的底层实现
1.1 序列化格式设计
PHP 原生 serialize() 输出的是一种人类可读的文本格式,每种数据类型都有对应的标识符:
N表示 nullb表示 booleani表示 integerd表示 doubles表示 stringa表示 arrayO表示 object
一个典型的序列化字符串如下:
a:2:{i:0;s:5:"hello";s:3:"key";i:42;}
这种格式的设计目标是可读性与跨平台兼容性,而非存储效率。每个字符串都需要写入长度前缀,每个数组元素都需要记录键和值,导致体积膨胀明显。
1.2 内部实现机制
在 PHP 源码中,serialize() 的核心实现在 ext/standard/var.c 的 php_var_serialize_intern() 函数中。其工作流程如下:
- 通过
php_var_serialize_get_smart_str()获取输出缓冲区 - 根据
zval的type字段进行分支判断 - 对复合类型(数组、对象)递归遍历 HashTable
- 使用
smart_str_appendl()将结果追加到缓冲区
值得注意的是,PHP 7 之后 zval 结构发生了重大变化,zend_value 联合体直接内联了整型和双精度浮点,使得序列化过程中访问标量数据更加高效。但对于数组和对象,仍然需要完整的 HashTable 遍历。
1.3 引用与循环处理
serialize() 通过 php_var_serialize_get_smart_str() 中的 var_hash 机制处理引用和循环引用。当遇到已序列化的对象时,会输出 r: 或 R: 标识符来引用之前的元素。这一机制保证了对象图的完整性,但也增加了额外的哈希查找开销。
二、igbinary 的底层实现
2.1 二进制编码设计
igbinary 采用紧凑的二进制格式,核心思想是用最少的字节表达最多的信息。其类型标识使用单字节编码:
0x00表示 null0x01表示 false,0x02表示 true0x03表示整型(后跟变长编码)0x05表示双精度浮点0x07表示字符串0x08表示数组
整型采用变长编码(类似 Protocol Buffers 的 varint),小整数仅占 1-2 字节。字符串长度也使用变长编码,且字符串内容直接以原始字节写入,无需转义。
2.2 字符串去重机制
igbinary 最核心的优化之一是字符串去重。在序列化过程中,所有出现过的字符串会被记录到一张哈希表中。当同一字符串再次出现时,仅写入其索引引用而非完整内容。
这一机制对包含大量重复键名的关联数组效果极为显著。例如一个包含 1000 条记录的数组,每条记录都有相同的键名(如 id、name、email),igbinary 只需存储一次键名字符串,后续全部使用引用。
2.3 内部实现架构
igbinary 的实现在 igbinary.c 中,核心函数为 igbinary_serialize_zval()。其处理流程:
- 初始化
igbinary_serialize_data结构体,包含字符串哈希表和输出缓冲区 - 对每个
zval进行类型判断,写入对应的类型标识字节 - 标量类型直接编码写入
- 数组和对象先写入元素数量,再递归处理每个元素
- 字符串在写入前先查询哈希表,命中则写引用,未命中则写入内容并注册
igbinary 的输出缓冲区采用预分配策略,减少了内存重分配次数。
三、性能对比分析
3.1 序列化体积
以典型业务数据为例,一个包含 100 条用户记录的数组:
| 方案 | 序列化后大小 | 相对比例 |
|---|---|---|
| serialize | ~45 KB | 100% |
| igbinary | ~12 KB | 27% |
体积缩减主要来自三个方面:二进制编码替代文本标识、变长整数编码、字符串去重。
3.2 序列化/反序列化速度
在 PHP 7.4 + igbinary 3.2 环境下的基准测试表明:
- 序列化速度:igbinary 比 serialize 快约 30-50%,主要得益于更少的字符串操作和内存分配
- 反序列化速度:igbinary 快约 40-60%,二进制解析比文本解析更高效
- 内存占用:igbinary 序列化过程中峰值内存更低,因为输出缓冲区增长更慢
3.3 适用场景差异
serialize() 的优势在于:
- 无需额外扩展,所有 PHP 环境原生支持
- 输出可读,便于调试
- 跨语言解析相对容易(文本格式)
igbinary 的优势在于:
- 存储和网络传输成本大幅降低
- 高频序列化场景下 CPU 开销更小
- 与 Redis、Memcached 等缓存系统配合时效果显著
四、兼容性与迁移考量
igbinary 提供了 igbinary_serialize() 和 igbinary_unserialize() 函数,同时可以通过 serialize_handler 配置项透明替换 PHP 原生序列化。在 Redis 场景中,只需将 serialize_handler 设为 igbinary,即可让 Session 和缓存自动使用二进制格式。
需要注意的是,igbinary 格式与原生 serialize() 不兼容。迁移时需要考虑:
- 存量数据的兼容读取(可通过版本标记或双写过渡)
- 跨系统数据交换时对方是否支持 igbinary 格式
- 调试工具链是否需要相应调整
五、总结
PHP 原生 serialize() 以可读性和通用性为设计目标,适合调试和跨语言场景;igbinary 以存储效率和执行性能为核心,适合高并发、大数据量的生产环境。二者在底层实现上的根本差异——文本编码 vs 二进制编码、无去重 vs 字符串去重、固定长度 vs 变长整数——决定了它们各自的性能特征和适用范围。在实际项目中,应根据数据规模、访问频率和系统边界做出合理选择。对于追求极致性能的 PHP 应用,igbinary 通常是更优的序列化方案。
未经允许不得转载:任鹏个人博客 » PHP 序列化机制深度剖析:serialize 与 igbinary 的底层实现对比

