SQL Server with Mr. Denny


June 10, 2013  12:00 PM

SQL Server 2014 Standard Edition High Availability

Denny Cherry Denny Cherry Profile: Denny Cherry

With all the announcements about SQL Server 2014 this last week there have been a lot of questions about what’s going to happen for SQL Server 2014 and the non-shared storage High Availability options as we are now one step closer to database mirroring being removed from the product.  You’ll see several blog posts coming out this morning from a variety of people all with their own opinions about what should be included.  These opinions are mine and mine alone.

In SQL Server 2005 Standard Edition and up we had database mirroring which supported having a single mirror on site which was synchronous mirroring only with asynchronous mirroring being an Enterprise Edition feature.  I would like to see this same feature set moved into the SQL Server 2014 Standard Edition product as well.  How I would see this working would be the normal AlwaysOn Availability Group configuration that we have today but only supporting a single replica.  I can see synchronous data movement being the only data movement option which would allow for a local onsite HA without giving you the ability for geographically distributed disaster recovery as that requires asynchronous data movement.

If Microsoft wanted to do something really nice for their Standard Edition customers they could allow for a second replica which would be an Azure database replica and that would allow for Disaster Recovery within Standard Edition while pushing Azure (which we all know is a big deal for Microsoft these days).

So there you have it, that’s what I would like to see in the SQL Server 2014 Standard Edition version of the product.  Do I expect to see it, honestly I’m really not sure.  Microsoft has been very tight lipped about what is coming in the Standard Edition, mostly because these decisions haven’t been made yet.  Once they are someone people will be happy, others wont be, but that’ll be what we have to deal with until we do this all over again in a couple more years when the next version of SQL Server is released.

Denny

June 5, 2013  2:00 PM

What does the RCSI overhead mean?

Denny Cherry Denny Cherry Profile: Denny Cherry

Earlier I posted a blog post which talked about the fact that when you turn on readable secondary replicas there are an additional 14 bytes of overhead which are added to each row.

Overall the thought here is that the impact of this is probably pretty minimal. After all this is the same overhead for RCSI.  However where this becomes a problem is due to the page splitting that I mention in the other article and the fact that these 14 bytes don’t survive after an index rebuild but they do an index reorg.

I can see major page splitting issues happening on clustered indexes which are using the default fill factor of 100% full, which most clustered indexes use as that’s the correct fill factor to be using for a key column which is always growing.  But now as rows need to be changes within the clustered index that’s going to cause our clustered indexes, which never used to have to worry about page splits to suddenly start to split.

The solution here is that when using the readable secondary feature clustered indexes will need to account for this by using a fill factor other than 100% (or 0%) for the fill factor so that the page splits within the clustered index can be avoided.

Additionally we need to think long and hard about using index rebuilds or index reorgs when doing index maintenance.  If we have a table where the records are updated for a while then never updated again index rebuilds probably makes sense instead of index reorgs.  If however we are only doing index reorgs we are now storing an additional 14 bytes of data per row, eventually for most if not all of our rows for ever.  When talking about large tables that’s suddenly some actual space that needs to be accounted for.  For a table with 1 Billion rows that’s an additional 13 Gigs of additional space.  All in all not all that much space.  But if your 1 Billion row table has a row width of just 35 bytes your table should be about 33 Gigs, so an additional 13 Gigs of space is quite a lot.

I guess where I going here is that if you are going to be using AlwaysOn Availability Groups to protect your data and you are going to be using the readable secondary feature then there are going to be some other things within the database that you want to take into account.


May 28, 2013  4:27 PM

Extra Bytes Per Row With AlwaysOn Availability Groups

Denny Cherry Denny Cherry Profile: Denny Cherry

One of things to keep in mind with SQL Server 2012 AlwaysOn Availability Groups is that when the availability group has readable secondary replicas any rows that are changed will have an additional 14 bytes added to each row.  These 14 bytes are used by the readable secondary to handle the read committed snapshot isolation level (RCSI) so that the readable secondary replicas work correctly.

To look at what’s going on lets create a sample new table in a table and take a peak at the data.  To setup this test we create a new database and setup that database for use with AlwaysOn Availability Groups.  The availability group is setup with no readable secondary replicas.

create table MyTest (c1 int identity(1,1),
c2 int,
c3 varchar(100))
GO
insert into MyTest
(c2, c3)
values
(1, ‘test’)
GO 400

This creates a new table in the database with 400 rows in it.  Looking at the output from DBCC IND we can see that this table takes up 2 data pages with a root page (this will become important later on).

Looking at the data for the first data page we can see the following information.

Slot 0 Offset 0x60 Length 23

Record Type = PRIMARY_RECORD        Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS
Record Size = 23
Memory Dump @0x000000003CACA060

0000000000000000:   30000c00 01000000 01000000 03000001 00170074  0………………t
0000000000000014:   657374                                        est

Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4

c1 = 1

Slot 0 Column 2 Offset 0x8 Length 4 Length (physical) 4

c2 = 1

Slot 0 Column 3 Offset 0x13 Length 4 Length (physical) 4

c3 = test

Slot 0 Offset 0x0 Length 0 Length (physical) 0

KeyHashValue = (8194443284a0)

When we update this row changing the value of c2 to equal 2 nothing really changes which we can see from DBCC PAGE again.

Slot 0 Offset 0x60 Length 23

Record Type = PRIMARY_RECORD        Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS
Record Size = 23
Memory Dump @0x000000003EECA060

0000000000000000:   30000c00 01000000 02000000 03000001 00170074  0………………t
0000000000000014:   657374                                        est

Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4

c1 = 1

Slot 0 Column 2 Offset 0x8 Length 4 Length (physical) 4

c2 = 2

Slot 0 Column 3 Offset 0x13 Length 4 Length (physical) 4

c3 = test

Slot 0 Offset 0x0 Length 0 Length (physical) 0

KeyHashValue = (8194443284a0)

Next I’ve changed the settings for the availability group to support readable secondary replicas.  Once that change has been made we change the value of c2 for the same row to equal the value of 3.  Again we can look at this with DBCC PAGE.

Slot 0 Offset 0x1d4e Length 37

Record Type = PRIMARY_RECORD        Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS VERSIONING_INFO
Record Size = 37
Memory Dump @0x000000003AEEBD4E

0000000000000000:   70000c00 01000000 03000000 03000001 00170074  p………………t
0000000000000014:   65737400 00000000 0000000f 06000000 00        est…………..

Version Information =
Transaction Timestamp: 1551
Version Pointer: Null

Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4

c1 = 1

Slot 0 Column 2 Offset 0x8 Length 4 Length (physical) 4

c2 = 3

Slot 0 Column 3 Offset 0x13 Length 4 Length (physical) 4

c3 = test

Slot 0 Offset 0x0 Length 0 Length (physical) 0

KeyHashValue = (8194443284a0)

Looking at these two outputs from DBCC PAGE we can see a couple of differences.  First we see an additional value in the “Record Attributes” field which adds in VERSIONING_INFO to the value.  We also see that the record size has changed from 32 to 37.  Additionally we see that the Version Information has been added.

Looking at the DBCC PAGE output on one of the replicas for the same page as before we see some different information.

Slot 0 Offset 0x1d4e Length 37

Record Type = PRIMARY_RECORD        Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS VERSIONING_INFO
Record Size = 37
Memory Dump @0x000000003893BD4E

0000000000000000:   70000c00 01000000 03000000 03000001 00170074  p………………t
0000000000000014:   65737440 01000001 0000000f 06000000 00        est@………….

Version Information =
Transaction Timestamp: 1551
Version Pointer: (file 1 page 320 currentSlotId 0)

Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4

c1 = 1

Slot 0 Column 2 Offset 0x8 Length 4 Length (physical) 4

c2 = 3

Slot 0 Column 3 Offset 0x13 Length 4 Length (physical) 4

c3 = test

Slot 0 Offset 0x0 Length 0 Length (physical) 0

KeyHashValue = (8194443284a0)

Specifically at this point we see that the Version Pointer now has a value in it. This tells us that the SQL Server has put a copy of the original page into the tempdb database so that we can read it.

A question comes up as to what happens to the page when this row information is added.  Specifically does the page split because of this additional 14 bytes of new data per row.  The answer to this question is “it depends”.  In my testing that I did when I updated a single row the page didn’t split, mostly this would be because there was some free space in the database page.  When I updated rows 2-40 and looked at DBCC IND I saw that in fact the page had split.

Looking at the values within the page there are now 159 rows in the page which was the original database page when there were 322 rows within the database page.  The remainder of the rows were copied into a new database page.

Now that we’ve identified that SQL Server is going to be page splitting older database pages, potentially like crazy what can we do about it?  The answer to that question is to just deal with it and to decrease the fill factor as needed so that page splits happen as little as possible.

To make matters worse when we rebuild the index on the table and look at the output from DBCC PAGE again we can see that the additional flag has been removed from row 1 (seen below).  This tells us that no only will this problem come up the first time that data is modified, it’ll come up every time that index rebuilds are done when the data is changed for the first time after the rebuild.

Slot 0 Offset 0x60 Length 23

Record Type = PRIMARY_RECORD        Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS
Record Size = 23
Memory Dump @0x000000003E0CA060

0000000000000000:   30000c00 01000000 03000000 03000001 00170074  0………………t
0000000000000014:   657374                                        est

Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4

c1 = 1

Slot 0 Column 2 Offset 0x8 Length 4 Length (physical) 4

c2 = 3

Slot 0 Column 3 Offset 0x13 Length 4 Length (physical) 4

c3 = test

Slot 0 Offset 0x0 Length 0 Length (physical) 0

KeyHashValue = (8194443284a0)

Changing the data again, this time changing the first 40 rows (id values 1-40) the new flag comes into place as expected.  If we reorganize the index instead of doing a rebuild this time the flags are left in place.

This tells us that the better option for doing index maintenance on databases which are being protected by AlwaysOn Availability Groups is going to be to use reorganize commands instead of rebuild commands.  This way the 14 byte pointer isn’t removed from the rows so that when they are modified the additional 14 bytes of data doesn’t need to be added.

If you’ve got rows which are changed all the time then this will be a way to handle it.  If the rows never change after the data is reorged then it may or may not be something worth worrying about.

Hopefully this helps answer the questions of what these extra bytes are for and how we can deal with them.

Denny


May 24, 2013  5:04 PM

Recommended reading from mrdenny for May 24, 2013

Denny Cherry Denny Cherry Profile: Denny Cherry

This week I’ve found some great things for you to read. These are a few of my favorites that I’ve found this week.

Hopefully you find these articles as useful as I did.

Don’t forget to follow me on Twitter where my username is @mrdenny

.

Denny


May 22, 2013  2:00 PM

The Optimizer Isn’t As Smart As You Might Want It To Be

Denny Cherry Denny Cherry Profile: Denny Cherry

A little while back I got one of those phone calls.  You know the one, the lovely 6am phone call about random performance problems.  There were two problems that night.  One which I’ll talk about later in another post, the second one which I want to talk about today.

The query that was having problems is a dynamically generated query which comes from a stored procedure.  The basic query which was being run looked a lot like this.

SELECT /*A bunch of columns*/
FROM answer a
JOIN session s ON a.SessionID = s.SessionID
WHERE a.SessionID IN (4857385,5269932,5682479,6095026)

Most of the time that this query was being run everything was just fine, however there were a some times when it was timing out. Looking into the execution plan for a normal run of the query everything looked just fine. However when this was being run sometimes there were 1.2M rows being pulled from the session table even though there were 4 specific IDs being passed in.

Looking at the properties of the index scan which was being performed against the session table I could see that the SQL Server turned the query to WHERE s.SessionID >= 4857385 AND s.SessionID <= 6095026. This was a problem as for some of these queries as like with this query there were 1.2M rows being returned from the session table instead of the 4 rows that should have been returned.

The fix in this case was to simply change there where clause from “WHERE a.SessionID” to “WHERE s.SessionID”. Now I’m not sure why this worked from the internals point of view but I do know that it worked. The next time the stored procedure ran it run in milliseconds instead of timing out at 30 seconds.

In this case the server in question was SQL Server 2008 R2 (10.50.2796). This may or may not apply to other builds of SQL Server. I’m pretty sure this is going to be a your mileage may vary sort of thing.

This is officially the least amount of work that I’ve ever done tuning a query as I only made a single change to a single letter of the query.

Denny


May 17, 2013  5:04 PM

Recommended reading from mrdenny for May 17, 2013

Denny Cherry Denny Cherry Profile: Denny Cherry

This week I’ve found some great things for you to read. These are a few of my favorites that I’ve found this week.

Hopefully you find these articles as useful as I did.

Don’t forget to follow me on Twitter where my username is @mrdenny

.

Denny


May 15, 2013  2:00 PM

Error Handeling and DBCC CHECKDB

Denny Cherry Denny Cherry Profile: Denny Cherry

A client that I’ve been working at for a while needed to start doing DBCC CHECKDBs on their production server.  We hadn’t been for a little while due to maintenance window constraints on their old production server.  Now that we’ve scaled the application across 6 different servers we can now do DBCC CHECKDB on the production server regularly to ensure that there aren’t any database corruption problems on their production server.

As a part of this configuration we wanted to do the DBCC CHECKDB as a part of the backup job so that after the DBCC CHECKDB is complete the databases will then be backed up.

The catch here is that we didn’t want to have to dig through the errorlog file to figure out what the database was that’s having the problem, and we didn’t want the job to fail if there was a problem so I started looking at catching errors when running DBCC CHECKDB.  Sadly there aren’t really any good ways to do this.  TRY/CATCH doesn’t work because DBCC doesn’t actually throw error messages like a normal SQL statement does.  It returns the errors, but it doesn’t actually throw the errors so the CATCH block isn’t actually captured.  Running DBCC CHECKDB within an EXEC sp_executesql doesn’t catch the error either for the same reason, the error isn’t thrown it is simply displayed.  (The reason that the errors from DBCC CHECKDB show up in Management Studio in red I would assume is because SSMS is catching the format and displaying it correctly.)

To catch the errors I had to resort to some old school “error handeling” using the @@ERROR system function.  While this isn’t perfect, I’m not looking for perfect here.  I’m just looking for something that says that there’s an error so that I can send an email when there is an error then continue to loop through the databases looking for others with problems.

As this is step 1 of a multistep job this step is configured to move on to the next step on success or failure (as an email will have been sent and the data logged to the ERRORLOG) then we’ll procede to the backups.

The code that I’m using looks something like this…

/*Populate the table #dbs with the databases that need to be checked.*/
DECLARE cur CURSOR FOR SELECT name from #dbs
OPEN cur
FETCH NEXT FROM cur INTO @name
WHILE @@FETCH_STATUS 0
BEGIN
SET @sql = 'DBCC CHECKDB ([' + @name + '])'
EXEC sp_executesql @sql
IF @@ERROR 0
BEGIN
set @subject = 'CHECKDB failure for ' + @name
set @body = 'DBCC CHECKDB failed for database ' + @name + '
Command run was: ' + @sql
exec msdb.dbo.sp_send_dbmail...
END
FETCH NEXT FROM cur INTO @name
END
CLOSE cur
DEALLOCATE cur

There’s some custom code at the top which figures out which databases to process so that the job step runs DBCC CHECKDB on the same databases which the job will backup (this is figured out based on the database size, if the database is online, and if the database is currently being bulk loaded).  This code isn’t shown as it’s not relivant to this specific problem.

Denny


May 5, 2013  2:38 PM

Two Factor Authentication Shouldn’t Depend on One Factor

Denny Cherry Denny Cherry Profile: Denny Cherry

Bank of America has decided to implement two factor authentication on their website when doing specific things like adding a remote account to transfer money to, or when doing a wire transfer (basically anything where money is going to leave the account). So far this sounds like an excellent plan. The second factor is that when I want to send money to another account or send a wire transfer they’ll send me a text message and I then enter the one time use code they text me into the website.

All this sounds perfect (except for if I’m out of the country and I can’t get their text messages), except for one little issue.

Adding a new cell phone to send a text message to is as simple as just logging onto the bank’s webpage. Once I log into the site I can simply add another cell phone, verify that I have the cell phone via a text message and then I can use that cell phone to approve any wire transfers. All very convenient. The problem is that is someone else figures out my username and password for the website they to can add a cell phone to my bank account, approve it for use, then start sending wire transfers off all my money to their account.

So while Bank of America has two factor authentication, the second factor is dependent on knowing the first factor. For this to be actually useful two factor authentication it would need to require that I go into a branch with my ID to prove that I’m me and that I can add the phone as a two factor authentication phone. Additionally they should be using as an option one of the phone application based two factor authentication processes so that if I have several phones I can just use the one application, or if I’m not in the country I can still manage my money (which has been a problem a couple of times).

While I applaud the effort that Bank of America has put into having two factor authentication, doing it correctly would be a lot more useful.  As currently you have one factor authentication with an annoyance.

Denny


May 3, 2013  10:25 AM

Atlanta SQL Saturday PreCon

Denny Cherry Denny Cherry Profile: Denny Cherry

At the upcoming SQL Saturday in Atlanta, GA I have the honor of delivering a precon on database security which has several seats still available.

This precon is a full day session where we will be talking about all the security best practices.

Signing up for the precon is pretty simple, just do to the URL for the precon and fill out the form.

I hope to see you there,
Denny


May 1, 2013  2:00 PM

I’m using simple recovery, so the log file isn’t important right?

Denny Cherry Denny Cherry Profile: Denny Cherry

One of the big myths of SQL Server resolves around the transaction log, and how it’s used with the simple recovery model.

Many people think that the simple recovery model doesn’t use the transaction log.  More specifically they think that it’s there because it has to be there, but that SQL Server doesn’t actually use it.  The reality is that SQL Server still uses the transaction log, much like it does in full or bulk logged recovery modes.  There are some transactions which are going to be minimally logged, but for the most part the INSERT, UPDATE and DELETE commands are going to be fully logged just like normal.

What SQL Server does with the transaction log in simple recovery model is that when the transactions are committed they are written to the transaction log and the pages are dirtied in the buffer pool.  When checkpoint runs the dirty pages in the buffer pool are written to the disk.  Everything up to this point is basically the same as with the other recovery models.  Once the checkpoint has been completed things get different between simple recovery and the other two recovery models.  With the simple recovery model the virtual log files which were just checkpointed and had their dirty pages written to disk will be marked as no longer in use (status=0).  With bulk logged and full recovery this doesn’t happen until the transaction log backup has been completed.

Hopefully this helps dispel the myth.

Denny


Forgot Password

No problem! Submit your e-mail address below. We'll send you an e-mail containing your password.

Your password has been sent to: