SQLTest系列之INSERT語句測試

風移發表於2016-11-17

場景引入

菜鳥不斷又猛又持久的給老鳥驚喜以後,老鳥開始不斷的折騰菜鳥:“鳥,你研究下有沒有一款可以測試MSSQL Server的工具吧?”。
“這還不簡單,用Red Gate的SQLTest唄”,於是菜鳥開始了工具的研究之旅:“要不,今天就分享下SQLTest之Insert語句測試吧”。

SQLTest簡介

領了任務的菜鳥,由於之前對這個工具有所瞭解,所以還是比較輕車熟路的。讓我們先來看看SQLTest是幹什麼的吧。
SQLTest是一款簡單易用,非常容易上手的SQL Server效能、壓力和單元測試工具。它既可以測試本地環境的SQL Server工作負載,也可以測試雲環境的SQL Server服務。

SQLTest一鍵安裝

SQLTest就是一個簡單的SQL Server測試工具,所以,它的安裝過程也簡單。官方推薦一鍵安裝,簡單到令人髮指的地步。
01.png

下載地址:
http://www.sqltest.org/Download

測試環境

在測試之前,菜鳥彙總自己的測試環境資訊:
CPU:4 cores
Memory:4 GB
Disk: SSD
SQL Server: SQL Server 2008R2 SP2

SQLTest INSERT語句測試

老實講,上面都不重要,看好了,這裡才是本文的重點:如何使用SQLTest來測試INSERT的效率呢?如何測試INSERT語句在不同執行緒數量下的效率?不同的資料型別選擇對INSERT效率的影響如何?
這裡虛擬一個場景,假設我們有一張名為Orders的訂單表,我們會根據Orders的主鍵資料型別的不同來測試INSERT的效率。

INT IDENTITY

建立測試資料庫和Orders表

use master;

IF DB_ID(`SQLTestDemo`) IS NULL
    CREATE DATABASE SQLTestDemo 
go

use SQLTestDemo
go
IF OBJECT_ID(`Orders`,`U`) IS NOT NULL
BEGIN
    TRUNCATE TABLE Orders
    DROP TABLE Orders
END 
GO
CREATE TABLE Orders (
OrderID INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED
, OrderDate datetime
, CustomerID int
, SourceID int
, StatusID int
, Amount decimal (18, 2)
, OrderDetails char(7000)
)
GO

所有準備工作就緒,菜鳥迫不及待的開始測試了,開啟SQLTest,設定SQLClient Connection String

Data Source=(local);Database=SQLTestDemo;Integrated Security=true;Pooling=false

SQL Command

insert into Orders values (getdate (), 1, 1, 1, 1, replicate (`a`, 7000)) 
go

Number of Threads
02.png

點選Start Current按鈕,測試時間10秒後,得到如下截圖:
1個執行緒執行10秒鐘,迭代了12471次,每次迭代消耗資料庫時間0.000秒。(由於這裡精確到千分之一秒,也就是一毫秒,說明每次迭代耗時少於1毫秒)。
03.png

現在我們分別將執行緒數調整為2,4,8,16,32,64,128,256來測試,為了測試的相對準確性,請在測試之前執行“建立測試資料庫和Orders表”中的程式碼,重新建立Orders表。SQLTest返回結果的設定方法如下:Settings => Workload Settings
04.png

測試完畢後,我們可以得到如下表格資料:
08.png
將這些資料繪製成直方圖和折線圖:
05.png

從這個圖中,可以很直觀的得出如下結論:

  • 從吞吐量來看:無輸出結果方式遠遠大於有輸出結果方式,前者是後者的兩倍還多;
  • 從資料庫平均耗時來看:無輸出結果效率也遠遠高於有輸出結果方式,後者是前者的兩倍;
  • 從執行緒數量來看:並不是執行緒數開得越多,SQL Server吞吐量越大,效率越高;無論是有輸出結果方式還是無輸出結果方式,併發8到16個執行緒SQL Server的吞吐量達到最大,效率最高;

注意:
最後一個結論不一定適用於所有的SQL Server,因為這個和SQL Server的版本,機器的CPU,Memory,磁碟等有密切的關係,使用者在得到這個值之前需要自己嚴格測試。

提供INT值

完成了主鍵值INT IDENTITY的測試後,菜鳥陷入了疑惑:每個執行緒如何插入不同的值呢?於是有了這個測試方法:

use SQLTestDemo
go
IF OBJECT_ID(`Orders`,`U`) IS NOT NULL
begin
    truncate table Orders
    drop table Orders
end
go
create table Orders (OrderID int primary key clustered
, OrderDate datetime
, CustomerID int
, SourceID int
, StatusID int
, Amount decimal (18, 2)
, OrderDetails char (7000)
)
go

讓每個執行緒生成不同的OrderID,我們可以使用SQLTest_Thread來代替執行緒數,SQLTest_Iteration代替迭代次數,最終將SQL Command修改為:

insert into Orders values (({SQLTest_Thread} * 100000) + {SQLTest_Iteration}, getdate(), 1, 1, 1, 1, replicate (`a`, 7000))
go

06.png

UNIQUEIDENTIFIER with NEWID()

測試方法類似於“INT IDENTITY”章節,只是Orders表結構和SQL Command不一致。

use SQLTestDemo 
go 
IF OBJECT_ID(`Orders`,`U`) IS NOT NULL
begin
    truncate table Orders
    drop table Orders
end
go 
create table Orders (
OrderID uniqueidentifier not null default newid () primary key clustered
, OrderDate datetime
, CustomerID int
, SourceID int
, StatusID int
, Amount decimal (18, 2)
, OrderDetails char (7000)) 
go
SQL Command
insert into Orders values (NEWID(),getdate (), 1, 1, 1, 1, replicate (`a`, 7000)) 
go

UNIQUEIDENTIFIER with NEWSEQUENTIALID()

同上,測試方法類似於“INT IDENTITY”章節,只是Orders表結構和SQL Command不一致。

use SQLTestDemo 
go 
IF OBJECT_ID(`Orders`,`U`) IS NOT NULL
BEGIN
    TRUNCATE TABLE Orders
    DROP TABLE Orders
END
GO 
CREATE TABLE Orders (
OrderID uniqueidentifier default newsequentialid () primary key clustered
, OrderDate datetime
, CustomerID int
, SourceID int
, StatusID int
, Amount decimal (18, 2)
, OrderDetails char (7000)) 
GO
SQL Command
insert into Orders(OrderDate,CustomerID,SourceID,StatusID,Amount,OrderDetails) values (getdate (), 1, 1, 1, 1, replicate (`a`, 7000)) 
go

總結

將四種資料型別在No Result輸出情況彙總統計如下表:
09.png
做一個漂亮炫酷的圖表出來對比下:
07.png
從這個圖示,我們可以發現如下規律:

  • 從吞吐量角度來看:所有資料型別,併發量聚集在8到16時,INSERT操作吞吐量達到最大值;
  • 吞吐量表現最好的是int identity資料型別和uniqueidentifier + newsequentialid做為主鍵的表;
  • 從資料庫平均耗時角度:所有資料型別,併發量在8到16時,INSERT操作的平均時間消耗最小,接近64個執行緒時,平均耗時會急劇上升;
  • 平均耗時表現最好的是int identity和Newsequentialid型別。

從結果來看,UNIQUEIDENTIFIER + NEWSEQUENTIALID和INT IDENTITY效能和吞吐量表現都非常好,我們到底該選擇哪一個更好一些呢? 我的結論是選擇IDENTITY屬性的數字型別欄位做為主鍵,因為它佔的空間更小,INT為4個位元組,BIGINT為8個位元組而UNIQUEIDENTIFIER 佔了36個位元組

寫在最後

老鳥看完菜鳥的研究報告,讚不絕口:“不錯啊,今天的最好表現就是明天對你的最低要求,SQLTest INSERT如何做引數化測試啊?”。
菜鳥賣起了關子:“鳥哥,預知後事如何,且聽下回分解”。


相關文章