SQL Server Memory Tables as Cache

Documentation

Starting with SQL Server 2016 (13.x), there is no limit on the size of memory-optimized tables, though the tables do need to fit in memory.

Memory tables in SQL Server are pretty close analogs to Redis

  • Will use as much memory as possible to stick entire table into memory
  • Backed by disk table or shadow file based table on disk (this is for recovery purpose of server restart which clears memory) can specify the durability to schema only or schema and data

Documentation and walkthroughs

Create the Database

This is done because at this time it is not possible to drop the MEMORY filegroup from a database once created, therefore keeping the database separate allows for the ability to delete the entire database to clear the filegroup

USE [model]
CREATE DATABASE DbCache
GO
USE [DbCache]
GO

Create the FileGroup for the Database

-- Add the file group
ALTER DATABASE DbCache 
ADD FILEGROUP DbCache_memgroup CONTAINS MEMORY_OPTIMIZED_DATA
GO

-- Find the path of the Database just created and replace folder for memory backing store
DECLARE @memFilePath nvarchar(MAX);
SELECT @memFilePath = REPLACE(physical_name, '\DbCache.mdf', '\Aggregator_MEM')
FROM sys.database_files
WHERE [name] = 'DbCache'

DECLARE @dynSql nvarchar(MAX)
-- Dynamic sql because of the above lookup for the path
SET @dynSql = 'ALTER DATABASE DbCache 
ADD FILE (name=''DbCache_MEM'', filename=''' + @memFilePath + ''') -- This path may be different on the database you are using
TO FILEGROUP DbCache_memgroup'

EXECUTE sp_executesql @dynSql;
GO

Create the Memory Table


-- Create the table
DROP TABLE IF EXISTS [dbo].[FastCache]
CREATE TABLE [dbo].[FastCache]
(
    Id              INTEGER             NOT NULL    IDENTITY(1,1),
    BatchId.        UNIQUEIDENTIFIER    NOT NULL    DEFAULT(NEWID()),
    JsonContents    NVARCHAR(MAX)       NOT NULL,
    OverrideCleanup BIT                 NOT NULL    DEFAULT(0),
    Created         DATETIME2           NOT NULL    DEFAULT(SYSUTCDATETIME()),
    CONSTRAINT [PK_FastCache] PRIMARY KEY NONCLUSTERED ([Id] ASC),
)
WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA);
GO
ALTER TABLE [dbo].[FastCache]
    ADD INDEX IX_FastCache_BatchId HASH ([BatchId]) WITH (BUCKET_COUNT = 131072)
GO

Add the following trigger

To mimic the Expires feature of Redis in an active manner as otherwise would need to create a scheduled job to do the same type of functionality as Redis

CREATE TRIGGER [dbo].[CacheExpiration]
	ON [dbo].[FastCache]
	WITH NATIVE_COMPILATION, SCHEMABINDING
	AFTER INSERT, UPDATE
AS BEGIN ATOMIC WITH
(
 TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english'
)
	DELETE 
	FROM [dbo].[FastCache]
	WHERE DATEDIFF(hh, [Created], SYSUTCDATETIME()) > 0
	AND [OverrideCleanup] = 0
END