In-Memory:記憶體資料庫

来源:http://www.cnblogs.com/ljhdo/archive/2017/01/06/5762716.html
-Advertisement-
Play Games

在逝去的2016後半年,由於項目需要支持數據的快速更新和多用戶的高併發負載,我試水SQL Server 2016的In-Memory OLTP,創建記憶體資料庫實現項目的負載需求,現在項目接近尾聲,系統運行穩定,寫一篇博客,記錄一下使用記憶體資料庫的經驗。 SQL Server 2016的In-Memo ...


在逝去的2016後半年,由於項目需要支持數據的快速更新和多用戶的高併發負載,我試水SQL Server 2016的In-Memory OLTP,創建記憶體資料庫實現項目的負載需求,現在項目接近尾聲,系統運行穩定,寫一篇博客,記錄一下使用記憶體資料庫的經驗。

SQL Server 2016的In-Memory OLTP,通俗地講,是記憶體資料庫,使用記憶體優化表(Memory-Optimized Table,簡稱MOT)來實現,MOT駐留在記憶體中,使用 Hekaton 記憶體資料庫引擎訪問。在查詢MOT時,只從記憶體中讀取數據行,不會產生Disk IO消耗;在更新MOT時,數據的更新直接寫入到記憶體中。記憶體優化表能夠在Disk上維護一個數據副本,該副本只用於持久化數據,不用於數據讀寫操作。

在記憶體資料庫中,不是所有的數據都需要存儲在記憶體中,有些數據仍然能夠存儲在Disk上,硬碟表(Disk-Based Table,簡稱DBT)是傳統的表存儲結構,每個Page是8KB,在查詢和更新DBT時,產生Disk IO操作,將數據從Disk讀取到記憶體,或者將數據更新非同步寫入到Disk中。

記憶體資料庫將原本存儲在Disk上的數據,存儲在記憶體中,利用記憶體的高速訪問優勢實現數據的快速查詢和更新,但是,記憶體資料庫,不僅僅是存儲空間的變化,Hekaton 記憶體資料庫訪問引擎實現本地編譯模塊(Natively compiled),交叉事務(Cross-Container Transaction)和查詢互操作(Query Interop):

  • 本地編譯模塊:如果代碼模塊只訪問MOT,那麼可以將該模塊定義為本地編譯模塊,SQL Server直接將TSQL腳本編譯成機器代碼;SQL Server 2016支持本地編譯的模式有:存儲過程(SP),觸發器(Trigger),標量值函數(Scalar Function)或內嵌多語句函數(Inline Multi-Statement Function)。相比於解釋性(Interpreted)TSQL 模塊,機器代碼直接使用記憶體地址,性能更高。
  • 交叉事務:在解釋性TSQL模塊中,一個事務既能訪問硬碟表,也能訪問記憶體優化表;實際上,SQL Server創建了兩個事務,一個事務用於訪問硬碟表,一個事務用於訪問記憶體優化表,在DMV中,分別使用transaction_id 和 xtp_transaction_id 來標識。
  • 查詢互操作:解釋性TSQL腳本能夠訪問記憶體優化表和硬碟表,本地編譯模塊只能訪問記憶體優化表。

記憶體數據被整合到SQL Server關係引擎中,使用記憶體資料庫時,客戶端應用程式甚至感受不到任何變化,DAL介面也不需要做任何修改。由於Query Interop的存在,任何解釋性TSQL腳本都能透明地訪問MOT,只是性能沒有本地編譯TSQL腳本性能高。在使用分散式事務訪問MOT時,必須設置合適的事務隔離級別,推薦使用Read Committed,如果發生MSSQLSERVER_41333 錯誤,說明產生交叉事務隔離錯誤(CROSS_CONTAINER_ISOLATION_FAILURE),原因是當前事務的隔離級別太高。

一,創建記憶體資料庫

記憶體優化表的數據必須存儲在包含Memory_Optimized_Data的File Group中,該FileGroup可以有多個File,每個File實際上是Folder,一個DB只能創建一個包含Memory_Optimized_Data的File Group。

step1,創建一個資料庫,創建的Data File的數量最好和CPU內核數量保持一致,存放在不同的物理磁碟上;

use master 
go 

--Create database
create database Test_MemboryDB
on Primary
(
name=Test_MemoryDB_1,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_1.mdf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_2,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_2.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_3,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_3.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_4,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_4.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_5,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_5.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_6,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_6.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_7,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_7.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_8,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_8.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_9,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_9.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_10,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_10.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_11,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_11.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_12,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_12.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_13,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_13.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_14,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_14.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_15,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_15.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_16,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_16.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_17,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_17.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_18,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_18.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_19,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_19.ndf',
size=5GB,
FileGrowth=1GB
),
(
name=Test_MemoryDB_20,
filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_20.ndf',
size=5GB,
FileGrowth=1GB
)
LOG ON 
( 
name = N'Test_MemboryDB_log', 
filename = N'D:\Program Files\Microsoft SQL Server\Test_MemboryDB_log.ldf' , 
size = 10GB , 
filegrowth = 1GB 
)
GO
View Code

step2,為資料庫創建一個包含記憶體優化數據的FileGroup,向該FileGroup中添加“File”,實際上是目錄(Directory),用於存儲記憶體優化數據文件,主要是CheckPoint文件,用於還原持久化的記憶體優化表。

-- Add File Group from memory-optimized data
alter database [Test_MemboryDB]
add filegroup fg_MemoryOptimizedData
contains MEMORY_OPTIMIZED_DATA;

alter database [Test_MemboryDB]
add file 
(
name=Test_MemboryDBDirectory,
filename='D:\Program Files\Microsoft SQL Server\Test_MemboryDBDirectory'
)
to FILEGROUP fg_MemoryOptimizedData;

文件組屬性:CONTAINS MEMORY_OPTIMIZED_DATA 子句,指定File Group用於存儲記憶體優化表數據,每個資料庫只能指定一個存儲記憶體優化數據的File Group,可以在該File Group下創建多個Directory,分佈在不同的物理Disk上,加快記憶體優化表數據還原的速度。

二,創建記憶體優化表

記憶體優化表用於存儲用戶數據,可以持久化存儲,數據存儲在記憶體中,同時,在Disk上維護數據的一個副本,通過選項 DURABILITY= SCHEMA_AND_DATA 指定持久化存儲記憶體優化表;也可以只存儲在記憶體中,通過選項DURABILITY= SCHEMA_ONLY指定。在記憶體優化表上,可以創建nonclustered index 或nonclustered hash index,每個記憶體優化表中至少創建一個Index。

--create memory optimized table
create table [dbo].[products]
(
    [ProductID] [bigint] not null,
    [Name] [varchar](64) not null,
    [Price] decimal(10,2) not null,
    [Unit] varchar(16) not null,
    [Description] [varchar](max) null,
    constraint [PK__Products_ProductID] primary key nonclustered hash ([ProductID])with (bucket_count=2000000)
    ,index idx_Products_Price  nonclustered([Price] desc)
    ,index idx_Products_Unit nonclustered hash(Unit) with(bucket_count=40000)
)
with(memory_optimized=on,durability= schema_and_data)
go

1,記憶體優化:MEMORY_OPTIMIZED

[MEMORY_OPTIMIZED = {ON | OFF}]

預設值是OFF,指定創建的表是硬碟表;設置選項MEMORY_OPTIMIZED為ON,指定創建的表是記憶體優化表;

2,持久性:Durability 

DURABILITY = {SCHEMA_ONLY | SCHEMA_AND_DATA}

預設值是SCHEMA_AND_DATA,指定創建的記憶體優化表是持久化的,這意味著,數據更新會持久化存儲到Disk上,在SQL Server重啟之後,記憶體優化表的數據能跟根據存儲在Disk上的副本還原。選項 SCHEMA_ONLY 指定創建的記憶體優化表是非持久化的,這意味著Table Schema是持久化存儲到Disk上,但是,任何數據更新都不會持久化到Disk上,在SQL Server重啟之後,記憶體優化表的數據會丟失。

3,哈希索引和範圍索引

記憶體優化表支持Hash Index,屬性 BUCKET_COUNT 指定為Hash Index創建的bucket的數量,一般hash bucket的數量是數據行的1-2倍,如果無法估計bucket的數量,請創建範圍索引(NonClustered Index),索引結構是Bw-Tree。

Hash 索引由一個數組和多個數據行鏈組成,每一個數組元素叫做一個Hash Bucket,通過內置的Hash函數,將Hash索引的Key映射到Hash Bucket上,例如,如果Hash Index的Key是(Col1,Col2),根據HashFunction(Col1,Col2)返回的Hash Value,將數據行映射到指定的Hash Bucket上;如果多個Key映射到同一個Hash Bucket上,那麼這些Key組成一個鏈。例如:數據表結構是(Name,City),在Name欄位上創建Hash Index,Hash值相同的數據行鏈接成一個單向鏈。

三,創建Natively Compiled SP

本地編譯SP在創建時編譯成機器代碼,整個SP以原子方式執行,這意味著,以SP為單位,整個SP中的所有操作是一個原子操作,要麼執行成功,要麼執行失敗。

create procedure dbo.usp_GetProduct
    @ProductID bigint not null
with native_compilation, schemabinding, execute as owner
as
begin atomic with (transaction isolation level = snapshot, language = N'US_English')  
select  [ProductID]
      ,[Name]
      ,[Price]
      ,[Unit]
      ,[Description]
from [dbo].[Products]
where ProductID=@ProductID
end
go 

1,在本地編譯SP中,能夠為參數,變數指定Nullability屬性,預設值是NULL

NOT NULL 屬性:不能為參數或變數指定NULL值,

  • 在本便編譯SP中,為參數指定NOT NULL屬性,不能為參數指定NULL值;
  • 在本便編譯SP中,為變數定義NOT NULL屬性,必須在Declare時初始化變數;

2,本地編譯SP必須包含兩個選項:SCHEMABINDING 和 ATOMIC Block

  • SCHEMABINDING:綁定引用的記憶體優化表
  • ATOMIC Block:在原子塊中的所有語句,以單個事務運行;在事務成功時,所有語句都提交成功;在事務失敗時,所有語句都回滾。Atomic Bloc保證原子地執行SP,如果SP在其他事務的上下文中被調用,那麼該SP開始一個新的事務。
    • Atomic blocks guarantee atomic execution of the stored procedure. If the procedure is invoked outside the context of an active transaction, it will start a new transaction, which commits at the end of the atomic block.

使用Atomic Block必須設置兩個選項:

  • TRANSACTION ISOLATION LEVEL:指定Atomic Block開啟事務的隔離級別,通常指定Snapshot隔離級別;
  • LANGUAGE:指定SP上下文的語言;

3,解釋型SP和本地編譯SP的區別

解釋性SP能夠訪問硬碟表(Disk-Based Table)和記憶體優化表(Memory-Optimized Table),其真正的區別是解釋性(Interpreted)SP在第一次執行時編譯,而本地編譯(Natively Compiled)SP是在創建時編譯,並且直接編譯成機器代碼,綁定的是記憶體地址。

4,延遲持久化

在本地編譯SP中,設置Atoic Block的選項:DELAYED_DURABILITY = ON ,使SP對記憶體優化表的更新操作,以非同步寫事務日誌方式,延遲持久化到Disk,這意味著,如果記憶體優化表維護了一個Disk-Based 的副本,數據在記憶體中修改之後,不會立即更新到Disk-Based 的副本中,這有丟失數據的可能性,但是能夠減少Disk IO,提高數據更新的性能。

四,使用記憶體優化的表變數和臨時表

傳統的表變數和臨時表,都使用tempdb存儲臨時數據,而tempdb不是記憶體資料庫,使用Disk存儲臨時表和表變數的數據,會產生Disk IO和競爭,SQL Server提供了記憶體優化的表變數,將臨時數據存儲在記憶體中,詳細信息,請參考我的博客:《In-Memory:在記憶體中創建臨時表和表變數》。

五,在記憶體資料庫中使用JSON

自從使用JSON之後,我的第一感概是:資料庫豈能沒有JSON,不管是資料庫將值傳遞前端,還是前端將數據傳遞到資料庫,使用JSON方便很多,相比XML,JSON的使用簡單很多,詳細信息,請參考我的博客:《使用TSQL查詢和更新 JSON 數據

六,記憶體資料庫的事務處理

交叉事務是指在一個事務中,解釋性TSQL語句同時訪問記憶體優化表(Memory-Optimized Table,簡稱MOT)和硬碟表(Disk-Based Table,簡稱DBT)。在交叉事務中,訪問MOT的操作和訪問DBT的操作都擁有自己獨立的事務序號,就像在一個大的交叉事務下,存在兩個單獨的子事務,分別用於訪問MOT和DBT;在sys.dm_db_xtp_transactions (Transact-SQL)中,訪問DBT的事務使用transaction_id標識,訪問MOT的事務序號使用xtp_transaction_id標識。詳細信息,請參考我的博客:《In-Memory:記憶體優化表的事務處理

 

參考文檔:

In-Memory OLTP (In-Memory Optimization)

Introduction to Memory-Optimized Tables

Natively Compiled Stored Procedures

Memory-Optimized Tables

試試SQLSERVER2014的記憶體優化表

SQLServer 2014 記憶體優化表

SQL Server 2014 記憶體優化表(1)實現記憶體優化表

CREATE TABLE (Transact-SQL)

CREATE PROCEDURE (Transact-SQL)

Creating Natively Compiled Stored Procedures


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

-Advertisement-
Play Games
更多相關文章
  • 【原】Android熱更新開源項目Tinker源碼解析系列之一:Dex熱更新 Tinker是微信的第一個開源項目,主要用於安卓應用bug的熱修複和功能的迭代。 Tinker github地址:https://github.com/Tencent/tinker 首先向微信致敬,感謝毫無保留的開源出了這 ...
  • 學習oracle也有一段時間了,發現oracle中的函數好多,對於做後臺的程式猿來說,大把大把的時間還要學習很多其他的新東西,再把這些函數也都記住是不太現實的,所以總結了一下oracle中的一些常用函數及示例,一是為了和大家分享,二是可以在以後工作中忘記了隨時查閱。廢話不多說,下麵直接上函數。 一. ...
  • 彙總篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 第一次引入文件組的概念:http://www.cnblogs.com/dunitian/p/5276431.html 上次說了其他的解決方案(http://www.cnblogs.com/ ...
  • SQL Server 2008 R2安裝時選擇的是windows身份驗證,未選擇混合身份驗證的解決辦法。 ...
  • 1. 本月的第一天SELECT DATEADD(mm, DATEDIFF(mm,0,getdate()), 0) 2. 本月的最後一天SELECT dateadd(ms,-3,DATEADD(mm, DATEDIFF(m,0,getdate())+1, 0)) 3.上個月的第一天select dat ...
  • --但是索引本質到底是什麼? --百度百科: --索引是為了加速對錶中數據行的檢索而創建的一種分散的存儲結構。索引是針對錶而建立的,它是由數據頁面以外的索引頁面組成的,每個索引頁面中的行都會含有邏輯指針,以便加速檢索物理數據。 --在資料庫關係圖中,可以在選定表的“索引/鍵”屬性頁中創建、編輯或刪除 ...
  • 1.前言 上篇 寫出易調試的SQL , 帶來了一些討論, 暴露了不能重用執行計劃和sql註入問題, 十分感謝園友們的建議 . 經過調整後 ,將原來的SQLHelper 抓SQL 用做調試環境用, 發佈環境還是走Dapper的參數化查詢, 保持原有優勢. 見如下代碼. 2. 在開發調試階段 抓最終SQ ...
  • 該視頻意在讓所有學員一次通過考試,避免重覆考試而承擔的巨額考試費用! 目前MongDB發展迅猛,有趕超mysql,和oracle看齊的苗頭。在這個時候MongoDB也適時的推出了官方的認證考試“MongoDB Certified DBA Associate Level”。該證書和OCP一樣,值得擁有 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...