<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Edwin M SarmientoIn-Memory OLTP in SQL Server: Logging Comparison with Disk-Based Tables &#8211; Edwin M Sarmiento</title>
	<atom:link href="https://www.edwinmsarmiento.com/in-memory-oltp-in-sql-server-logging/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.edwinmsarmiento.com</link>
	<description>Intentional Excellence</description>
	<lastBuildDate>Sun, 19 Jul 2026 15:09:22 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
<site xmlns="com-wordpress:feed-additions:1">84283043</site>		<item>
		<title>In-Memory OLTP in SQL Server: Logging Comparison with Disk-Based Tables</title>
		<link>https://www.edwinmsarmiento.com/in-memory-oltp-in-sql-server-logging/</link>
		<comments>https://www.edwinmsarmiento.com/in-memory-oltp-in-sql-server-logging/#respond</comments>
		<pubDate>Mon, 29 Jun 2015 01:21:16 +0000</pubDate>
		<dc:creator>Edwin M Sarmiento</dc:creator>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[In-Memory OLTP]]></category>
		<category><![CDATA[SQL Server 2014]]></category>
		<category><![CDATA[transaction log]]></category>
		<guid isPermaLink="false">http://www.edwinmsarmiento.com/?p=1746</guid>

				<description><![CDATA[In a previous blog post, I talked about Partition-Level Online Index Operations in SQL Server 2014 and how it affects high availability and disaster recovery. With more features being added in every new version of SQL Server, it&#8217;s important to understand how its implementation will affect your overall objectives. One feature that got a lot [&#8230;]]]></description>
					<content:encoded><![CDATA[<img width="760" height="233" src="https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize-760x233.jpg" class="featured-image wp-post-image" alt="" srcset="https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize-760x233.jpg 760w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize-300x92.jpg 300w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize-1024x314.jpg 1024w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize-518x159.jpg 518w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize-82x25.jpg 82w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize-600x184.jpg 600w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize.jpg 1104w" sizes="(max-width: 760px) 100vw, 760px" /><p>In a previous blog post, I talked about <a href="https://www.edwinmsarmiento.com/partition-level-online-index-operations-in-sql-server-2014-and-its-impact-in-hadr/" target="_blank">Partition-Level Online Index Operations in SQL Server 2014</a> and how it affects high availability and disaster recovery. With more features being added in every new version of SQL Server, it&#8217;s important to understand how its implementation will affect your overall objectives. One feature that got a lot of attention when it was introduced was <a href="https://msdn.microsoft.com/en-us/library/dn133186.aspx" target="_blank">In-Memory OLTP</a>.</p>
<p>In-Memory OLTP, in its simplest form, is a memory-optimized database engine integrated within SQL Server designed for OLTP workloads. With the trends in cost reduction for memory and CPU resources, the timing is just right for this feature. I&#8217;ve shown this feature in some of the workshops that I&#8217;ve done, demonstrating how in-memory OLTP can increase performance of high-volume transactions. You can try it out for yourself by following the workflow of the scripts provided in this <a href="https://msdn.microsoft.com/en-us/library/dn530757(v=sql.120).aspx" target="_blank">MSDN article</a>. And while there are a lot of limitations for in-memory OLTP (it&#8217;s the first version, anyway) it&#8217;s worth evaluating it as you prepare to migrate your database workloads.</p>
<p>Most of the blog posts and articles that pertain to in-memory OLTP focus on performance improvements. The focus of this blog post is mainly on how logging works with in-memory OLTP.</p>
<h3>Let&#8217;s start with logging</h3>
<p>Every transaction in a SQL Server database gets logged in the transaction log (LDF) file to ensure durability and consistency. SQL Server uses a <a href="https://technet.microsoft.com/en-us/library/ms186259(v=sql.105).aspx" target="_blank">write-ahead log (WAL)</a> which guarantees that no data modification is written to the data file (MDF/NDF/etc.) before the associated log record is written to the LDF file. The log records associated with a transaction are written to the LDF file regardless of whether or not the transaction committed. When a page is modified (and reflected in the LDF file) but not yet persisted in the MDF/NDF file, it is considered to be a <em><strong>dirty page</strong></em>. During regular checkpoints, committed transactions written on the LDF file are then persisted to the MDF/NDF file. When recovery (or crash recovery) runs, SQL Server reads the LDF file to see if there are committed and/or uncommitted transactions and recovers the database to a state that is consistent to what is written in the LDF file. This is the main reason why I consider the LDF file to be the most important file in a SQL Server database.</p>
<p>But this is for traditional transactions that are executed against traditional tables (I just like calling them that to differentiate them with the newer <em>watchamacallit</em>.) With the introduction of in-memory OLTP, you now have disk-based (traditional) tables and memory-optimized tables. I&#8217;m going to refer to them as such since that&#8217;s what SQL Server Books Online calls them.</p>
<h3>Logging for transactions against memory-optimized tables</h3>
<p>When you create an in-memory table and run transactions against it, <span style="color: #800000;"><strong>the transactions do not use write-ahead logging</strong></span> such as when we deal with transactions against disk-based tables. Also, the log records pertaining to transactions against memory-optimized tables are only generated when the transaction commits. Dirty pages are never written to the LDF file. This means that there are fewer transaction log records generated compared to disk-based tables. Fewer transaction log records mean faster log send to Availability Group replicas, fewer log records to roll forward during crash recovery,  fewer log records read for replication subscribers, size of your backups, etc. There is more to what really happens during restore and recovery with in-memory OLTP but will save that for future blog posts.</p>
<p>Let&#8217;s look at an example code to compare the log records generated between disk-based tables and memory-optimized tables. We&#8217;ll create a database that will contain both disk-based and memory-optimized tables.</p>
<div style="background-color:#eeeeee;border:1px solid #D6D6D6;font-family:arial,helvetica,sans-serif;font-size:15px;line-height:20px;margin:8px 0 20px;padding:15px 20px;"><code style="font-size: 14px;"><span style="color: blue;">CREATE DATABASE </span><span style="color: black;">testDB<br />
GO<br />
</span><span style="color: green;">--Create a new filegroup for the in-memory optimized objects<br />
</span><span style="color: blue;">ALTER DATABASE </span><span style="color: black;">testDB </span><span style="color: blue;">ADD </span><span style="color: black;">FILEGROUP [testDB_mod] </span><span style="color: blue;">CONTAINS </span><span style="color: black;">MEMORY_OPTIMIZED_DATA<br />
</span><span style="color: blue;">ALTER DATABASE </span><span style="color: black;">testDB </span><span style="color: blue;">ADD FILE </span><span style="color: gray;">(</span><span style="color: black;">name </span><span style="color: blue;">= </span><span style="color: black;">[testDB_dir]</span><span style="color: gray;">, </span><span style="color: black;">filename</span><span style="color: blue;">= </span><span style="color: red;">'C:\DBFiles\testDB_mod.ndf'</span><span style="color: gray;">) </span><span style="color: blue;">TO </span><span style="color: black;">FILEGROUP testDB_mod</span><span style="color: gray;">;<br />
</span><span style="color: black;">GO<br />
</span><span style="color: green;">--Create in-memory table<br />
</span><span style="color: blue;">USE </span><span style="color: black;">testDB<br />
GO<br />
</span><span style="color: green;">--table (memory-optimized) to store sales information<br />
</span><span style="color: blue;">CREATE TABLE </span><span style="color: black;">[dbo].[Sales_InMemory]</span><span style="color: gray;">(<br />
</span><span style="color: black;">[SalesID] [int] </span><span style="color: gray;">NOT NULL </span><span style="color: blue;">PRIMARY KEY NONCLUSTERED </span><span style="color: black;">HASH </span><span style="color: blue;">WITH </span><span style="color: gray;">(</span><span style="color: black;">BUCKET_COUNT </span><span style="color: blue;">= </span><span style="color: black;">1000000</span><span style="color: gray;">),<br />
</span><span style="color: black;">[CustomerID] [int] </span><span style="color: gray;">NOT NULL,<br />
</span><span style="color: black;">[DateCreated] [datetime2] </span><span style="color: gray;">NOT NULL,<br />
</span><span style="color: black;">[TotalPrice] [money]<br />
</span><span style="color: gray;">) </span><span style="color: blue;">WITH </span><span style="color: gray;">(</span><span style="color: black;">MEMORY_OPTIMIZED</span><span style="color: blue;">=ON</span><span style="color: gray;">, </span><span style="color: black;">DURABILITY </span><span style="color: blue;">= </span><span style="color: black;">SCHEMA_AND_DATA</span><span style="color: gray;">);<br />
</span><span style="color: black;">GO<br />
</span></code></div>
<p>Let&#8217;s populate the memory-optimized table with data. I&#8217;m naming the transaction so it will be easier to search for it in the LDF file.</p>
<div style="background-color:#eeeeee;border:1px solid #D6D6D6;font-family:arial,helvetica,sans-serif;font-size:15px;line-height:20px;margin:8px 0 20px;padding:15px 20px;"><code style="font-size: 14px;"><span style="color: green;">--populate table with sample data<br />
</span><span style="color: blue;">BEGIN TRAN </span><span style="color: black;">InMemory<br />
</span><span style="color: blue;">DECLARE </span><span style="color: #434343;">@SalesID </span><span style="color: blue;">INT = </span><span style="color: black;">0</span></code></p>
<p><span style="color: blue;">WHILE </span><span style="color: #434343;">@SalesID </span><span style="color: gray;">&lt; </span><span style="color: black;">1000<br />
</span><span style="color: blue;">BEGIN<br />
INSERT </span><span style="color: black;">[dbo].[Sales_InMemory] </span><span style="color: blue;">VALUES </span><span style="color: gray;">(</span><span style="color: #434343;">@SalesID</span><span style="color: gray;">,</span><span style="color: black;">1</span><span style="color: gray;">,</span><span style="color: magenta;">GETUTCDATE</span><span style="color: gray;">(),</span><span style="color: black;">300</span><span style="color: gray;">)<br />
</span><span style="color: blue;">SET </span><span style="color: #434343;">@SalesID </span><span style="color: gray;">+</span><span style="color: blue;">=</span><span style="color: black;">1<br />
</span><span style="color: blue;">END<br />
COMMIT TRAN </span><span style="color: black;">InMemory<br />
GO</span> </div>
<p>We&#8217;ll use the undocumented function <span style="color: #800000;"><strong>fn_dblog()</strong></span> to measure the amount of transaction log record generated by the transaction against the memory-optimized table.</p>
<p><span style="color: #ff0000;"><strong>WARNING:</strong></span> It is not recommended to use this undocumented <span style="color: #800000;"><strong>fn_dblog()</strong></span> function on your production system. Use at your own risk.</p>
<div style="background-color:#eeeeee;border:1px solid #D6D6D6;font-family:arial,helvetica,sans-serif;font-size:15px;line-height:20px;margin:8px 0 20px;padding:15px 20px;"><code style="font-size: 14px;"><span style="color: blue;">SELECT </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span><span style="color: black;">[Log Record Length]</span><span style="color: gray;">) </span><span style="color: black;">totalLogRecordLength </span><span style="color: blue;">FROM </span><span style="color: darkred;">fn_dblog</span><span style="color: gray;">(NULL, NULL)<br />
</span><span style="color: blue;">WHERE<br />
</span><span style="color: black;">[transaction id] </span><span style="color: blue;">=<br />
</span><span style="color: gray;">(<br />
</span><span style="color: blue;">SELECT </span><span style="color: black;">[transaction id] </span><span style="color: blue;">FROM </span><span style="color: darkred;">fn_dblog</span><span style="color: gray;">(NULL, NULL)<br />
</span><span style="color: blue;">WHERE </span><span style="color: black;">[transaction name] </span><span style="color: blue;">= </span><span style="color: red;">'InMemory'<br />
</span><span style="color: gray;">)</span></code> </div>
<p><a href="https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize.jpg"><img fetchpriority="high" decoding="async" class="aligncenter size-full wp-image-1749" src="https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize.jpg" alt="in-memory_logSize" width="1104" height="338" srcset="https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize.jpg 1104w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize-300x92.jpg 300w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize-1024x314.jpg 1024w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize-760x233.jpg 760w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize-518x159.jpg 518w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize-82x25.jpg 82w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/in-memory_logSize-600x184.jpg 600w" sizes="(max-width: 1104px) 100vw, 1104px" /></a>Inserting 1,000 rows in the memory-optimized table generated 43,396 bytes of log records. Let&#8217;s do the same thing for a disk-based table.</p>
<div style="background-color:#eeeeee;border:1px solid #D6D6D6;font-family:arial,helvetica,sans-serif;font-size:15px;line-height:20px;margin:8px 0 20px;padding:15px 20px;"><code style="font-size: 14px;"><span style="color: black;"><br />
</span><span style="color: green;">--Create disk-based table<br />
</span><span style="color: blue;">USE </span><span style="color: black;">testDB<br />
GO<br />
</span><span style="color: green;">--table (memory-optimized) to store sales information<br />
</span><span style="color: blue;">CREATE TABLE </span><span style="color: black;">[dbo].[Sales_Old]</span><span style="color: gray;">(<br />
</span><span style="color: black;">[SalesID] [int] </span><span style="color: gray;">NOT NULL </span><span style="color: blue;">PRIMARY KEY</span><span style="color: gray;">,<br />
</span><span style="color: black;">[CustomerID] [int] </span><span style="color: gray;">NOT NULL,<br />
</span><span style="color: black;">[DateCreated] [datetime2] </span><span style="color: gray;">NOT NULL,<br />
</span><span style="color: black;">[TotalPrice] [money]<br />
</span><span style="color: gray;">) </span><span style="color: gray;"><br />
</span><span style="color: black;">GO<br />
</span></code></div>
<p>In the same way, let&#8217;s populate the table and use a named transaction to easily search the records in the LDF file.</p>
<div style="background-color:#eeeeee;border:1px solid #D6D6D6;font-family:arial,helvetica,sans-serif;font-size:15px;line-height:20px;margin:8px 0 20px;padding:15px 20px;"><code style="font-size: 14px;"><span style="color: green;">--populate table with sample data<br />
</span><span style="color: blue;">BEGIN TRAN </span><span style="color: black;">DiskBased<br />
</span><span style="color: blue;">DECLARE </span><span style="color: #434343;">@SalesID </span><span style="color: blue;">INT = </span><span style="color: black;">0</span></code></p>
<p><span style="color: blue;">WHILE </span><span style="color: #434343;">@SalesID </span><span style="color: gray;">&lt; </span><span style="color: black;">1000<br />
</span><span style="color: blue;">BEGIN<br />
INSERT </span><span style="color: black;">[dbo].[Sales_Old] </span><span style="color: blue;">VALUES </span><span style="color: gray;">(</span><span style="color: #434343;">@SalesID</span><span style="color: gray;">,</span><span style="color: black;">1</span><span style="color: gray;">,</span><span style="color: magenta;">GETUTCDATE</span><span style="color: gray;">(),</span><span style="color: black;">300</span><span style="color: gray;">)<br />
</span><span style="color: blue;">SET </span><span style="color: #434343;">@SalesID </span><span style="color: gray;">+</span><span style="color: blue;">=</span><span style="color: black;">1<br />
</span><span style="color: blue;">END<br />
COMMIT TRAN </span><span style="color: black;">DiskBased<br />
GO</span> </div>
<p>We&#8217;ll again use the undocumented function <span style="color: #800000;"><strong>fn_dblog()</strong></span> to measure the amount of transaction log record generated by the transaction against the disk-based table.</p>
<div style="background-color:#eeeeee;border:1px solid #D6D6D6;font-family:arial,helvetica,sans-serif;font-size:15px;line-height:20px;margin:8px 0 20px;padding:15px 20px;"><code style="font-size: 14px;"><span style="color: blue;">SELECT </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span><span style="color: black;">[Log Record Length]</span><span style="color: gray;">) </span><span style="color: black;">totalLogRecordLength </span><span style="color: blue;">FROM </span><span style="color: darkred;">fn_dblog</span><span style="color: gray;">(NULL, NULL)<br />
</span><span style="color: blue;">WHERE<br />
</span><span style="color: black;">[transaction id] </span><span style="color: blue;">=<br />
</span><span style="color: gray;">(<br />
</span><span style="color: blue;">SELECT </span><span style="color: black;">[transaction id] </span><span style="color: blue;">FROM </span><span style="color: darkred;">fn_dblog</span><span style="color: gray;">(NULL, NULL)<br />
</span><span style="color: blue;">WHERE </span><span style="color: black;">[transaction name] </span><span style="color: blue;">= </span><span style="color: red;">'DiskBased'<br />
</span><span style="color: gray;">)</span></code> </div>
<p><a href="https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/diskBased_logSize.jpg"><img decoding="async" class="aligncenter size-full wp-image-1752" src="https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/diskBased_logSize.jpg" alt="diskBased_logSize" width="1087" height="339" srcset="https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/diskBased_logSize.jpg 1087w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/diskBased_logSize-300x94.jpg 300w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/diskBased_logSize-1024x319.jpg 1024w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/diskBased_logSize-760x237.jpg 760w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/diskBased_logSize-518x162.jpg 518w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/diskBased_logSize-82x26.jpg 82w, https://www.edwinmsarmiento.com/wp-content/uploads/2015/06/diskBased_logSize-600x187.jpg 600w" sizes="(max-width: 1087px) 100vw, 1087px" /></a></p>
<p>&nbsp;</p>
<p>Wow! That&#8217;s three (3) times as much log records generated on transactions against disk-based tables versus memory-optimized ones. Which proves that in-memory OLTP transactions provide a much better transaction log throughput compared to the disk-based ones. And that is always a good thing in terms of high availability and disaster recovery.</p>
<h3>Caveats</h3>
<p>The feature is cool, it&#8217;s just not for everyone. Keep in mind that it&#8217;s the first iteration. SQL Server MCM Klaus Aschenbrenner (<a href="https://www.sqlpassion.at/blog/" target="_blank">blog </a>| <a href="https://twitter.com/Aschenbrenner" target="_blank">Twitter</a>) wrote an interesting <a href="https://www.sqlpassion.at/archive/2015/03/11/dont-yet-recommend-memory-oltp-customers/" target="_blank">blog post</a> as to why he wouldn&#8217;t (yet) recommend it to his customers and I totally agree with him on all his points. There are certain use cases for in-memory OLTP, the most famous one being the solution to <a href="http://blogs.msdn.com/b/kenkilty/archive/2014/07/03/asp-net-session-state-using-sql-sever-in-memory.aspx" target="_blank">ASP.NET session state</a>.</p>
<div style="background-color:#eeeeee;border:1px solid #D6D6D6;font-family:arial,helvetica,sans-serif;font-size:15px;line-height:20px;margin:8px 0 20px;padding:15px 20px;"><span style="color: #808080;"><em>While some might argue that web session state should not be stored in a relational databases but rather in non-relational stores like <a href="http://redis.io/" target="_blank">Redis cache</a>, the reality is that there are a lot of ASP.NET web applications that use SQL Server for session state management.</em></span></div>
<p>My biggest pet peeve with this feature is its impact in recovery time objective (RTO,) particularly during database recovery because memory-optimized tables must be loaded into memory before the database is made available. That applies to crash recovery, restoring backups and failover clustered instances. We&#8217;ll cover these items in a future blog post.</p>
<h4><em>Additional Resources</em></h4>
<ul>
<li><a href="http://sqlserver-help.com/2013/12/26/a-z-of-in-memory-oltp-transaction-logging/" target="_blank">A-Z of In-Memory OLTP : How In-Memory Table is made durable – Transaction Logging Concepts</a></li>
<li><a href="https://www.sqlpassion.at/archive/2015/03/11/dont-yet-recommend-memory-oltp-customers/" target="_blank">Why I don’t (yet) recommend In-Memory OLTP to my customers</a></li>
<li><a href="http://download.microsoft.com/download/5/F/8/5F8D223F-E08B-41CC-8CE5-95B79908A872/SQL_Server_2014_In-Memory_OLTP_TDM_White_Paper.pdf" target="_blank">SQL Server 2014 In-Memory OLTP Whitepaper</a></li>
<li><a href="http://blogs.msdn.com/b/arvindsh/archive/2013/07/03/sql-2014-in-memory-oltp-hekaton-training-videos-and-white-papers.aspx" target="_blank">SQL 2014 In-Memory OLTP ‘Hekaton’: training videos and white papers</a></li>
</ul>
]]></content:encoded>
			

		<wfw:commentRss>https://www.edwinmsarmiento.com/in-memory-oltp-in-sql-server-logging/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
				<post-id xmlns="com-wordpress:feed-additions:1">1746</post-id>	</item>
	</channel>
</rss>