談談序列化的作用

1. 寫在前面

我們應該都用過各種序列化(serialization)的方法(如Python中的pickle.dumps),甚至自己也寫過一些序列化的小工具。
維基百科對於序列化的解釋比較冗長;相比之下,百度百科的解釋更爲簡單:

序列化 (Serialization)是將對象的狀態信息轉換爲可以存儲或傳輸的形式的過程

這裏面重點強調了兩個概念:格式的轉變和轉變的目的

  • [格式的轉變] 轉變前的格式是對象狀態信息,轉變後的格式是“可以存儲或傳輸的形式
  • [轉變的目的] 轉變成字節流後的目的主要有兩個:1. 存儲到磁盤; 2. 通過網絡進行傳輸

存儲和傳輸的本質是一樣的,都是I/O。我們後面就以磁盤存儲爲例進行說明。
轉變後的格式(即:可存儲或傳輸的形式)比較抽象。因爲我們以磁盤存儲爲例進行說明,爲方便表述,我們後面就以“存儲格式”簡稱該轉變後的格式。
序列化的作用又可以簡單理解爲:把內存中的數據存儲到磁盤中的過程
那麼,一個隨之而來的問題便是:

  • 對象狀態格式和存儲格式的本質區別是什麼?
  • 或者:內存數據和磁盤數據的本質區別是什麼?

2. 問題闡述

我們知道,內存和磁盤本質上都是存儲二進制數據的。
那麼,

  1. 爲什麼不直接把內存中的數據複製一份到磁盤中呢?
  2. 爲什麼要進行序列化?
  3. 如果只是爲了數據持久化,直接進行數據的拷貝可以嗎?
  4. 內存中數據格式和磁盤中數據格式的本質區別是什麼?
  5. 既然內存和磁盤的存儲本質是一樣的,不考慮內存和磁盤的數據存取速度差異,使用磁盤直接和cache、CPU進行數據交互可以嗎(即跳過內存這一級別)?這個想法似乎和NVM有些不謀而合了。

這些問題其實說的是同一個問題。

3. 解釋

3.1 一些不夠完整的解釋

以上一系列問題中,“爲什麼要進行序列化”這個問題可能是最直接的。在網上也能搜索到一些答案,包括:

  • 跨語言:某種編程語言(Java)在磁盤上存儲的數據,有可能被別的編程語言(C++)讀取
  • 跨平臺:這個問題在網絡傳輸時更爲突出,在A機器上可能爲小端序,在B機器上則爲大端序

這些答案雖然也有道理,但我覺得還不夠完整。
假設我們使用同一門編程語言,在同一個機器上,還需要進行序列化嗎?
答案是:可能需要

3.2 一種完整的解釋

我們知道,序列化其實主要是進行了數據格式的轉換,即從內存格式轉換爲磁盤格式。
進行該轉換還有兩個很重要的原因:去地址和節省空間。

3.2.1 去地址

對於一些包含地址或引用的數據結構(如二叉樹),對象第一次在內存中的地址,和數據落盤後重新加載到內存中的地址,極有可能是不同的。
因此,需要對這種數據結構的對象,進行一些“去地址”的操作。該操作往往便是通過“序列化”來完成。

可能有人會想到在將對象落盤時,同時記錄下對象中的內存地址。第二次加載對象時,按照之前記錄的地址進行內存分配。
但內存一般是由多個應用共享的,第二次加載對象時,之前地址對應的內存空間可能已經被佔用了。

3.2.2 節省空間

前面提到一個問題:爲什麼不直接把內存中的數據複製一份到磁盤中呢?
複製操作對於一些簡單的數據結構(尤其是內存連續的數據結構)是可行的,比如說一個byte。在不考慮字節順序(大/小端序)的前提下,一個int也是可行的。實際上,序列化操作對於這些簡單數據結構也是這麼複製處理的。
但對於二叉樹這種複雜的數據結構,複製操作便不可行了。

現代操作系統的內存管理往往了採用了“內存分頁”、“邏輯空間”的機制。邏輯空間連續的頁面在物理空間中往往是分散的。
對於二叉樹這種複雜的數據結構,樹中不同的節點可能存儲在不同的內存頁面中,這些頁面分散在內存的不同地方。
如下圖所示,一棵二叉樹的三個節點分別對應內存中編號爲1,10,15的三塊內存空間。如果進行簡單複製,需要將1~15編號的整塊內存數據複製到磁盤中,即使2,3,4等編號的內存空間跟當前二叉樹無關。這便造成了嚴重的磁盤空間浪費
複製

這裏引申出另外一個問題:既然物理內存是共享使用的(如,編號爲2的內存空間可以由其他對象甚至應用使用),那麼磁盤空間可以共享使用嗎?如將紅色框中的磁盤空間也提供給別的應用使用。這似乎可以解決“磁盤空間浪費”的問題。
理論上這是可以的,但這要求在磁盤管理層面對這些“可共享”的空間進行記錄、管理和再分配。相當於在磁盤層面實現一套類似於“內存分頁”和“邏輯空間”映射的機制,這大大增加了磁盤管理的複雜度。

採用“序列化”的方法的目的之一,也是爲了解決“磁盤空間浪費”的問題。與磁盤層面的管理不同,“序列化”相當於在應用層面進行了管理,將數據更緊密地存儲在一起,如下圖所示:
序列化

4. 小節

序列化操作的本質是將對象中的字節組織成(順序的)字節流的過程
序列化的主要目的包括四點:

  • 實現數據的跨語言使用
  • 實現數據的跨平臺使用
  • 數據去內存地址
  • 降低磁盤存儲空間

實現後兩者目的的根本原因是:對象對內存地址的操作

參考鏈接

  1. Understanding serialization
  2. 維基百科 - 序列化
  3. 百度百科 - 序列化
  4. Why do we use serialization?
  5. Why do we serialize data?
  6. 實體類爲啥要序列化
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章