日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

如何使用有序GUID提升数据库读写性能

發布時間:2023/12/4 数据库 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何使用有序GUID提升数据库读写性能 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

源寶導讀:數據庫設計時,經常會使用GUID作為表的主鍵,但由于GUID的隨機性會導致數據庫在讀寫數據時效率嚴重下降,影響應用程序整體性能。本文將深入探討如何通過使用有序GUID提升數據讀寫的性能。

一、背景

? ??常見的數據庫設計是使用連續的整數為做主鍵,當新的數據插入到數據庫時,由數據庫自動生成,但這種設計不一定適合所有場景。

? 隨著越來越多的應用程序使用Nhibernate、Entity Framework Core等ORM(對象關系映射)框架,應用被設計成為工作單元(Unit Of Work)模式,需要在數據持久化之前生成主鍵,解決主實體與子系統的依賴關系;為了保證在多線程并發以及站點集群環境中主鍵的唯一性,最簡單最常見的方式是將主鍵設計成為GUID類型。

? ? 工作單元是數據庫應用程序經常使用的一種設計模式,簡單一點來說,就是對多個數據庫操作進行打包,記錄對象上的所有變化,并在最后提交時一次性將所有變化通過系統事務寫入數據庫。目的是為了減少數據庫調用次數以及避免數據庫長事務。關于工作單元的知識可以在各類博客網站中都有說明,在這里就不做詳細的介紹了。

? ? GUID(全球唯一標識符)也稱為UUID,是一種由算法生成的二進制長度為128位的數字標識符。在理想情況下,任何計算機之間都不會生成兩個相同的GUID。GUID 的總數達到了2^128(3.4×10^38)個,所以隨機生成兩個相同GUID的可能性非常小,但并不為0。GUID一詞有時也專指微軟對UUID標準的實現。

? ??RFC 41222描述了創建標準GUID,如今大多數GUID生成算法通常是一個很長的隨機數,再結合一些像網絡MAC地址這種隨機的本地組件信息。

? ? GUID的優點允許開發人員隨時創建新值,而無需從數據庫服務器檢查值的唯一性,這似乎是一個完美的解決方案。

? ? 很多數據庫在創建主鍵時,為了充分發揮數據庫的性能,會自動在該列上創建聚集索引。我們先來說一說什么是聚集索引。集索引確定表中數據的物理順序,類似于電話簿,按姓氏排列數據。由于聚集索引規定數據在表中的物理存儲順序,因此一個表也只能包含一個聚集索引。它能夠快速查找到數據,但是如果插入數據庫的主鍵不在列表的末尾,向表中添加新行時就非常緩慢。例如,看下面這個例子,在表中已經存在三行數據(例子來自Jeremy Todd的博客《GUIDs as fast primary keys under multiple databases》):

? ? 此時非常簡單:數據行按對應ID列的順序儲存。如果我們新添加一行ID為8的數據,不會產生任何問題,新行會追加的末尾。

? ? 但如果我們想插入一行的ID為5的數據。

? ? ID為7,8的數據行必須向下移動。雖然在這算什么事兒,但當您的數據量達到數百萬行的級別之后,這就是個問題了。如果您還想要每秒處理上百次這種請求,那可真是難上加難了。

? ? 這就是GUID主鍵引發的問題:它是隨機產生的,所以在數據插入時,隨時都會涉及到數據的移動,導致插入會很緩慢,還會涉及大量不必要的磁盤活動。根據數據庫的存儲的相關知識,會帶如下兩點問題:

  • 空間的浪費以及由此帶來的讀寫效率的下降;

  • 更主要的,存儲的碎片化以及由此帶來的讀寫效率嚴重下降。

  • ? ? GUID最關鍵的問題就是它是隨機的。我們需要設計一種有規則的GUID生成方式,在之后生成的GUID類型總是比之前的要大,保證插入數據庫的主鍵是在表數據的末尾追加的,這種我們稱之為有序GUID。

    二、GUID排序規則

    ? ? 在講解有序GUID之前,我們必須先了解一下GUID在.Net中以及各個數據庫中的排序規則,排序規則不一樣,生成有序GUID的規則也會隨之變化。

    128位的GUID主要有4部分組成:Data1, Data2, Data3, and Data4,你可以看成下面這樣:“11111111-2222-3333-4444-444444444444”。

    ? ? Data1 占4個字節, Data2 2個字節, Data3 2個字節加 Data4 8個字節。我們分別的對各字節編上序號:

    GUID在.Net中的排序規則

    ? ? 在.Net中,GUID默認的排序規則是按左到右的,看下面這個示例。

    ? ? 輸出結果:

    ? ? 通過上面的輸出結果,我們可以得到排序的權重如下

    ? ? 這與數字排序規則一致,從右到左進行依次進行排序(數字越小,權重越高,排序的優先級越高)。

    GUID在各個數據庫中的排序規則

    ? ? 在SQL Server數據庫中,我們有一種非常簡單的方式來比較兩個GUID類型的大小值(其實在SQL Server數據庫中稱為UniqueIdentifier類型):

    ? ? 上面的例子來自Ferrari的博客《How are GUIDs sorted by SQL Server?》。

    ? ? 查詢結果:

    通過上面可以得到如下結果:

    • 先按每1-8從左到右進行排序;

    • 接著按第9-10位從右到左進行排序;

    • 最后按后11-16位從右到左進行排序;

    通過分析,我們可得到如下權重列表:

    ? ? 在Microsoft官方文檔中,有一篇文檔關于GUID與uniqueidentifier的值比較:《Comparing GUID and uniqueidentifier Values》。

    ? ? 不同的數據庫處理GUID的方式也是不同的。在SQL Server存在內置GUID類型,沒有原生GUID支持的數據庫通過模擬來方式來實現的。在Oracle保存為raw bytes類型,具體類型為raw(16);在MySql中通常將GUID儲存為char(36)的字符串形式。

    ? ? 關于Oracle、MySql數據庫的排序規則與.Net中排序規則,不過篇章的限制,這里不再做具體的演示,您可以自己進行測試。我們在這里只給出最終的結論:

    • .Net中GUID的排序規則是從左到右依次進行排序,與數字排序規則一致;

    • Sql Server數據庫提供對GUID類型的支持,在數據庫中稱為UniqueIdentifier類型,但是排序規則比較復雜:

      • 先按每1-8從左到右進行排序;

      • 接著按第9-10位從右到左進行排序;

      • 最后按后11-16位從右到左進行排序;

    • Oracle數據庫未提供對GUID類型的支持,使用的是raw bytes類型保存數據,真實類型為raw(16),排序規則是按Oracle二進制進行排序的;

    • MySql數據庫未提供對GUID類型的支持,使用的是字符串的類型保存數據,使用是的char(36)類型,由于使用的是字符串類型,排序規則與GUID在.Net中的規則一致。

    三、有序GUID

    ? ? 有序GUID是有規則的生成GUID,保證在之后生成的GUID的值總是比之前的要大。不過在上一節中,已經提到過各個數據庫對GUID支持不一樣,而且排序的規則也不一樣,所以我們需要為每一個數據庫提供不一致的有序GUID生成規則。

    UuidCreateSequential函數

    ? ? 我們都知道SQL Server數據庫有一個NewSequentialId()函數,用于創建有序GUID。在創建表時,可以將它設置成為GUID類型字段的默認值,在插入新增數據時自動創建主鍵的值(該函數只能做為字段的默認值,不能直接在SQL中調用)。示例如下:

    ? ? NewSequentialId()函數只能在數據庫使用,不過在 Microsoft 的 MSDN 文檔中有說明,NEWSEQUENTIALID 是對 Windows UuidCreateSequential 函數的包裝,https://msdn.microsoft.com/zh-cn/library/ms189786(v=sql.120).aspx。這樣我們可以在C#通過非托管方法調用:

    ? ? 但是上面的方法也存在三個問題:

    1、這個方法涉及到安全問題,UuidCreateSequential函數依賴的計算硬件,該方法的后12位其實是網卡的MAC地址。這是我電腦生成的一組有序GUID。


    ? ? 這是我本地電腦的網卡的MAC地址:

    2、由于UuidCreateSequential函數生成的有序GUID中包括MAC地址,所以如果在服務器集群環境中,肯定存在一臺服務器A上生成的有序GUID總比另一臺服務器B生成要更小,服務器A產生的數據插入到數據庫時,由于聚集索引的問題,總是會移動服務器B已經持久化到數據庫中的數據。集群的服務器越多,產生的IO問題更嚴重。在服務器群集環境中,需要自行實現有序GUID。

    3、UuidCreateSequential函數生成的GUID規則與SQL Server中排序的規則存在不一致,這樣仍然會導致嚴重的IO問題,所以需要將GUID重新排序后再持久化到數據庫。例如上面列出生成的GUID列表,依次生成的數據可以看出,是第4位字節在自增長,在這與任何一個數據庫的排序規則都不一致;關于該函數生成的規則,可以見此文章:https://stackoverflow.com/questions/5585307/sequential-guids。

    ? ? 下面的方法是將生成的GUID調整成為適合Sql Server使用的有序GUID(針對其它數據庫支持,您可以按排序規則自行修改):

    小結:
    ? ? UuidCreateSequential函數存在隱私的問題,不適合集群環境,并且需要重新排序后再提交到數據庫;

    COMB解決方案

    ? ? COMB 類型的GUID 是由Jimmy Nilsson在他的“The Cost of GUIDs as Primary Keys”一文中設計出來的。
    ? ? ?基本設計思路是這樣的:既然GUID數據生成是隨機的,會造成索引效率低下,影響了系統的性能,那么能不能通過組合的方式,保留GUID的前10個字節,用后6個字節表示GUID生成的時間(DateTime),這樣我們將時間信息與GUID組合起來,在保留GUID的唯一性的同時增加了有序性,以此來提高索引效率(這是針對Sql Server數據庫來設計的)。

    ? ? 在NHibernate框架中已經實現該功能,可以在github上看到實現方式:https://github.com/nhibernate/nhibernate-core/blob/master/src/NHibernate/Id/ GuidCombGenerator.cs#L45-L69。

    ? ? 在EF以及EF Core也同樣實現了類似的解決方案,EF Core的實現方式:https://github.com/aspnet/EntityFrameworkCore/blob/f7f6d6e23c8e47e44a61983827d9e41f2afe5cc7/src/EFCore/ValueGeneration/SequentialGuidValueGenerator.cs#L25-L44。

    ? ? 在這里介紹一下使用的方式,由EF Core框架自動生成有序GUID的方式:

    ? ? 但是請注意,這兩個ORM的解決方案只針對Sql Server數據庫,因為只保證了最后幾位字節是按順序來生成的。

    SequentialGuid框架

    ? ? SequentialGuid框架也是我要推薦給您,因為它提供了常見數據庫生成有序Guid的解決方案。

    ? ? 基本原理與COMB方案一樣,使用時間來保證有序GUID的順序,使用System.Security.Cryptography. RNGCryptoServiceProvider保證生成的數據的唯一性;關于該框架的設計思路以及針對各個數據庫的性能測試,見鏈接:https://www.codeproject.com/Articles/388157/GUIDs-as-fast-primary-keys-undermultiple-database。

    ? ? 使用方式,建議您參考ABP框架,在ABP中使用SequentialGuid框架來生成有序GUID,關鍵代碼鏈接:https://github.com/aspnetboilerplate/aspnetboilerplate/ blob/b36855f0c238c3592203f058c641862844a0614e/src/Abp/SequentialGuidGenerator.cs#L36-L51。

    四、總結

    ? ? 我們來總結一下:

    • 在數據庫中最好不要使用隨機的GUID,它會影響性能;

    • 在SQL Server中提供了NewSequentialId函數來生成有序GUID;

    • 各個數據庫對GUID支持的不一樣,而且排序的規則也不一樣;

    • UuidCreateSequential函數存在隱私的問題,不適合集群環境,并且需要重新排序后再提交到數據庫;

    • 各ORM框架提供了有序GUID的支持,但是其實只是針對Sql Server數據庫設計的;

    • 推薦您使用SequentialGuid框架,它解決了多數據庫以及集群環境的問題。

    ------ END ------


    作者簡介

    唐同學:?架構師,目前負責ERP運行平臺整體架構設計和開發。

    也許您還想看

    ERP緩存實踐經驗分享

    大數據列表頁面前端性能優化方案與實踐

    .Net最小工作線程對應用程序性能的影響

    成本計算引擎動態規則解析技術詳解

    總結

    以上是生活随笔為你收集整理的如何使用有序GUID提升数据库读写性能的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。