注册
web

JSON非常慢:这里有更快的替代方案!

是的,你没听错!JSON,这种在网络开发中普遍用于数据交换的格式,可能正在拖慢我们的应用程序。在速度和响应性至关重要的世界里,检查 JSON 的性能影响至关重要。在这篇博客中,深入探讨 JSON 可能成为应用程序瓶颈的原因,并探索更快的替代方法和优化技术,使您的应用程序保持最佳运行状态。


JSON 是什么,为什么要关心?


image.png


JSON 是 JavaScript Object Notation 的缩写,一种轻量级数据交换格式,已成为应用程序中传输和存储数据的首选。它的简单性和可读格式使开发者和机器都能轻松使用。但是,为什么要在项目中关注 JSON 呢?


JSON 是应用程序中数据的粘合剂。它是服务器和客户端之间进行数据通信的语言,也是数据库和配置文件中存储数据的格式。从本质上讲,JSON 在现代网络开发中起着举足轻重的作用。


JSON 的流行以及人们使用它的原因...


主要有就下几点:



  1. 人类可读格式:JSON 采用简单明了、基于文本的结构,便于开发人员和非开发人员阅读和理解。这种人类可读格式增强了协作,简化了调试。
  2. 语言无关:JSON 与任何特定编程语言无关。它是一种通用的数据格式,几乎所有现代编程语言都能对其进行解析和生成,因此具有很强的通用性。
  3. 数据结构一致性:JSON 使用键值对、数组和嵌套对象来实现数据结构的一致性。这种一致性使其具有可预测性,便于在各种编程场景中使用。
  4. 浏览器支持:浏览器原生支持 JSON,允许应用程序与服务器进行无缝通信。这种本地支持极大地促进了 JSON 在开发中的应用。
  5. JSON API:许多服务和应用程序接口默认以 JSON 格式提供数据。这进一步巩固了 JSON 在网络开发中作为数据交换首选的地位。
  6. JSON 模式:开发人员可以使用 JSON 模式定义和验证 JSON 数据的结构,从而为其应用程序增加一层额外的清晰度和可靠性。

鉴于这些优势,难怪全球的开发人员都依赖 JSON 来满足他们的数据交换需求。不过,随着我们深入探讨,会发现与 JSON 相关的潜在性能问题以及如何有效解决这些挑战。


对速度的需求


应用速度和响应速度的重要性


在当今快节奏的数字环境中,应用程序的速度和响应能力是不容忽视的。用户希望在网络和移动应用中即时获取信息、快速交互和无缝体验。对速度的这种要求是由多种因素驱动的:



  1. 用户期望:用户已习惯于从数字互动中获得闪电般快速的响应。他们不想等待网页加载或应用程序响应。哪怕是几秒钟的延迟,都会导致用户产生挫败感并放弃使用。
  2. 竞争优势:速度可以成为重要的竞争优势。与反应慢的应用程序相比,反应迅速的应用程序往往能更有效地吸引和留住用户。
  3. 搜索引擎排名:谷歌等搜索引擎将页面速度视为排名因素。加载速度更快的网站往往在搜索结果中排名靠前,从而提高知名度和流量。
  4. 转换率:电子商务网站尤其清楚速度对转换率的影响。网站速度越快,转换率越高,收入也就越高。
  5. 移动性能:随着移动设备的普及,对速度的需求变得更加重要。移动用户的带宽和处理能力往往有限,因此,快速的应用程序性能必不可少。

JSON 会拖慢我们的应用程序吗?


在某些情况下,JSON 可能是导致应用程序运行速度减慢的罪魁祸首。解析 JSON 数据的过程,尤其是在处理大型或复杂结构时,可能会耗费宝贵的毫秒时间。此外,低效的序列化和反序列化也会影响应用程序的整体性能


JSON 为什么会变慢


1.解析开销


JSON 数据到达应用程序后,必须经过解析过程才能转换成可用的数据结构。解析过程可能相对较慢,尤其是在处理大量或深度嵌套的 JSON 数据时。


2.序列化和反序列化


JSON 要求在从客户端向服务器发送数据时进行序列化(将对象编码为字符串),并在接收数据时进行反序列化(将字符串转换回可用对象)。这些步骤会带来开销并影响应用程序的整体速度。


在微服务架构的世界里,JSON 通常用于在服务之间传递消息。但是,JSON 消息需要序列化和反序列化,这两个过程会带来巨大的开销。



在众多微服务不断通信的情况下,这种开销可能会累积起来,有可能会使应用程序减慢到影响用户体验的程度。



image.png


3.字符串操作


JSON 以文本为基础,主要依靠字符串操作来进行连接和解析等操作。与处理二进制数据相比,字符串处理速度较慢。


4.缺乏数据类型


JSON 的数据类型(如字符串、数字、布尔值)有限。复杂的数据结构可能需要效率较低的表示方法,从而导致内存使用量增加和处理速度减慢。


image.png


5.冗长性


JSON 的人机可读设计可能导致冗长。冗余键和重复结构会增加有效载荷的大小,导致数据传输时间延长。


6.不支持二进制


JSON 缺乏对二进制数据的本地支持。在处理二进制数据时,开发人员通常需要将其编码和解码为文本,这可能会降低效率。


7.深嵌套


在某些情况下,JSON 数据可能嵌套很深,需要进行递归解析和遍历。这种计算复杂性会降低应用程序的运行速度,尤其是在没有优化的情况下。


JSON 的替代品


虽然 JSON 是一种通用的数据交换格式,但由于其在某些情况下的性能限制,开发者开始探索更快的替代格式。我们来看呓2其中的一些替代方案。


1.协议缓冲区(protobuf)


协议缓冲区(通常称为 protobuf)是谷歌开发的一种二进制序列化格式。其设计宗旨是高效、紧凑和快速。Protobuf 的二进制特性使其在序列化和反序列化时比 JSON 快得多。


何时使用:当你需要高性能数据交换时,尤其是在微服务架构、物联网应用或网络带宽有限的情况下,请考虑使用 protobuf


2. MessagePack 信息包


MessagePack 是另一种二进制序列化格式,以速度快、结构紧凑而著称。其设计目的是在保持与各种编程语言兼容的同时,提高比 JSON 更高的效率。


何时使用:当你需要在速度和跨语言兼容性之间取得平衡时,MessagePack 是一个不错的选择。它适用于实时应用程序和对减少数据量有重要要求的情况。


3. BSON(二进制 JSON)


BSON 或二进制 JSON 是一种从 JSON 衍生出来的二进制编码格式。它保留了 JSON 的灵活性,同时通过二进制编码提高了性能。BSON 常用于 MongoDB 等数据库。


何时使用:如果你正在使用 MongoDB,或者需要一种能在 JSON 和二进制效率之间架起桥梁的格式,那么 BSON 就是一个很有价值的选择。


4. Apache Avro(阿帕奇 Avro)


Apache Avro 是一个数据序列化框架,专注于提供一种紧凑的二进制格式。它基于模式,可实现高效的数据编码和解码。


何时使用:Avro 适用于模式演进非常重要的情况,如数据存储,以及需要在速度和数据结构灵活性之间取得平衡的情况。


与 JSON 相比,这些替代方案在性能上有不同程度的提升,具体选择取决于您的具体使用情况。通过考虑这些替代方案,您可以优化应用程序的数据交换流程,确保将速度和效率放在开发工作的首位。


image.png


每个字节的重要性:优化数据格式


JSON 数据


下面是我们的 JSON 数据示例片段:


{
"id": 1, // 14 bytes
"name": "John Doe", // 20 bytes
"email": "johndoe@example.com", // 31 bytes
"age": 30, // 9 bytes
"isSubscribed": true, // 13 bytes
"orders": [ // 11 bytes
{ // 2 bytes
"orderId": "A123", // 18 bytes
"totalAmount": 100.50 // 20 bytes
}, // 1 byte
{ // 2 bytes
"orderId": "B456", // 18 bytes
"totalAmount": 75.25 // 19 bytes
} // 1 byte
] // 1 byte
}

JSON 总大小: ~139 字节


JSON 功能多样,易于使用,但也有缺点,那就是它的文本性质。每个字符、每个空格和每个引号都很重要。在数据大小和传输速度至关重要的情况下,这些看似微不足道的字符可能会产生重大影响。


效率挑战:使用二进制格式减少数据大小


现在,我们提供其他格式的数据表示并比较它们的大小:


协议缓冲区 (protobuf)


syntax = "proto3";

message User {
int32 id = 1;
string name = 2;
string email = 3;
int32 age = 4;
bool is_subscribed = 5;
repeated Order orders = 6;

message Order {
string order_id = 1;
float total_amount = 2;
}
}

0A 0E 4A 6F 68 6E 20 44 6F 65 0C 4A 6F 68 6E 20 44 6F 65 65 78 61 6D 70 6C 65 2E 63 6F 6D 04 21 00 00 00 05 01 12 41 31 32 33 03 42 DC CC CC 3F 05 30 31 31 32 34 34 35 36 25 02 9A 99 99 3F 0D 31 02 42 34 35 36 25 02 9A 99 99 3F

协议缓冲区总大小: ~38 字节


MessagePack


二进制表示法(十六进制):


a36a6964000000000a4a6f686e20446f650c6a6f686e646f65406578616d706c652e636f6d042100000005011241313302bdcccc3f0530112434353625029a99993f

信息包总大小: ~34 字节


Binary Representation (Hexadecimal):


3e0000001069640031000a4a6f686e20446f6502656d61696c006a6f686e646f65406578616d706c652e636f6d1000000022616765001f04370e4940

BSON 总大小: ~43 字节


Avro


二进制表示法(十六进制):


0e120a4a6f686e20446f650c6a6f686e646f65406578616d706c652e636f6d049a999940040a020b4108312e3525312e323538323539

Avro 总大小: ~32 字节


image.png


现在,你可能想知道,为什么这些格式中的某些会输出二进制数据,但它们的大小却各不相同。Avro、MessagePack 和 BSON 等二进制格式具有不同的内部结构和编码机制,这可能导致二进制表示法的差异,即使它们最终表示的是相同的数据。下面简要介绍一下这些差异是如何产生的:


1. Avro



  • Avro 使用模式对数据进行编码,这种模式通常包含在二进制表示法中。
  • Avro 基于模式的编码通过提前指定数据结构,实现了高效的数据序列化和反序列化。
  • Avro 的二进制格式设计为自描述格式,这意味着模式信息包含在编码数据中。这种自描述性使 Avro 能够保持不同版本数据模式之间的兼容性。

2. MessagePack



  • MessagePack 是一种二进制序列化格式,直接对数据进行编码,不包含模式信息。
  • 它使用长度可变的整数和长度可变的字符串的紧凑二进制表示法,以尽量减少空间使用。
  • MessagePack 不包含模式信息,因此更适用于模式已提前知晓并在发送方和接收方之间共享的情况。


3. BSON



  • BSON 是 JSON 数据的二进制编码,包括每个值的类型信息。
  • BSON 的设计与 JSON 紧密相连,但它增加了二进制数据类型,如 JSON 缺乏的日期和二进制数据。
  • 与 MessagePack 一样,BSON 不包括模式信息。

这些设计和编码上的差异导致了二进制表示法的不同:



  • Avro 包含模式信息并具有自描述性,因此二进制文件稍大,但与模式兼容。
  • MessagePack 的编码长度可变,因此非常紧凑,但缺乏模式信息,因此适用于已知模式的情况。
  • BSON 与 JSON 关系密切,并包含类型信息,与 MessagePack 等纯二进制格式相比,BSON 的大小会有所增加。

总之,这些差异源于每种格式的设计目标和特点。Avro 优先考虑模式兼容性,MessagePack 侧重于紧凑性,而 BSON 在保持类似 JSON 结构的同时增加了二进制类型。格式的选择取决于您的具体使用情况和要求,如模式兼容性、数据大小和易用性。


优化 JSON 性能


下面是一些优化 JSON 性能的实用技巧以及代码示例和最佳实践:


1.最小化数据大小



  • 使用简短的描述性键名:选择简洁但有意义的键名,以减少 JSON 对象的大小

// Inefficient
{
"customer_name_with_spaces": "John Doe"
}

// Efficient
{
"customerName": "John Doe"
}


  • 尽可能缩写:在不影响清晰度的情况下,考虑对键或值使用缩写。

// 效率低
{
"transaction_type": "purchase"
}

// 效率高
{
"txnType": "purchase"
}

2.明智使用数组



  • 尽量减少嵌套:避免深度嵌套数组,因为它们会增加解析和遍历 JSON 的复杂性。

// 效率低
{
"order": {
"items": {
"item1": "Product A",
"item2": "Product B"
}
}
}

// 效率高
{
"orderItems": ["Product A", "Product B"]
}

3.优化数字表示法


尽可能使用整数:如果数值可以用整数表示,就用整数代替浮点数。


// 效率低
{
"quantity": 1.0
}

// 效率高
{
"quantity": 1
}

4.删除冗余


避免重复数据:通过引用共享值来消除冗余数据。


// 效率低
{
"product1": {
"name": "Product A",
"price": 10
},
"product2": {
"name": "Product A",
"price": 10
}
}

// 效率高
{
"products": [
{
"name": "Product A",
"price": 10
},
{
"name": "Product B",
"price": 15
}
]
}

5.使用压缩


应用压缩算法:如果适用,在传输过程中使用 Gzipor Brotlito 等压缩算法来减小 JSON 有效负载的大小。


// 使用 zlib 进行 Gzip 压缩的 Node.js 示例
const zlib = require('zlib');

const jsonData = {
// 在这里填入你的 JSON 数据
};

zlib.gzip(JSON.stringify(jsonData), (err, compressedData) => {
if (!err) {
// 通过网络发送 compressedData
}
});


6.采用服务器端缓存:


缓存 JSON 响应:实施服务器端缓存,高效地存储和提供 JSON 响应,减少重复数据处理的需要。


7.配置文件和优化


剖析性能:使用剖析工具找出 JSON 处理代码中的瓶颈,然后优化这些部分。


实际优化:在实践中加快 JSON 的处理速度


在本节中,我们将探讨实际案例,这些案例在使用 JSON 时遇到性能瓶颈并成功克服。我们会看到诸如 LinkedIn、Auth0、Uber 等知名技术公司如何解决 JSON 的限制并改善他们应用的性能。这些案例为如何提升应用处理速度和响应性提供了实用的策略。


1.LinkedIn 的协议缓冲区集成:



  • 挑战:LinkedIn 面临的挑战是 JSON 的冗长以及由此导致的网络带宽使用量增加,从而导致延迟增加。
  • 解决方案:他们采用了 Protocol Buffers,这是一种二进制序列化格式,用以替换微服务通信中的 JSON。
  • 影响:这一优化将延迟降低了 60%,提高了 LinkedIn 服务的速度和响应能力。

2.Uber 的 H3 地理索引:


挑战:Uber 使用 JSON 来表示各种地理空间数据,但解析大型数据集的 JSON 会降低其算法速度。


解决方案:他们引入了 H3 Geo-Index,这是一种用于地理空间数据的高效六边形网格系统,可减少 JSON 解析开销。


影响:这一优化大大加快了地理空间业务的发展,增强了 Uber 的叫车和地图服务。


3.Slack 的信息格式优化:


挑战:Slack 需要在实时聊天中传输和呈现大量 JSON 格式的消息,这导致了性能瓶颈。


解决方案:他们优化了 JSON 结构,减少了不必要的数据,只在每条信息中包含必要的信息。


影响:这项优化使得消息展现更快,从而提高了 Slack 用户的整体聊天性能。


4.Auth0 的协议缓冲区实现:


挑战:Auth0 是一个流行的身份和访问管理平台,在处理身份验证和授权数据时面临着 JSON 的性能挑战。


解决方案:他们采用协议缓冲区(Protocol Buffers)来取代 JSON,以编码和解码与身份验证相关的数据。


影响:这一优化大大提高了数据序列化和反序列化的速度,从而加快了身份验证流程,并增强了 Auth0 服务的整体性能。


这些现实世界中的例子展示了通过优化策略解决 JSON 的性能挑战如何对应用程序的速度、响应速度和用户体验产生实质性的积极影响。它们强调了考虑替代数据格式和高效数据结构的重要性,以克服各种情况下与 JSON 相关的速度减慢问题。


结论


在不断变化的网络开发环境中,优化 JSON 性能是一项宝贵的技能,它能让你的项目与众不同,并确保你的应用程序在即时数字体验时代茁壮成长。


作者:王大冶
来源:juejin.cn/post/7303424117243297807

0 个评论

要回复文章请先登录注册