Friday, April 5, 2013

Use delegated session configuration in Powershell 3.0

Powershell 3.0 has many new feature, and they are useful in some circumstance.

let's say User A log in server A, and he wants to access the resource on Server B, however User A doesn't have permission to access the resource, User B has resource access permission, so User A needs to use User B's credential.














Normally if we want to use remote session to access resource , we can use cmdlets
1. Enter-PSSesssion
2. Invoke-Command.

However we need to provide User B's credential and password when create remote session. There is potential risk, sometimes we don't want to give the password to operators, in that case, we can use delegated session configuration.

Here is the step
1. Register a new session configuration with "-RunAsCredential UserB" on Server B

Register-PSSessionConfiguration -Name DomainAdmin -RunAsCredential xxx\administrator -ShowSecurityDescriptorUI

Here we create a session configuration named "DomainAdmin", and the session will be run as user "xxx\administrator" credential

2. Grant full access to User A
In the next popup window, grant full access to User A
























3. Verify the session configuration
you can run "Get-PSSessionConfiguration" to view the all session configuration on Server B.
or you can run "Set-PSSessionConfiguration DomainAdmin -ShowSecurityDescriptorUI" to configure the permission.








4. Now on server A, create remote session with "-ConfigurationName" parameter
Enter-PSSession -ComputerName ServerB -Credential xxx\UserA -ConfigurationName DomainAdmin

here we use UserA's credential to create remote session, and use session configuration DomainAdmin. so we just need provide password for UserA when creating remote session, then we can run as User B's credential on Server B





so when we run whoami, it shows the current user is administrator(User B).

delegated session configuration is easy to use, and very useful especially on system maintenance work.


Wednesday, April 3, 2013

Compression Backup with BUFFERCOUNT parameter

If you want to make you backup faster, you can try BUFFERCOUNT parameter with compression backup.

From Book Online description:

BUFFERCOUNT = { buffercount | @buffercount_variable }
Specifies the total number of I/O buffers to be used for the backup operation. You can specify any positive integer; however, large numbers of buffers might cause "out of memory" errors because of inadequate virtual address space in the Sqlservr.exe process.
The total space used by the buffers is determined by: buffercount * maxtransfersize.

Below is my testing result, all backups use compression parameter. the data file is 195GB with 11GB used. by using compression, the backup file is 8.4GB

1. No BUFFERCOUNT parameter
BACKUP DATABASE abc TO DISK = 'c:\temp\abc.bak' WITH COMPRESSION

it completed with 88MB/sec throughput, avg cpu% is 45

2. Add BUFFERCOUNT parameter
BACKUP DATABASE abc TO DISK = 'c:\temp\abc.bak' WITH COMPRESSION, BUFFERCOUNT = 50
it completed with 105MB/sec throughput, avg cpu% is 50

Next I tried BUFFERCOUNT = 100, BUFFERCOUNT = 150 and BUFFERCOUNT = 200

here is the result:

based on the diagram upper, when BUFFERCOUNT = 100, Throughput reach the ceiling, comparing with the first command without BUFFERCOUNT parameter(default), we made the backup process 30% faster.

you can also use multiple backup files to reach the same goal. 

3. Test multiple backup files
my sql server has 2 CPU, so I use 2 backup files:
BACKUP DATABASE abc TO 
DISK = 'c:\temp\abc1.bak',
DISK = 'c:\temp\abc2.bak'
WITH COMPRESSION

it completed with 115MB/sec throughput. avg cpu 60%.

4. Test multiple backup files with BUFFERCOUNT parameter
BACKUP DATABASE abc TO 
DISK = 'c:\temp\abc1.bak',
DISK = 'c:\temp\abc2.bak'
WITH COMPRESSION, BUFFERCOUNT = 50
















based on the diagram upper, there is only 5% performance difference between using BUFFERCOUNT and no BUFFERCOUNT.

Although BUFFERCOUNT makes higher CPU %, the backup always runs during off business, so if you can afford the higher CPU% penalty, it is worth to try BUFFERCOUNT. Please test the appropriate BUFFERCOUNT value based on your environment. 


Reference:
http://sqlcat.com/sqlcat/b/technicalnotes/archive/2008/04/21/tuning-the-performance-of-backup-compression-in-sql-server-2008.aspx



Thursday, February 21, 2013

Get Perfmon data with Powershell

Sometimes you just want to have a look at the system performance, or you feel tired with log on the server, open the perfmon and configure the performance counter, perhaps you want to run a simple command to get the performance data from remote server, here we have a convenient way check the system performance counter on remote server:

Powershell Command : Get-Counter
http://technet.microsoft.com/en-us/library/hh849685.aspx

However, read the data from Get-Counter is not a easy way, so I wrapped it up with several functions and put them into a powershell module : osperfmon.psm1
you can download it from
https://docs.google.com/file/d/0B4Xde9z-OME1ZHhEZG8tS3h4Sms/edit?usp=sharing

Here is the steps to run it
       1.Setup the module.
    • Download the osperfmon.psm1, Copy it to local drive
    • Open powershell window, and import the module
                    Import-Module .\osPerfmon.psm1
              
    • List the available function
                   Get-Command -Module osperfmon
                 

      2. Get Function detail.
       You can get the function help with "Get-Help -detailed", for instance
       Get-Help Get-CPUPerf -Detailed







Here is the sample for each function:
1. Get-CPUPerf











   2. Get-DiskPerf



 3. Get-MemoryPerf









4. Get-NetPerf








5. Get-ProcessPerf

Each function has several parameter, like -SortBy, -Top. you can customize it with your demand.

your suggestion and advice are welcomed. thanks  











Monday, January 28, 2013

"Auto update statistics" option on tempdb

"Auto update statistics" option is enabled by default on tempdb, if it is disabled, you may get trouble in some case.Today when I tested script, I found the index created on the temp table didn't work because of "Auto update statistics" disabled.

here is the script.

USE [master]
GO
ALTER DATABASE [tempdb] SET AUTO_UPDATE_STATISTICS OFF WITH NO_WAIT
GO
USE [tempdb]
GO
dbcc freeproccache
GO
Create table #mytemp1(a int, b int)
GO
create  index temp_inx1 on #mytemp1(a)
GO
declare @int int
set @int=1
while @int <10000
begin
insert into #mytemp1 values(@int, @int+1)
--insert into #mytemp2 values(@int, @int+1)
set @int+=1
end

GO
set statistics profile on
GO
select b from #mytemp1 where a=50
GO
set statistics profile off
GO


and you can see the script use table scan instead of index seek, because we created an index on a, table scan is not we wanted.

let's enable the "Auto update statistics", and do the test again
USE [master]
GO
ALTER DATABASE [tempdb] SET AUTO_UPDATE_STATISTICS on WITH NO_WAIT
GO
USE [tempdb]
GO
dbcc freeproccache
GO
Create table #mytemp1(a int, b int)
GO
create  index temp_inx1 on #mytemp1(a)
GO
declare @int int
set @int=1
while @int <10000
begin
insert into #mytemp1 values(@int, @int+1)
--insert into #mytemp2 values(@int, @int+1)
set @int+=1
end

GO
set statistics profile on
GO
select b from #mytemp1 where a=50
GO
set statistics profile off
GO
drop table #mytemp1

this time we got index seek, this is because:
when "Auto update statistics" is enabled, sql server will check if statistics is stall before generation query plan. since we inserted 1000 rows after index created, the statistics is stall. then sql server will update the statistics, so sql server can get correct statistics and select index seek instead of table scan as query plan.

if you are working with big temp table object, be careful for the "Auto update statistics" on tempdb. 

Saturday, January 19, 2013

Got "Cannot generate SSPI context" error message after changing sql service account

Today I changed a sql server service startup account during testing, then when I tried to connect sql server with powershell, I got error message below:

The target principal name is incorrect.  Cannot generate SSPI context."


Here is the troubleshooting step I used
1. Rollback service startup account to old one, then the powershell script work. so it should be the issue with the new account.

2. Start sql server service again with new account. check sql server error log, then found error message in it


2013-01-20 00:53:36.49 Server      The SQL Server Network Interface library could not register the Service Principal Name (SPN) for the SQL Server service. Error: 0x2098, state: 15. Failure to register an SPN may cause integrated authentication to fall back to NTLM instead of Kerberos. This is an informational message. Further action is only required if Kerberos authentication is required by authentication policies.


the error indicted sql server service can not register SPN during startup.

3. search the error code 0x2098, found link below
http://msdn.microsoft.com/en-us/library/ms681390(VS.85).aspx

ERROR_DS_INSUFF_ACCESS_RIGHTS
8344 (0x2098)
Insufficient access rights to perform the operation.
4. grant "Read servicePrincipalName" and "Write servicePrincipalName" permission to new account.
http://support.microsoft.com/kb/811889

for window 2012 AD, it is a little bit different

  1. Click Start, click Run, type Adsiedit.msc, and then click OK.
  2. In the ADSI Edit snap-in, expand Domain [DomainName], expand DC= RootDomainName, expand CN=Users, right-click CN= AccountName, and then click Properties. 
  3. In the CN= AccountName Properties dialog box, click the Security tab.
  4. On the Security tab, click Advanced.
  5. In the Advanced Security Settings dialog box, select one of "SELF"'s row just like the pic below

     6. Click Edit, Open Permission Entry dialog box.
     7. Make sure Pricipal is "SELF", Type is "Allow" and "Applied to" is "This Object Only", in Properties section, select the properties below:
Read servicePrincipalName
Write servicePrincipalName

     8. Click OK three times, and then exit the ADSI Edit snap-in.

after restart the sql server service, make sure the spn resgister correctly:
2013-01-20 03:08:27.20 Server      The SQL Server Network Interface library successfully registered the Service Principal Name (SPN) [ MSSQLSvc/machinename.domainname.com ] for the SQL Server service. 


Friday, January 4, 2013

Row of nonclustered index page

Below is the formula of calculating the row size of non-clustered index page




















so unique non-clustered index created on unique clustered index use smallest space, while non-unique non-clustered index created on non-unique clustered index use largest space.



Thursday, January 3, 2013

Heap might take more space than clustered Index when inserting row

Sometimes when you insert row on heap, even if the page has enough free space, the new row can not be inserted into that page, and a new page will be created for the new row. Here is interesting example:

1. Create testing DB

use master
go
CREATE DATABASE [test] ON  PRIMARY
( NAME = N'test', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\test.mdf' , SIZE = 2048KB , FILEGROWTH = 1024KB )
 LOG ON
( NAME = N'test_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\test_log.ldf' , SIZE = 1024KB , FILEGROWTH = 1024KB)
GO

2. Create table

CREATE TABLE myHeap
(
a char(500) NOT NULL,
b char(100) NOT NULL,
c char(153) NOT NULL,
)  ON [PRIMARY]
GO

so the row length will be 753+7=760 bytes, in theory, the every page can hold 10 rows(8060/760=10)

3, insert 10 rows
Declare @int int
set @int=1
while (@int <= 10)
begin
insert into myHeap(a,b,c)
values(right('00000'+CONVERT(varchar(5),@int),5),'bbbbb','ccccc')
set @int=@int+1
end

4. check the table page status
SELECT %%physloc%% rowid, sys.fn_PhysLocFormatter (%%physloc%%) AS [Physical RID],DATALENGTH(a) a_length, DATALENGTH(b) b_length,DATALENGTH(c) d_length, * 
FROM test.dbo.myHeap 

rowid Physical RID a_length b_length d_length
0x9900000001000000 (1:153:0) 500 100 153
0x9900000001000100 (1:153:1) 500 100 153
0x9900000001000200 (1:153:2) 500 100 153
0x9900000001000300 (1:153:3) 500 100 153
0x9900000001000400 (1:153:4) 500 100 153
0x9900000001000500 (1:153:5) 500 100 153
0x9900000001000600 (1:153:6) 500 100 153
0x9900000001000700 (1:153:7) 500 100 153
0x9900000001000800 (1:153:8) 500 100 153
0x9B00000001000000 (1:155:0) 500 100 153

in fact, the first 9 rows are in page 153, however, the 10th row are in the other page 155.


4. let's check how much free space is in page 153
DBCC TRACEON(3604)
GO
dbcc page(test,1,153,3) 
GO

m_pageId = (1:153)                   m_headerVersion = 1                  m_type = 1
m_typeFlagBits = 0x4                 m_level = 0                          m_flagBits = 0x8000
m_objId (AllocUnitId.idObj) = 27     m_indexId (AllocUnitId.idInd) = 256  
Metadata: AllocUnitId = 72057594039697408                                 
Metadata: PartitionId = 72057594038779904                                 Metadata: IndexId = 0
Metadata: ObjectId = 2105058535      m_prevPage = (0:0)                   m_nextPage = (0:0)
pminlen = 757                        m_slotCnt = 9                        m_freeCnt = 1238
m_freeData = 6936                    m_reservedCnt = 0                    m_lsn = (32:86:2)
m_xactReserved = 0                   m_xdesId = (0:0)                     m_ghostRecCnt = 0
m_tornBits = 0                       

Allocation Status

GAM (1:2) = ALLOCATED                SGAM (1:3) = ALLOCATED               
PFS (1:1) = 0x63 MIXED_EXT ALLOCATED  95_PCT_FULL                         DIFF (1:6) = CHANGED
ML (1:7) = NOT MIN_LOGGED    

Slot 0 Offset 0x60 Length 760

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP     Record Size = 760

well, there is 1238 bytes free space, the row size is 760 byte, so there is enough free space to hold the 10th rows. why sql server create a new page to keep 10th row??? here we found a interesting info in the page head : PFS indicts the page is 95% full, so there is only 8060*5%=403 bytes free space.

let's review how sql server insert rows on Heap, because there is no clustered index, a new row is always inserted wherever room is available in the heap table. sql server will use IAMs and the PFS pages to keep track of which extents in ta file already belong to a table and which of the pages in those extens have space available. 


5. let's check the PFS page:
DBCC TRACEON(3604)
GO
dbcc page(test,1,1,3) 
GO

(1:153)      -              =     ALLOCATED  95_PCT_FULL                     Mixed Ext


The PFS page contains 1 byte for each page in a 8088-age range of a file, and the last three bits are used to indicate the age fullness. 

The bits in each byte has some meaning as illustrated below

  • bits 0-2: how much free space is on the page
    • 000 is empty
    • 001 is 1 to 50% full
    • 010 is 51 to 80% full
    • 011 is 81 to 95% full
    • 100 is 96 to 100% full
  • bit 3 : 1 if page has ghost records else 0
  • bit 4 : 1 if an IAM page else 0
  • bit 5 : 1 if mixed extent, 0 if uniform
  • bit 6 : 1 if page allocated, 0 if unallocated
we have 9 rows in page 153, 9*760/8060 =84.9% full, so PFS indicts it is 95% full. when sql server check page 153, it think the free space on page 153 is 8060*5%=403 bytes < row size 760bytes, so a new page 156 was created for 10th row.


6. In order to save space and keep the 10 rows in the same page, we can create a clustered index, 
ALTER TABLE dbo.myheap ADD CONSTRAINT
PK_mytest PRIMARY KEY CLUSTERED 
(
a
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO

then we can find all 10 rows are in the same page
SELECT %%physloc%% rowid, sys.fn_PhysLocFormatter (%%physloc%%) AS [Physical RID],DATALENGTH(a) a_length, DATALENGTH(b) b_length,DATALENGTH(c) d_length, * 
FROM test.dbo.myHeap 
rowid Physical RID a_length b_length d_length
0x9C00000001000000 (1:156:0) 500 100 153
0x9C00000001000100 (1:156:1) 500 100 153
0x9C00000001000200 (1:156:2) 500 100 153
0x9C00000001000300 (1:156:3) 500 100 153
0x9C00000001000400 (1:156:4) 500 100 153
0x9C00000001000500 (1:156:5) 500 100 153
0x9C00000001000600 (1:156:6) 500 100 153
0x9C00000001000700 (1:156:7) 500 100 153
0x9C00000001000800 (1:156:8) 500 100 153
0x9C00000001000900 (1:156:9) 500 100 153

DBCC TRACEON(3604)
GO
dbcc page(test,1,156,3) 
GO

m_pageId = (1:156)                   m_headerVersion = 1                  m_type = 1
m_typeFlagBits = 0x0                 m_level = 0                          m_flagBits = 0x4
m_objId (AllocUnitId.idObj) = 28     m_indexId (AllocUnitId.idInd) = 256  
Metadata: AllocUnitId = 72057594039762944                                 
Metadata: PartitionId = 72057594038845440                                 Metadata: IndexId = 1
Metadata: ObjectId = 2105058535      m_prevPage = (0:0)                   m_nextPage = (0:0)
pminlen = 757                        m_slotCnt = 10                       m_freeCnt = 476
m_freeData = 7696                    m_reservedCnt = 0                    m_lsn = (32:93:21)
m_xactReserved = 0                   m_xdesId = (0:0)                     m_ghostRecCnt = 0
m_tornBits = 0                       

Allocation Status

GAM (1:2) = ALLOCATED                SGAM (1:3) = ALLOCATED               
PFS (1:1) = 0x60 MIXED_EXT ALLOCATED   0_PCT_FULL                         DIFF (1:6) = CHANGED


this example is one of the reasons why clustered index is recommended on big table

reference
http://sqlsimplified.blogspot.com/2012/01/page-free-space.html