Design6:選擇合適的數據類型

来源:http://www.cnblogs.com/ljhdo/archive/2016/09/20/5521043.html
-Advertisement-
Play Games

資料庫使用Table來存儲海量的數據,細分Table結構,數據最終存儲在Table Column中,因此,在設計Table Schema時,必須慎重選擇Table Column的Data Type,數據類型不僅決定了Column能夠存儲的數據範圍和能夠進行的操作,而且合適的數據類型還能提高查詢和修改 ...


資料庫使用Table來存儲海量的數據,細分Table結構,數據最終存儲在Table Column中,因此,在設計Table Schema時,必須慎重選擇Table Column的Data Type,數據類型不僅決定了Column能夠存儲的數據範圍和能夠進行的操作,而且合適的數據類型還能提高查詢和修改數據的性能。數據類型的選擇標準既要滿足業務和擴展性的需求,又要使行寬最小(行寬是一行中所有column占用的Byte)。最佳實踐:使用與Column最大值最接近的數據類型。

例如,bit 類型只能存儲1和0,能夠對bit進行邏輯比較(=或<>),不能對進行算術運算(+,-,*,/,%),不要對其進行>或<的比較,雖然bit類型支持,但是,這不 make sense。

declare @b1 bit
declare @b2 bit 

set @b1=1
set @b2=0

-- right,return 0
select iif(@b1=@b2,1,0)

--error,The data types bit and bit are incompatible in the add operator.
select @b1+@b2

在設計Table Schema時,要實現三大目標:占用空間少,查詢速度快,更新速度快。這三個目標有些千絲萬縷的關聯,設計良好的Table Schema,都會實現,反之,設計差的Table Schema,都不能實現。

記憶體是訪問速度最快的存儲介質,如果數據全部存儲在記憶體中,那會極大的提高資料庫系統的吞吐量,但是,每個資料庫系統能夠使用的記憶體有限,為了提高查詢性能,SQL Server將最近使用過的數據駐留在記憶體中。SQL Server 查詢的數據必須在記憶體中,如果目標數據頁不在記憶體中,那麼SQL Server會將數據從Disk讀取到記憶體中。SQL Server 響應時間跟數據載入很大的關係,如果載入的數據集占用的空間小,數據頁分佈集中,那麼SQL Server使用預讀機制,能夠很快將數據載入到記憶體,相應地,SQL Server的響應時間會很小。

創建索引能夠提高查詢性能,其實是因為,索引欄位比Base Table的欄位少,索引結構占用的存儲空間小,SQL Server 載入索引結構的耗時少。由於索引結構是有序的,避免了全表掃描,也能提高查詢性能。使用窄的數據類型,使用數據壓縮,創建BTree索引,創建ClumnStore 索引,都能減少數據集占用的存儲空間,提高數據載入到記憶體的速度。SQL Server在執行用戶的查詢請求時,每一行數據都必須在記憶體中,因此,數據集占用的空間越少,載入的過程越快,SQL Server的查詢性能越高。

一,窄的數據行會節省存儲空間,減少IO次數

使用窄的數據類型,使行的寬度達到最小,在存儲相同數據量時,能夠節省存儲空間,減少Disk IO的次數。

在存儲日期數據時,Date占用3Byte,DateTime占用8Byte,DateTime2(2)占用6Byte,DateTime2(4)占用7Byte,DateTime2(7)占用8Byte。不管從表示的精度上,還是從占用的存儲空間上來看,DateTime2(N)都完勝DateTime。

例如,存儲‘yyyy-mm-dd MM:HH:SS’格式的日期數據,有以下4中選擇:

  • 使用字元串 varchar(19) 或 nvarchar(19)存儲,十分不明智,前者占用19Byte後再占用38Byte;
  • 使用數據類型 datetime2(7)存儲,占用8Byte,雖然精度更高,但是毫秒都是0,浪費存儲空間;
  • 使用數據類型 datetime存儲,占用8Byte,如果需要存儲毫秒,datetime不滿足;
  • 使用數據類型 datetime2(2)存儲,占用6Byte,相比較是最理想的。

由於SQL Server存儲數據是按照row存儲數據的,每個Page能夠存儲的數據行是有限的。在查詢同等數量的數據行時,如果row寬度窄,那麼每個page會容納更多的數據行,不僅減少IO次數,而且節省存儲空間。

二,在窄的數據列上創建index,能夠提高查詢性能

在窄的數據列上創建Index,索引結構占用的存儲空間更小,SQL Server消耗更少的Disk IO就能將索引結構載入到記憶體中,能夠提高查詢性能。

在創建Index時,必須慎重選擇聚集索引鍵,主要有兩個原因

1,聚集索引其實就是表本身,SQL Server必須保持物理存儲順序和邏輯存儲順序一致

在SQL Server中,Clustered Index能夠確定Table的物理存儲,使Table的物理存儲順序和聚集索引鍵的邏輯順序保持一致。在對Table數據進行update時,如果更新聚集索引鍵,導致數據行所在聚集索引鍵必須移動,此時,SQL Server不能“原地更新”數據行,必須將數據行移動到其應有的物理位置上,Table的物理存儲順序和聚集索引鍵的邏輯順序才能保持一致。SQL Server將Update命令拆分成等價的delete命令和insert 命令。

示例:聚集索引鍵4被修改為8,那麼,SQL Server將數據行5刪除,然後再相應的位置上插入數據行8。

如果插入的位置上沒有多餘的存儲空間,那麼,插入操作會導致頁拆分,產生索引碎片,影響查詢性能。

2,NonClustered Index的葉子節點中,都包含Clustered Index鍵。

例如,在表上有兩個索引:Clustered Index(c1,c2),Nonclustered Index(c2,c3),實際上,Nonclustered index的索引定義(c2,c3)include(c1),即,在Nonclustered Index的葉子節點中,包含Clustered Index所有的Index Key。包含列和Index Key的區別在於,Index Key用於路由索引結構,而包含列用於返回數據,不提供搜索功能。

由於Clustered Index“無所不在”,Clustered Index的索引鍵最好創建在窄的,不變的,唯一的和只增長的數據列上。在創建Clustered Index時,最好是唯一索引(Unique Index)。窄的數據行會使每一個Index page存儲更多的index key,SQL Server Engine定位到某一行所經過的節點數更少,即導航的Path更短,載入和查詢速度更快。

由於每一個nonclustered index的Index pages或index key columns中都會包含Clustered Index key columns,如果Clustered Index key columns的寬度比較大,這會導致所有nonclustered index的索引樹占用較大的存儲空間,IO此次更多,更新和查詢都會變慢。

In general, it is best practice to create a clustered index on narrow, static, unique, and ever-increasing columns. This is for numerous reasons. First, using an updateable column as the clustering key can be expensive, as updates to the key value could require the data to be moved to another page. This can result in slower writes and updates, and you can expect higher levels of fragmentation. Secondly, the clustered key value is used in non-clustered indexes as a pointer back into the leaf level of the clustered index. This means that the overhead of a wide clustered key is incurred in every index created.

三,使用正確的數據類型,減少轉換的次數

在SQL Server中,對數據進行強制類型轉換或隱式類型轉換都需要付出代價,所以,使用正確的數據類型,避免類型轉換是十分必要的。例如,如果存儲的數據格式是‘yyyy-mm-dd MM:HH:SS’,雖然字元串類型和Datetime類型能夠隱式轉換,但是使用字元串類型 varchar(19)或 nvarchar(19)存儲是十分不明智的,不僅浪費存儲空間,而且隱式轉換對性能有負作用。

四,常見數據類型所占用的位元組數

數據類型大致分為四種:數值類型,日期和時間類型,字元串類型,GUID,使用DataLength()能夠查看任意數據類型的變數所占用的位元組數量

1,數值類型

對於整數類型,TinyInt 占用1Byte,表示的整數範圍是:0-255;SmallInt,int和bigint 分別占用2B,4B和8B。

對於小數類型,decimal(p,s)表示精確的小數類型,float(n)表示近似的小數類型,常用於表示百分比,除法的結果,有兩種類型float(24)占用4B,float(53)占用8B,參考《SQL Server的小數數值類型(float 和 decimal)用法》。

2,日期和時間類型

date表示日期,占用3B

Datetime2(n),根據時間的毫秒部分來確定占用的位元組數量:當n是1,或2時,占用6B;當n是3,或4時,占用7B;當n是5,6,或7時,占用8B。

datetime占用8B,建議使用datetime2(n)來替代datetime。

3,字元類型

建議使用變長字元類型,varchar和nvarchar,後者占用的位元組是前者一倍;如果數據中都是拉丁字母,使用varchar更好。

4,UniqueIdentifier數據類型

UniqueIdentifier數據類型占用16B,GUID的格式是8-4-4-4-12,即'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',不要使用varchar來存儲GUID。

例如,使用varchar來存儲GUID,將會浪費20B

declare @ui uniqueidentifier
declare @vc varchar(max)

set @ui=newid()
set @vc=cast(@ui as varchar(max))

select @ui,@vc,datalength(@ui),datalength(@vc)

如果表的數據行總量是5千萬,那麼使用varchar來存儲GUID將會浪費:953MB。在數據類型的選擇上,必須錙銖必較,能省就省。

五,示例

模擬一個場景:業務人員需要分析帖子,需要存儲的欄位:PostID,AuthorName,PostTitle,PostURL,PostContent,PostedTime。

在使用ETL同步社區爬蟲數據時,通常會額外增加兩個Column:DataCreatedTime和DataUpdatedTime,用於存儲新建數據行的時間和最後一次更新數據行的時間。

1,社區分析,通常涉及海量的數據,使用數據壓縮(data_compression=page),提高查詢性能。

2,增加代理鍵,使用代理鍵作為主鍵。

3,URL使用varchar類型,對於AuthorName,Title和Content需要使用unicode類型來存儲。

4,對於時間類型,精度不會很高,使用最節省的數據類型 datetime2(2)來存儲,錙銖必較。

5,將最占空間的PostContent和主表Posts分開,實際上是垂直分區,便於主表Posts的快速查詢。

create table dbo.Posts
(
PostID bigint identity(1,1) not null,
OriginalPostID bigint not null,
AuthorID int not null,
Title nvarchar(256) not null,
url varchar(2048) not null,
PostedTime datetime2(2) not null,
IsDeleted bit not null,
DataCreatedTime datetime2(2) not null,
DataUpdatedTime datetime2(2) not null,
constraint PK__Posts_ID primary key clustered(PostID)
)
with(data_compression=page);

create table dbo.Authors
(
AuthorID int Identity(1,1) not null,
OriginalAuthorID int not null,
Name nvarchar(128) not null,
DataCreatedTime datetime2(2) not null,
DataUpdatedTime datetime2(2) not null,
constraint PK__Authors_AuthorID primary key clustered(AuthorID)
)
with(data_compression=page);

create table dbo.PostContent
(
PostID int not null,
Content nvarchar(max) not null,
DataCreatedTime datetime2(2) not null,
DataUpdatedTime datetime2(2) not null,
constraint PK__PostContent_PostID primary key clustered(PostID)
)
with(data_compression=page);

 

推薦閱讀《Performance Considerations of Data Types》:
A clustered index created as part of a primary key will, by definition, be unique. However, a clustered index created with the following syntax,

CREATE CLUSTERED INDEX <index_name>
ON <schema>.<table_name> (<key columns>);

will not be unique unless unique is explicitly declared, i.e.

CREATE UNIQUE CLUSTERED INDEX <index_name>
ON <schema>.<table_name> (<key columns>);

In order for SQL Server to ensure it navigates to the appropriate record, for example when navigating the B-tree structure of a non-clustered index, SQL Server requires every row to have an internally unique id. In the case of unique clustered index, this unique row id is simply the clustered index key value. However, as SQL Server will not require a clustered index to be unique - that is, it will not prevent a clustered index
from accepting duplicate values - it will ensure uniqueness internally by adding a 4-byte uniquifier to any row with a duplicate key value.

In many cases, creating a non-unique clustered index on a unique or mostly unique column will have little-to-no impact. This is because the 4-byte overhead is only added to duplicate instances of an existing clustered key value. An example of this would be creating a non-unique clustered index on an identity column. However, creating a non-unique clustered index on a column with many duplicate values, perhaps on a column of date data type where you might have thousands of records with the same clustered key value, could result in a significant amount of internal overhead.
Moreover, SQL Server will store this 4-byte uniquifier as a variable-length column. This is significant in that a table with all fixed columns and a large number of duplicate clustered values will actually incur 8 bytes of overhead per row, because SQL Server requires 4 bytes to manage this variable column (2 bytes for the count of variable-length columns in the row and 2 bytes for the offset of the the variable-length column of the uniquifier column). If there are already variable-length columns in the row, the overhead is only 6 bytes—two for the offset and four for the uniquifier value. Also, this value will be present in all nonclustered indexes too, as it is part of the clustered index key.

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 從 Xcode 8.0 開始,目前所有的插件都無法工作! NSLog 無法輸出 -- 此bug等待正式版本... Xcode 提供了文檔註釋快捷鍵option + cmd + / 但是要把系統升級到10.11.5、 Interface Builder 界面構建器——加速 編輯器擴展 Editor E ...
  • 介紹:MD版的花瓣網App運行效果: <ignore_js_op> 源碼下載:http://code.662p.com/view/13784.html 使用說明:架構 這個項目在寫在很久之前,當時MVP架構網路上各種分析描述,但是感覺都是各說各的。不確定項目是否採用MVP架構就先動手寫代碼,隨時準備 ...
  • 詳細說明:http://android.662p.com/thread-6599-1-1.html 代碼我已經上到那個安卓教程網了,需要的朋友可以參考一下,該案例僅供參考和學習,不得用於商業使用,謝謝。 ...
  • 先看演示效果: 1 ViewPager類提供了多界面切換的新效果。 新效果有如下特征: [1] 當前顯示一組界面中的其中一個界面。 [2] 當用戶通過左右滑動界面時,當前的屏幕顯示當前界面和下一個界面的一部分。 [3]滑動結束後,界面自動跳轉到當前選擇的界面中 2 介紹裡面幾個比較重要的方法與介面 ...
  • 處理日期的常見情景 NSDate -> String & String -> NSDate 日期比較 日期計算(基於參考日期 +/- 一定時間) 計算日期間的差異 拆解NSDate對象(分解成year/month/day/hour/minute/second 等) NSDate相關類 NSDate ...
  • 視圖控制器是 UIViewController 類或其子類對象。每個視圖控制器都負責管理一個視圖層次結構,包括創建視圖層級結構中的視圖並處理相關用戶事件,以及將整個視圖層次結構添加到應用視窗。 創建一個程式,並將上節 JXHypnosisView 類導入到工程中。 創建 UIViewControll ...
  • 和Cocoapods相比各有利弊吧,具體對比參見: Carthage 初探:四大優勢與四大劣勢 第一步:如果沒有安裝Homebrew先安裝 打開命令終端,直接輸入以下命令回車 這樣它會自己安裝,期間會讓你輸入本機密碼 第二步:安裝Carthage 在命令終端,輸入以下命令回車 安裝完以後查看版本命令 ...
  • mysql5.x升級至mysql5.7後導入之前資料庫date出錯的解決方法! ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...