Confusion about SQL Server snapshot isolation and how to use it - sql

I am newbie to MS Sql Server. Just a few months experience maintaining SQL Server SPs. I am reading up about transaction isolation levels for optimising an SP and am quite confused. Please help me with below questions:
If I run DBCC useroptions in ClientDB, the default value for isolation level is 'read committed'. Does this mean the DB is set to isolation level is set to READ_COMMITTED_SNAPSHOT ON ?
Is SET TRANSACTION ISOLATION LEVEL (at transaction level) same as SET READ_COMMITTED_SNAPSHOT ON (at DB level)? By this I mean that IF my DB has SNAPSHOT enabled, then can I set isolation level in my SP and process data accordingly?
is ALLOW_SNAPSHOT_ISOLATION similar to above?
I have an SP which starts off with a very long running SELECT statement that also dumps it's contents into a temp table. Then uses the temp table to UPDATE/INSERT base table. There are about 8 mil records being selected and dumped into temp table, then a similar number of total rows updated/inserted. The problem we are facing is that this SP takes up too much disk space.
It is a client DB and we do not have permissions to check disk space/log size etc in the DB. So I do not know if the tempDB/tempDB-log is taking up this disk space or clientDB/clientDB-log is. But the disk space can reduce by as much as 10GB at one go! This causes the transaction log to run out of disk space (as disk is full) and SP errors out.
If I use SNAPSHOT isolation level, will this disk space get even more affected? as it uses tempDB to versionize the data?
What I really want to do is this:
SET transaction isolation level to SNAPSHOT. Then do the SELECT into Temp table. Then BEGIN TRANSACTION and update/insert base table for say ... 1 mil records. Do this in a LOOP until all records processed. Then END TRANSACTION. Do you think this is a good idea? Should the initial SELECT be kept out of the TRANSACTION? Will this help in reducing the load on the transaction logs?

An isolation level of "read committed" isn't the same thing as setting READ_COMMITTED_SNAPSHOT ON. Setting READ_COMMITTED_SNAPSHOT ON sets the default isolation level for all queries. A query or procedure that then uses "read committed" isolation level does use snapshot isolation. See Isolation Levels in the Database Engine and SET TRANSACTION ISOLATION LEVEL in Books Online.
When the READ_COMMITTED_SNAPSHOT database option is set ON, read
committed isolation uses row versioning to provide statement-level
read consistency. Read operations require only SCH-S table level locks
and no page or row locks. When the READ_COMMITTED_SNAPSHOT database
option is set OFF, which is the default setting, read committed
isolation behaves as it did in earlier versions of SQL Server. Both
implementations meet the ANSI definition of read committed isolation.
ALLOW_SNAPSHOT_ISOLATION doesn't change the default isolation level. It lets each query or procedure use snapshot isolation if you want it to. Each query that you want to use snapshot isolation needs to SET TRANSACTION ISOLATION LEVEL SNAPSHOT. On big systems, if you want to use snapshot isolation, you probably want this rather than changing the default isolation level with READ_COMMITTED_SNAPSHOT.
A database configured to use snapshot isolation does take more disk space.
Think about moving the log files to a bigger disk.

Related

INSERT and UPDATE blocking other session for DML operation

I am inserting data into table from xml data and when I have large xml then it is taking time to insert.
The problem is when inserting data with in BEGIN TRAN & COMMIT TRAN then other session not being able to read and write data into BrokerData table.
I am using default isolation. Please guide me what changes I should do in database as a result table should not be blocked when one session will insert or update data into that table as a result user from other session can do the DML operation on this BrokerData table.
BEGIN TRAN
INSERT INTO BrokerData
SELECT col.value('(Section/text())[1]', 'NVARCHAR(MAX)') AS Section
,col.value('(LineItem/text())[1]', 'NVARCHAR(MAX)') AS LineItem
,col.value('(XFundCode/text())[1]', 'NVARCHAR(MAX)') AS XFundCode
,col.value('(StandardDate/text())[1]', 'NVARCHAR(MAX)') AS StandardDate
,col.value('(StandardValue/text())[1]', 'VARCHAR(MAX)') AS StandardValue
,col.value('(ActualProvidedByCompany/text())[1]', 'VARCHAR(MAX)') AS ActualProvidedByCompany
FROM #BogyXML.nodes('/Root/PeriodicalData') AS tab (col)
COMMIT TRAN
First, it is good to know default isolation level for MSSQL Server is READ COMMITED.
READ COMMITTED uses pessimistic locking to protect data. With this
isolation level set, a transaction cannot read uncommitted data that
is being added or changed by another transaction. A transaction
attempting to read data that is currently being changed is blocked
until the transaction changing the data releases the lock. A
transaction running under this isolation level issues shared locks,
but releases row or page locks after reading a row.
So, in your case isolation level is not only problem. Loading XML is heavy operation and best practice is to load this data first in the some staging table.
Staging table can be:
Temporay table stored on the disk
Temporary table in memory
Memory optimized table
In your case and probably the best option is Memory-Optimized with two modes of durability SCHEMA_AND_DATA and SCHEMA_ONLY. If you want faster mode and if you don't need this data stored on the disk then it is SCHEMA_ONLY because non-durable memory-optimized tables do not incur logging overhead, transactions writing to them run faster than write operations on durable tables.
The In-Memory OLTP feature built into SQL Server 2016 adds a new
memory-optimized relational data management engine and a native stored
procedure compiler to the platform that you can use to run
transactional workloads with higher concurrency.
There is a little configuration for using this feature of In-Memory OLTP and if you are not in possition to use this feature temporary table can be replacement, but not good as memory-optimized tables.
Once the data is loaded and stored in the memory of SQL Server then you can start updating your real table which is BrokerData in your case.
However, updating real table is still a problem and I am pretty sure it will be faster if you prepare exactly data in the staging table and then move to the producation one. If you have small number of queries that are accessing to the producation table you can use READ UNCOMMITED isolation level.
The READ UNCOMMITTED isolation level is the least restrictive setting.
It allows a transaction to read data that has not yet been committed
by other transactions. SQL Server ignores any locks and reads data
from memory. Furthermore, transactions running under this isolation
level do not acquire shared (S) locks to prevent other transactions
from changing the data being read.
For these reasons, READ UNCOMMITTED is never a good choice for line of business applications where accuracy matters most, but might be acceptable for a reporting application where the performance benefit outweighs the need for a precise value.
It is really easy to use READ UNCOMMITED isolation level in the query directy:
SELECT *
FROM BrokerData
WITH (NOLOCK)
If the data is really important and you cannot allow your queries to read dirty data, and some data where is possibility of rollback high then you should think about READ_COMMITTED_SNAPSHOT. This isolation level is combination of READ COMMITED and SNAPSHOT.
The READ_COMMITTED_SNAPSHOT isolation level is an optimistic
alternative to READ COMMITTED. Like the SNAPSHOT isolation level, you
must first enable it at the database level before setting it for a
transaction.
During the transaction, SQL Server copies rows modified by other transactions into a collection of pages in tempdb known as the version store. When a row is updated multiple times, a copy of each change is in the version store. This set of row versions is called a version chain.
If you are going to use READ_COMMITTED_SNAPSHOT prepare a lot of memory in the tempdb.

Sql Table Locked By Readpast

I have a SQL View. I use READPAST on this SQL View. Because i don't want to see dirty data. But SQL READPAST locked this SQL View 's Table. I don't want to Table locking, i just want to locking Row.
Which method is correct?
When you select from a table you put a shared lock on it anyway...but if your table is locked and you don't want to see dirty data beside using readpast you should make sure that your table has an index and then set page lock to off and row lock to on..of course your query should have a where clause on on yhe indexed column..
https://learn.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-table?view=sql-server-2017
It seems the problem is isolation level.You must use read committed snapshot if you use sql server.This provides that you fetch only committed data.Also this does not cause table lock.But you must enable it in database level.You can look https://www.google.com/amp/s/www.red-gate.com/simple-talk/sql/performance/read-committed-snapshot-isolation-high-version_ghost_record_count/amp/ for configuration.
Also readpast is not a solution.It skips locked rows.So you get missing results.When a row is updated,Your select query ignore this row.But in read committed isolation level you get committed version of this row even if locked by another transaction and
this isolation level does not lock your table.I assume that you use default isolation level for the transaction.if you do not set a isolation level to the transaction it uses default isolation level and this is read committed.Read committed snapshot only works under read committed isolation level without locking.Except this for example in serializable isolation level it continues to lock.So you can use default isolation level for the transaction calling your view.

How to set transaction isolation level as read uncommitted as default for users

Is it possible to set the transaction isolation level to read uncommitted for users by default in MS SQL Server Management Studio 2012?
I was thinking it could either be done through editing a config file or changing a reg key but i haven't been able to locate anything that would change this yet.
As far as I know you can't change the default lock level.
For workloads with a lot of reads and fewer writes, you can avoid blocking queries with multiversion concurrency control. That's the default for Postgres and Oracle. In SQL Server, MVCC is called "read committed snapshot", and you can enable it with:
ALTER DATABASE YourDb SET READ_COMMITTED_SNAPSHOT ON;
You can’t. The default isolation level for all SQL Server databases is Read Committed, and your only option is to set the isolation level within a session, if you want to use a level other than the default.
You could also set SET TRANSACTION ISOLATION LEVEL within stored procedure body.
You cannot do this. As I'm sure you are aware; you should be careful when using the READ UNCOMMITTED isolation level. From MSDN:
When this option is set, it is possible to read uncommitted
modifications, which are called dirty reads. Values in the data can be
changed and rows can appear or disappear in the data set before the
end of the transaction. This option has the same effect as setting
NOLOCK on all tables in all SELECT statements in a transaction.
This means non of your results are guaranteed to contain accurate data.
You can do this in SQL Server Management Studio by setting execution options. (Tools>Options>Query Execution>SQL Server>Advanced>SET TRANSACTION ISOLATION LEVEL>)
NB: This only does it for queries executed from this instance of SSMS.

Oracle equivalent of SQL Server Snapshot isolation

In Microsoft SQL Server, I use the READ_COMMITTED_SNAPSHOT ISOLATION
ALTER DATABASE MyDatabase
SET ALLOW_SNAPSHOT_ISOLATION ON
ALTER DATABASE MyDatabase
SET READ_COMMITTED_SNAPSHOT ON
In Session 1,update the Principal from 4000 to 5000
BEGIN TRAN
Update MyTable Set Principal=5000 Where InvestorId=10
Now in Session 2, I say
Select Principal from MyTable where InvestorId=10
I get 4000, since the Session 1 Transaction is not committed.
If I do not use the READ_COMMITTED_SNAPSHOT isolation mode, and use
READ COMMITTED ISOLATION Mode then my Session 2 will keep waiting
If I use READ_UNCOMMITTED ISOLATION Mode then my session 2 will give 5000 (equivalent to using a nolock on the select statement)
In Oracle, if I perform the equivalent set of commands, by default it behaves as if the READ_COMMITTED_SNAPSHOT isolation mode is set.
I read in microsoft articles that SNAPSHOT isolation mode writes to the tempdb before updates are done.
-How does Oracle achieve this by default ?
-Is it also writing to the disk ? does it cause i/o problems ?
-Is the default locking level in Oracle different from SQL server ?
Thanks in advance for your help and time.
In Oracle, the READ_COMMITTED Isolation level is the default mode, i.e. data is written to the datafile (disk) and available for select by other sessions only after COMMIT. It uses UNDO segment for this.
It does not cause any I/O problem while doing a select
Oracle uses Row Level Locking by default.
You can have a look at Chapters 9 and 10 of Oracle DataBase Concepts for more details
In Oracle, its a non blocking queries by default.. similar to SQL snapshot isolation mode. The locking behaviour still in place but doesn't affect reads which only query committed data before transaction started on affected rows thus avoiding dirty reads.
see chapter 9 - Non blocking queries.

why objectID(N'tablename') does not lock and name='tablename' does lock on sys.objects?

Experiment details:
I am running this in Microsoft SQL Server management studio.
On one query window I run:
BEGIN TRANSACTION a;
ALTER table <table name>
ALTER column <column> varchar(1025)
On the other I run:
SELECT 1
FROM sys.objects
WHERE name = ' <other_table name>'
Or this:
SELECT 1
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[<other_table name>]')
For some reason the select with name= does not return until I do commit to the tranaction.
I am doing transaction to simulate a long operation of alter column that we have in our DB sometimes. Which I don't want to harm other operations.
This is because you are working under Default Transaction Isolation Level i.e ReadCommited .
Read Commited
Under Read Commited Trasanction Isolation Level when you explicitly Begin a Transaction Sql Server obtains Exclusive locks on the resources in order to maintain data integrity and prevent users from dirty reads. Once you have begin a transaction and working with some rows other users will not be able to see them rows untill you Commit your transaction or Rollback. However this is sql server's default behaviour this can be changed under different Transaction Isolation Level for instance under Read Uncommited You will be able to read rows which are being Modified/Used by other users , but there is a chance of you have Dirty Reads "Data That you think is still in database but Other user has changed it."
My Suggestion
If Dirty Reads is something you can live with go on than
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITED;
Preferably Otherwise stick to default behaviour of Sql Server and let the user wait for a bit rather than giving them dirty/wrong data. I hope it helps.
Edit
it does not matter if the both queries are executed under same or different isolation level what matters is under what isolation level the queries are being executed, and you have to have Read Uncommited transaction isolation level when you are making use of explicit transactions. If you want other user to have access to the data in during a transaction. Not recomended and a bad practice, I would personally use Snapshot Isolation which will extensively make use of tempdb and will only show the last commited data.