{"id":8807,"date":"2016-03-14T00:51:43","date_gmt":"2016-03-14T00:51:43","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/how-to-identify-the-source-of-page-splits-in-a-database\/"},"modified":"2017-10-28T02:14:16","modified_gmt":"2017-10-28T02:14:16","slug":"how-to-identify-the-source-of-page-splits-in-a-database","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/blogs\/how-to-identify-the-source-of-page-splits-in-a-database\/","title":{"rendered":"How to identify the source of page splits in a database"},"content":{"rendered":"<p>Page splits have the ability to slow down our queries and increase the amount of IOs needed to retrieve our data. We need to monitor and identify the source of page splits to avoid them in our databases.<\/p>\n<p>We can monitor page splits with extended events, there is an event called <b>&#8216;page_split&#8217;<\/b>. The problem is that there are many kinds of page splits and we need to monitor only the bad ones. The <b>&#8216;page_split&#8217;<\/b> event doesn&#8217;t identify if the page split is a bad one.<\/p>\n<p>When one page becomes full and a new page is needed this is reported as a page split, but this is a regular operation with no bad consequences for our queries. The problem happens with updates and non-sequential inserts, when the row needs to be inserted in the middle of the pages of the object and there is no space for this. SQL Server creates a new page, transfers half of the page data to the new page and writes the row data. This creates page fragmentation and is very bad for performance and is also reported as page split.<\/p>\n<p>We can find the bad page splits using the event <b>sql_server.transaction_log<\/b>. This event monitors all the activities in the transaction log, because that we need to use with caution. We can filter the <b>&#8216;operation&#8217;<\/b> field looking for the value 11, which means <b>LOP_DELETE_SPLIT<\/b>. This is the deletion of rows that happens when SQL Server is moving rows from one page to another in a page split, a bad page split.<\/p>\n<p>We can&#8217;t use the XE interface to create a session with this event, it&#8217;s not visible in the interface. We need to create the session by t-sql. The code to create the session will be this:<\/p>\n<pre class=\"csharpcode \">CREATE EVENT SESSION [BadSplits]\r\nON    SERVER\r\nADD EVENT sqlserver.transaction_log(\r\n    WHERE operation = 11  -- LOP_DELETE_SPLIT\r\n)\r\nADD TARGET package0.event_file\r\n-- You need to customize the path\r\n(SET filename=N'C:\\xel\\badsplits.xel')\r\nGO\r\n\r\n-- Start the session\r\nALTER EVENT SESSION [BadSplits]\r\nON SERVER\r\nSTATE = start;\r\nGO<\/pre>\n<p>&nbsp;<\/p>\n<p>After some time, we can query this session to find the source of page split problem. The first challenge is to find the database where the page splits are happening.<\/p>\n<p>To read the information in the files we need to use the DMF <b>sys.fn_xe_file_target_read_file<\/b>, but the information will be returned in XML format, so we need to use XML methods to retrieve the information from inside the XML document.<\/p>\n<p>The query will be this:<\/p>\n<pre class=\"csharpcode\">with qry as\r\n        (select\r\n               -- Retrieve the database_id from inside the XML document\r\ntheNodes.event_data.value('(data[@name=\"database_id\"]\/value)[1]','int') as database_id\r\n               from\r\n        (select convert(xml,event_data) event_data -- convert the text field to XML\r\n               from\r\n-- reads the information in the event files\r\nsys.fn_xe_file_target_read_file('c:\\xel\\badsplits*.xel', NULL, NULL, NULL)) theData\r\n                cross apply theData.event_data.nodes('\/\/event') theNodes(event_data) )\r\nselect db_name(database_id),count(*) as total from qry\r\ngroup by db_name(database_id) -- group the result by database\r\norder by total desc<\/pre>\n<p><a href=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2016\/03\/pagesplit1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-7519\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2016\/03\/pagesplit1.png\" alt=\"pagesplit1.png\" width=\"184\" height=\"117\" \/><\/a><\/p>\n<p>Now that we know the database with most page splits, we need to identify the objects that are causing the problem. The XE event only returns the <b>allocation_unit_id<\/b>, so we need to convert the <b>allocation_unit_id<\/b> to the object name that owns these pages.<\/p>\n<p>The query to convert the allocation_unit_id to the object name needs to run on the database that contains the allocation units and we will filter the result from XE for this specific database. We need to join the result with the DMV sys.allocation_units using the allocation_unit_id field. This DMV has three kinds of allocation units but we are interested only in two of them, type 1 and type 3, that means data row pages and row overflow pages.<\/p>\n<p>We need to add sys.partitions to the query, join the container_id field of sys.allocation_units with hobt_id field in sys.partitions. Finally we add sys.objects to the query, joining with object_id field from sys.partitions and get the name from sys.objects.<\/p>\n<pre class=\"csharpcode\">with qry as\r\n         (select\r\ntheNodes.event_data.value('(data[@name=\"database_id\"]\/value)[1]','int') as database_id,\r\ntheNodes.event_data.value('(data[@name=\"alloc_unit_id\"]\/value)[1]','varchar(30)') as alloc_unit_id,\r\ntheNodes.event_data.value('(data[@name=\"context\"]\/text)[1]','varchar(30)') as context\r\n                 from\r\n                          (select convert(xml,event_data) event_data\r\n                          from\r\n                 sys.fn_xe_file_target_read_file('c:\\xel\\badsplits*.xel', NULL, NULL, NULL)) theData\r\n                  cross apply theData.event_data.nodes('\/\/event') theNodes(event_data) )\r\nselect name,context,count(*) as total -- The count of splits by objects\r\n from qry,sys.allocation_units au, sys.partitions p, sys.objects ob\r\nwhere qry.alloc_unit_id=au.allocation_unit_id\r\n                 and au.container_id=p.hobt_id and p.object_id=ob.object_id\r\n                 and (au.type=1 or au.type=3) and\r\n                   db_name(database_id)='MDW' -- Filter by the database\r\ngroup by name,context -- group by object name and context\r\norder by name<\/pre>\n<p><a href=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2016\/03\/pagesplit2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-7520\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2016\/03\/pagesplit2.png\" alt=\"pagesplit2.png\" width=\"374\" height=\"349\" \/><\/a><\/p>\n<p>Now that we know the table that&#8217;s causing most of page splits, we can analyze the indexes of this table to solve the problem.<\/p>\n<p>Although this solution can identify page split problems, we can&#8217;t let this session stay started for a long time because we are capturing transaction log activities, this kind of capture can be too intensive for the server. We can, however, use <a href=\"http:\/\/www.red-gate.com\/products\/dba\/sql-monitor\/\">Redgate SQL Monitor<\/a> to point to us when there is a page split problem in the server and only then start this session for a short period to identify the source of the problem.<\/p>\n<p>SQL Monitor can create analysis graphs based on several performance counters. In this case we should use &#8216;page splits\/sec&#8217; and &#8216;page splits\/batch requests&#8217;.<\/p>\n<p>We should create a baseline to identify the regular values of these counters in our environment, so when we notice these values increasing we can use the XE session to identify the source of the page split problem.<\/p>\n<p><a href=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2016\/03\/pagesplit3.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-7521\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2016\/03\/pagesplit3.png\" alt=\"pagesplit3.png\" width=\"508\" height=\"153\" \/><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Page splits have the ability to slow down our queries and increase the amount of IOs needed to retrieve our data. We need to monitor and identify the source of page splits to avoid them in our databases. We can monitor page splits with extended events, there is an event called &#8216;page_split&#8217;. The problem is&#8230;&hellip;<\/p>\n","protected":false},"author":50808,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[],"coauthors":[],"class_list":["post-8807","post","type-post","status-publish","format-standard","hentry","category-blogs"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/8807","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/50808"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=8807"}],"version-history":[{"count":10,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/8807\/revisions"}],"predecessor-version":[{"id":75118,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/8807\/revisions\/75118"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=8807"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=8807"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=8807"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=8807"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}