{"id":1762,"date":"2014-01-09T12:18:23","date_gmt":"2014-01-09T20:18:23","guid":{"rendered":"http:\/\/3.209.169.194\/blogs\/bobb\/?p=1762"},"modified":"2014-01-09T12:18:23","modified_gmt":"2014-01-09T20:18:23","slug":"in-memory-oltp-read_set-write_set-and-scan_set","status":"publish","type":"post","link":"https:\/\/www.sqlskills.com\/blogs\/bobb\/in-memory-oltp-read_set-write_set-and-scan_set\/","title":{"rendered":"In-memory OLTP &#8211; read_set, write_set, and scan_set"},"content":{"rendered":"<p>I was looking through some\u00a0in-memory OLTP DMVs\u00a0in SQL Server 2014 and came across these in the DMV sys.dm_xtp_system_memory_consumers:<br \/>\nWRITE_SET_LOOKASIDE<br \/>\nSCAN_SET_LOOKASIDE<br \/>\nREAD_SET_LOOKASIDE<\/p>\n<p>Thought it might be nice to see how these work, but explain first\u00a0I&#8217;ll explain\u00a0what they&#8217;re about. By the way, a lookaside is just a cache, sometimes\u00a0of pointers to memory addresses. You&#8217;ve probably heard of the L1 and L2 cache built into CPUs.<\/p>\n<p>These sets and their corresponding lookasides\u00a0have to do with how memory-optimized tables do transactions against multi-version concurrency control (MVCC) tables (memory-optimized tables in SQL Server 2014 use MVCC). The three basic transaction types supported by\u00a0MVCC tables (lowest to highest isolation) are snapshot, repeatable read, and serializable. In all isolation levels, reads are performed as of the beginning of the transaction. So any of these levels will read rows where the begin-time of the transaction is between the begin-timestamp and end-timestamp of the row.<\/p>\n<p>Snapshot transactions require no additional processing, simply read the correct versions. Repeatable read means that the version you&#8217;d read at the end of the transaction must be the same as you&#8217;d read at the end. Serializable means repeatable read and in addition, if you&#8217;re doing a scan (e.g. select rows where id is between 10 and 20) no new rows in your range were added between beginning and end of your transaction. READ_SET and SCAN_SET have to do with repeatable read and serializable transactions, respectively.<\/p>\n<p>SQL Server\u00a0MVCC accomplishes the additional checks by **re-reading the rows at commit time**. Sounds expensive. It needs to make this re-reading fast, so instead of doing the\u00a0whole operation again, repeatable read transactions keep a pointer to each row they&#8217;ve read in a READ_SET and check the pointer again at commit time to see if they&#8217;re the same. You can see this behavior on a system with no other activity, in sys.dm_xtp_system_memory_consumers.<\/p>\n<p>&#8212; execute sys.dm_xtp_system_memory_consumers and check the READ_SET_LOOKASIDE amount<br \/>\nselect * from sys.dm_xtp_system_memory_consumers;<br \/>\n&#8212; now repeatable read transaction using AdventureWorks memory-optimized table sample<br \/>\nbegin transaction<br \/>\nselect * from Sales.SalesOrderHeader_inmem with (repeatableread);<br \/>\ncommit<br \/>\n&#8212; note READ_SET_LOOKASIDE should be bigger<br \/>\nselect * from sys.dm_xtp_system_memory_consumers;<\/p>\n<p>For a serializable transaction you need to ensure read consistency with the READ_SET and also ensure scans will produce the same rows and\u00a0no additional rows\u00a0with SCAN_SET. Choose a different table if you want to see them both increase, because, if there&#8217;s no other changes in Sales.SalesOrderHeader_inmem (from the first experiment)\u00a0the READ_SET_LOOKASIDE will retain the same rows\/same size:<\/p>\n<p>&#8212; execute sys.dm_xtp_system_memory_consumers and check the READ_SET_LOOKASIDE and SCAN_SET_LOOKASIDE amount<br \/>\nselect * from sys.dm_xtp_system_memory_consumers;<br \/>\n&#8212; now repeatable read transaction using AdventureWorks memory-optimzed table sample<br \/>\nbegin transaction<br \/>\nselect * from Production.Product_inmem with (serializable);<br \/>\ncommit<br \/>\n&#8212; note READ_SET_LOOKASIDE and SCAN_SET_LOOKASIDE should be bigger<br \/>\nselect * from sys.dm_xtp_system_memory_consumers;<\/p>\n<p>So what about the WRITE_SET then? Transactions that update MVCC rows replace the end-timestamp of the rows they update with the transaction ID while the transaction is running. A bit indicates whether the information is an end-timestamp or a transaction ID. The transaction ID also prohibits concurrent updates of the same row (it&#8217;s not a lock, transactions that attempt concurrent update will simply fail and roll back the transaction). They&#8217;ll also insert new rows with a transaction ID as a begin-timestamp.<\/p>\n<p>Transaction commit happens like this:<br \/>\nAcquire an end-timestamp<br \/>\nValidation (see READ_SET and SCAN_SET above)<br \/>\nHarden to the transaction log<br \/>\nUpdate the timestamps in the updated\/new rows<\/p>\n<p>The WRITE_SET is used to make updating the timestamps fast. Because this entry is getting a bit long, I&#8217;ll leave it to the reader to observe WRITE_SET activity using the DMV. And I&#8217;ll also leave it to you to\u00a0figure out how the transaction commit series of events guarantees transactionally consistent data. Hint: edge-cases and additional information\u00a0is\u00a0available in my previous blog entries of the subject.<\/p>\n<p>Cheers, @bobbeauch<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I was looking through some\u00a0in-memory OLTP DMVs\u00a0in SQL Server 2014 and came across these in the DMV sys.dm_xtp_system_memory_consumers: WRITE_SET_LOOKASIDE SCAN_SET_LOOKASIDE READ_SET_LOOKASIDE Thought it might be nice to see how these work, but explain first\u00a0I&#8217;ll explain\u00a0what they&#8217;re about. By the way, a lookaside is just a cache, sometimes\u00a0of pointers to memory addresses. You&#8217;ve probably heard of [&hellip;]<\/p>\n","protected":false},"author":7,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[42,41],"tags":[],"class_list":["post-1762","post","type-post","status-publish","format-standard","hentry","category-hekaton","category-sql-server-2014"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v21.9.1 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>In-memory OLTP - read_set, write_set, and scan_set - Bob Beauchemin<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.sqlskills.com\/blogs\/bobb\/in-memory-oltp-read_set-write_set-and-scan_set\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"In-memory OLTP - read_set, write_set, and scan_set - Bob Beauchemin\" \/>\n<meta property=\"og:description\" content=\"I was looking through some\u00a0in-memory OLTP DMVs\u00a0in SQL Server 2014 and came across these in the DMV sys.dm_xtp_system_memory_consumers: WRITE_SET_LOOKASIDE SCAN_SET_LOOKASIDE READ_SET_LOOKASIDE Thought it might be nice to see how these work, but explain first\u00a0I&#8217;ll explain\u00a0what they&#8217;re about. By the way, a lookaside is just a cache, sometimes\u00a0of pointers to memory addresses. You&#8217;ve probably heard of [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.sqlskills.com\/blogs\/bobb\/in-memory-oltp-read_set-write_set-and-scan_set\/\" \/>\n<meta property=\"og:site_name\" content=\"Bob Beauchemin\" \/>\n<meta property=\"article:published_time\" content=\"2014-01-09T20:18:23+00:00\" \/>\n<meta name=\"author\" content=\"Bob Beauchemin\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Bob Beauchemin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/bobb\/in-memory-oltp-read_set-write_set-and-scan_set\/\",\"url\":\"https:\/\/www.sqlskills.com\/blogs\/bobb\/in-memory-oltp-read_set-write_set-and-scan_set\/\",\"name\":\"In-memory OLTP - read_set, write_set, and scan_set - Bob Beauchemin\",\"isPartOf\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/bobb\/#website\"},\"datePublished\":\"2014-01-09T20:18:23+00:00\",\"dateModified\":\"2014-01-09T20:18:23+00:00\",\"author\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/bobb\/#\/schema\/person\/62bfa986c5b5d28fcffd8b4fc409c73e\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/bobb\/in-memory-oltp-read_set-write_set-and-scan_set\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.sqlskills.com\/blogs\/bobb\/in-memory-oltp-read_set-write_set-and-scan_set\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/bobb\/in-memory-oltp-read_set-write_set-and-scan_set\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.sqlskills.com\/blogs\/bobb\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Hekaton\",\"item\":\"https:\/\/www.sqlskills.com\/blogs\/bobb\/category\/hekaton\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"In-memory OLTP &#8211; read_set, write_set, and scan_set\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/bobb\/#website\",\"url\":\"https:\/\/www.sqlskills.com\/blogs\/bobb\/\",\"name\":\"Bob Beauchemin\",\"description\":\"SQL Server Blog\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.sqlskills.com\/blogs\/bobb\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/bobb\/#\/schema\/person\/62bfa986c5b5d28fcffd8b4fc409c73e\",\"name\":\"Bob Beauchemin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/bobb\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/6f80e6cc667410857fa6a21931dc528b8092f4d112bf7a8ff7c267674d44ee37?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/6f80e6cc667410857fa6a21931dc528b8092f4d112bf7a8ff7c267674d44ee37?s=96&d=mm&r=g\",\"caption\":\"Bob Beauchemin\"},\"sameAs\":[\"http:\/www.sqlskills.com\/blogs\/bobb\/\"],\"url\":\"https:\/\/www.sqlskills.com\/blogs\/bobb\/author\/bobb\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"In-memory OLTP - read_set, write_set, and scan_set - Bob Beauchemin","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.sqlskills.com\/blogs\/bobb\/in-memory-oltp-read_set-write_set-and-scan_set\/","og_locale":"en_US","og_type":"article","og_title":"In-memory OLTP - read_set, write_set, and scan_set - Bob Beauchemin","og_description":"I was looking through some\u00a0in-memory OLTP DMVs\u00a0in SQL Server 2014 and came across these in the DMV sys.dm_xtp_system_memory_consumers: WRITE_SET_LOOKASIDE SCAN_SET_LOOKASIDE READ_SET_LOOKASIDE Thought it might be nice to see how these work, but explain first\u00a0I&#8217;ll explain\u00a0what they&#8217;re about. By the way, a lookaside is just a cache, sometimes\u00a0of pointers to memory addresses. You&#8217;ve probably heard of [&hellip;]","og_url":"https:\/\/www.sqlskills.com\/blogs\/bobb\/in-memory-oltp-read_set-write_set-and-scan_set\/","og_site_name":"Bob Beauchemin","article_published_time":"2014-01-09T20:18:23+00:00","author":"Bob Beauchemin","twitter_misc":{"Written by":"Bob Beauchemin","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.sqlskills.com\/blogs\/bobb\/in-memory-oltp-read_set-write_set-and-scan_set\/","url":"https:\/\/www.sqlskills.com\/blogs\/bobb\/in-memory-oltp-read_set-write_set-and-scan_set\/","name":"In-memory OLTP - read_set, write_set, and scan_set - Bob Beauchemin","isPartOf":{"@id":"https:\/\/www.sqlskills.com\/blogs\/bobb\/#website"},"datePublished":"2014-01-09T20:18:23+00:00","dateModified":"2014-01-09T20:18:23+00:00","author":{"@id":"https:\/\/www.sqlskills.com\/blogs\/bobb\/#\/schema\/person\/62bfa986c5b5d28fcffd8b4fc409c73e"},"breadcrumb":{"@id":"https:\/\/www.sqlskills.com\/blogs\/bobb\/in-memory-oltp-read_set-write_set-and-scan_set\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.sqlskills.com\/blogs\/bobb\/in-memory-oltp-read_set-write_set-and-scan_set\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.sqlskills.com\/blogs\/bobb\/in-memory-oltp-read_set-write_set-and-scan_set\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.sqlskills.com\/blogs\/bobb\/"},{"@type":"ListItem","position":2,"name":"Hekaton","item":"https:\/\/www.sqlskills.com\/blogs\/bobb\/category\/hekaton\/"},{"@type":"ListItem","position":3,"name":"In-memory OLTP &#8211; read_set, write_set, and scan_set"}]},{"@type":"WebSite","@id":"https:\/\/www.sqlskills.com\/blogs\/bobb\/#website","url":"https:\/\/www.sqlskills.com\/blogs\/bobb\/","name":"Bob Beauchemin","description":"SQL Server Blog","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.sqlskills.com\/blogs\/bobb\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.sqlskills.com\/blogs\/bobb\/#\/schema\/person\/62bfa986c5b5d28fcffd8b4fc409c73e","name":"Bob Beauchemin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.sqlskills.com\/blogs\/bobb\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/6f80e6cc667410857fa6a21931dc528b8092f4d112bf7a8ff7c267674d44ee37?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/6f80e6cc667410857fa6a21931dc528b8092f4d112bf7a8ff7c267674d44ee37?s=96&d=mm&r=g","caption":"Bob Beauchemin"},"sameAs":["http:\/www.sqlskills.com\/blogs\/bobb\/"],"url":"https:\/\/www.sqlskills.com\/blogs\/bobb\/author\/bobb\/"}]}},"_links":{"self":[{"href":"https:\/\/www.sqlskills.com\/blogs\/bobb\/wp-json\/wp\/v2\/posts\/1762","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sqlskills.com\/blogs\/bobb\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqlskills.com\/blogs\/bobb\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/bobb\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/bobb\/wp-json\/wp\/v2\/comments?post=1762"}],"version-history":[{"count":0,"href":"https:\/\/www.sqlskills.com\/blogs\/bobb\/wp-json\/wp\/v2\/posts\/1762\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.sqlskills.com\/blogs\/bobb\/wp-json\/wp\/v2\/media?parent=1762"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/bobb\/wp-json\/wp\/v2\/categories?post=1762"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/bobb\/wp-json\/wp\/v2\/tags?post=1762"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}