SQL Server On Linux(21)—— SQL Server On Linux性能(7)——性能進階簡介——分區(1)

本人新書上市,請多多關照:《SQL Server On Linux運維實戰 2017版從入門到精通》

在這裏插入圖片描述

本篇開始專門對性能進行一系列講解,這一系列不限於Linux平臺,更多的是針對SQL Server本身。

SQL Server性能新特性

  SQL Server發展至今,爲了不斷提升性能,引入了不少最新技術。它們主要以分區表/索引,In-Memory OLTP 和列存儲索引這三類核心技術爲主。雖然這些技術有各自的使用場景,但是都具有一個共同的目標:針對關鍵系統大幅度提高性能。這些功能不僅僅是提升性能,而且還是大幅度提升!

  接下來會簡單介紹這三種技術,有機會的話再細說具體技術。由於工作需要,首先從分區表和分區索引入手。

分區表和分區索引

  這個嚴格意義來說不能稱爲“新技術“,因爲從SQL Server 2005開始就有了。而且業界很多數據庫產品都有分區功能。分區的出現源自於大量關係數據的出現。當一個表存儲了億級(請更新你對幾百萬行數據就成爲“大表“的叫法),增刪改查的性能將明顯下降,其維護成本(比如備份/重建索引/更新統計信息等等)都會變得非常大。
  因此,業界通常會對錶進行拆分操作。拆分通常有***垂直拆分***和***橫向拆分***, 垂直拆分大概就是把一個有很多列的表,按照業務邏輯,拆成多個行數相等但是列數更少的表。這種拆分***並不減少數據行數***,但是可以減少列數,可以減少很多開銷,特別是索引相關的開銷。橫向拆分也是根據業務,把數據按某些條件來拆分搬到別的相同結構的新表中,比如按照日期、地區等,把一個存有全國一年數據的表,拆成34個表(按照省級行政區域),也可以拆成12個表(按照月份)或者綜合這兩種方法,使其體積降到原有的1/n,大大減少單表體積,增大數據操作的性能。
  其中垂直拆分通常會針對明細表或者DW類型的寬表,因爲這個方式更多是減少列數。但是對於常規的OLTP系統而言,其問題往往在於行數太多,這時候通常用橫向拆分,也就是把表的部分數據搬到其他表中。
  在SQL Server 2000時代,只能通過把數據拆到“實體”表中,實現橫向拆分,但是這種方式下,表的個數可能會突增,而且需要很多額外維護操作,在編寫SQL語句時,也需要使用類似UNION/UNION ALL或者視圖等方式來合併起來,同時需要記住某些篩選條件需要訪問哪些具體的實體表,總而言之,用起來很不友好。
  從SQL 2005開始,引入了分區技術,簡單來說,它就是由SQL Server幫你管理分區的功能,最大限度降低運維和使用成本的前提下面,減少最終直接操作的表的數據行數。不過需要提醒的是,分區實際上跟垂直和橫向拆分是不一樣的。
  從業務出發,有一些數據天生就是可被“切片”的。比如前面提到的地區、時間,或者種類這些相對固化的特性。使用分區之前,先了解一下技術概念:

  1. 分區函數(partition function):用於通過使用一個範圍的值來定義分區的數量和分區的範圍(邊界) 。就是定義哪個列的哪裏到哪裏的值屬於某個分區。(定義範圍)
  2. 分區方案(partition scheme):定義分區函數使用那些文件組,通常一個分區會映射到一個或多個“用戶”文件組上,但是這個過程只能用TSQL命令實現。(定義物理存儲)
  3. 分區列(partition column):是具體分區的依據,分區函數配合這個列及其值來做分區,最常用的分區列就是日期了。

  很多人聽說過,分區的優點源自於數據量的減少。但是從技術來說,應該是來自於叫“partition elimination”(分區消除)。
但是很多人又可能認爲,一旦出現了掃描操作,那麼就意味着沒有使用partition elimination,後面將會演示一下分區消除的內容。

分區演示

  在本篇中,我們先快速演示一下簡單的分區創建:
  我們使用WideWorldImporters庫做演示,因爲這個庫已經做了相應的分區,所以我們這裏就不實操創建過程,不過可以展示一下,我們可以在數據庫的【存儲】看到創建了什麼分區函數和分區方案: 在這裏插入圖片描述
  然後查看紅框的兩個地方看一下它們具體的實現:

在這裏插入圖片描述
  導出來的腳本如下:

USE [WideWorldImporters]
GO

/****** Object:  PartitionFunction [PF_TransactionDate]    Script Date: 2020/1/8 11:20:30 ******/
CREATE PARTITION FUNCTION [PF_TransactionDate](date) AS RANGE RIGHT 
FOR VALUES (N'2014-01-01T00:00:00.000', N'2015-01-01T00:00:00.000', 
N'2016-01-01T00:00:00.000', N'2017-01-01T00:00:00.000')
GO


USE [WideWorldImporters]
GO

/****** Object:  PartitionScheme [PS_TransactionDate]    Script Date: 2020/1/8 11:20:13 ******/
CREATE PARTITION SCHEME [PS_TransactionDate] AS PARTITION [PF_TransactionDate] 
TO ([USERDATA], [USERDATA], [USERDATA], [USERDATA], [USERDATA], [USERDATA])
GO

  下面來解釋一下:

  1. 分區函數定義了分區是基於一個date類型的列,同時指定了5個分區,每個分區是一個自然年。RANGE RIGHT意味着第五個分區是所有≥2017-01-01的值。
  2. 分區方案把分區函數映射到USERGROUP的文件組中,但是這個文件組包含了5個USERDATA的文件。這種方式可以分攤I/O性能和減緩單個磁盤的空間壓力。並且可以通過使用“文件組”備份來處理超大規模的數據庫備份工作。

  現在回到表上面,我們導出表的腳本看看:

CREATE TABLE [Sales].[CustomerTransactions](
      [CustomerTransactionID] [int] NOT NULL,
      [CustomerID] [int] NOT NULL,
      [TransactionTypeID] [int] NOT NULL,
      [InvoiceID] [int] NULL,
      [PaymentMethodID] [int] NULL,
      [TransactionDate] [date] NOT NULL,
      [AmountExcludingTax] [decimal](18, 2) NOT NULL,
      [TaxAmount] [decimal](18, 2) NOT NULL,
      [TransactionAmount] [decimal](18, 2) NOT NULL,
      [OutstandingBalance] [decimal](18, 2) NOT NULL,
      [FinalizationDate] [date] NULL,
       [IsFinalized]  AS (case when [FinalizationDate] IS NULL then 
CONVERT([bit],(0)) else CONVERT([bit],(1)) end) PERSISTED,
      [LastEditedBy] [int] NOT NULL,
      [LastEditedWhen] [datetime2](7) NOT NULL,
 CONSTRAINT [PK_Sales_CustomerTransactions] PRIMARY KEY NONCLUSTERED
(
      [CustomerTransactionID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, 
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [USERDATA]
) ON [PS_TransactionDate]([TransactionDate])
GO
CREATE CLUSTERED INDEX [CX_Sales_CustomerTransactions] ON [Sales].
[CustomerTransactions]
(
      [TransactionDate] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, 
DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = 
ON) ON [PS_TransactionDate]([TransactionDate])
GO

  表上的非聚集索引是基於CustomerTransactionID,但是分區是基於TransactionDate。同時聚集索引也是TransactionDate列,稱之爲“對齊(align)”

  在表中的數據是根據元數據,並且按照分區函數中的範圍來劃分分區。分區函數和分區方案都是獨立的,所以可以被重用,如果你查看[Purchasing].[SupplierTransactions]表,可以看到它和CustomerTransaction使用同樣的分區函數和分區方案。

  分區之後,來看看性能表現:

SET STATISTICS IO ON
GO
SET STATISTICS XML ON
GO
SELECT COUNT(*) FROM Sales.CustomerTransactions
WHERE TransactionDate between '2013-01-01' and '2014-01-01'
GO

  開啓執行計劃後,我們看一下XML格式的執行計劃:

在這裏插入圖片描述

  PartitionAccessed表名訪問了2個分區,然後分別是1和2(看PartitionRange)。這個表名即使執行計劃裏面是掃描,底層執行的時候也不會真的需要訪問全表。

小結

  到這裏爲止,我演示了簡單的分區創建和簡單的查詢,主要目的是一種引入,接下來的文章會對分區做一個比較深入的介紹。

發佈了171 篇原創文章 · 獲贊 1267 · 訪問量 249萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章