Read Only Transaction or No Lock Query (Go) - sql

I am currently working on fixing some performance issues we are having with our application. I am curious to hear what/if are the best practices for my situation. I have a SELECT statement that is causing locks to happen. Would it be more advisable to just have the select query have (nolock) or setup a read-only transaction? From what I've found, setting up a transaction is the only way to set RO in Go.
Here is the query:
SELECT * 
FROM [pipe].[Message]
ORDER BY [MessageID] DESC OFFSET 0 ROWS 
FETCH NEXT 100 ROWS ONLY

Related

Select query hangs when a large number of rows have an update lock

I am designing a program that will read a queue table. I am using locking to make sure that multiple instances do not pull the same row.
I am locking rows like this:
BEGIN TRANSACTION
UPDATE top(10) Source with (ROWLOCK, READPAST, updlock)
SET status = status + 1
With another connection I read the rows like this:
SELECT COUNT(*) FROM Source WITH (ROWLOCK, READPAST, updlock)
The count from the select statement does not include the rows I have locked. This is exactly what I want.
This works fine when I pick the top 10 rows, or 100, or even 1000. Somewhere around 4,690 (it's not consistent) the select begins to hang until the transaction is committed. It's not just slow; it waits for the transaction to end.
This is a test. My real query will not be using top. It will use a join which also causes the problem when too many rows are locked.
Any ideas on what may cause this?
Is there a better way to have multiple instances read a table and not have conflicts?

SQL - how can I avoid a concurrency conflict on this UPDATE?

I have a table. Multiple external processes will be simultaneously looking at it & updating it.
I want to write some SQL to update "the one row with the highest PRIORITY, which has not already been updated (by another external process)".
The processes access my table using the SQL below. But my fear is... I'm not sure what will happen if two processes attempt to run this code at nearly the same time. Is there some risk that two nearly simultaneous instances of this code will both try to update the same row?
I just want to be sure that the code, as written (using a CTE) runs the SELECT and UPDATE in a single transaction, with no chance of multiple processes selecting & updating the same row. If that's not already the case, then let me know what I'd need to change to accomplish this.
Thanks!
WITH MostUrgentWorkAssignment AS
(
SELECT TOP(1) *
FROM
dbo.WorkAssignments a
WHERE
a.UserID IS NULL
ORDER BY
a.Priority
)
UPDATE MostUrgentWorkAssignment
SET UserID = #UserID
Shouldn't that TSQL be something like this, avoiding the unnecessary CTE?
UPDATE [dbo].[WorkAssignments]
SET
[UserId] = #UserID
FROM
[dbo].[WorkAssignments] [A]
JOIN
(
SELECT TOP 1
[A].[Id]
FROM
[dbo].[WorkAssignments] [A]
WHERE
[A].[UserId] IS NULL
ORDER BY
[A].[Priority]
) [MostUrgentWorkAssignment]
ON [MostUrgentWorkAssignment].[Id] = [A].[Id];
If you have a sensible Isolation Level this statement will be safe. The select and update will run within an implicit transaction as they are part of the same statement. I suspect this is equally true, with or without the CTE.

SQL Server - READPAST, UPDLOCK update method?

We're in need of yet another massive update which as it is, would require downtime because of the risk of extensive locking problems. Basically, we'd like to update hundreds of millions of rows during business hours.
Now, reducing the updates to manageable < 5000 batch sizes helps, but I was wondering if it was feasible to create a template to only read and lock available rows, udpate them, and move on to the next batch? The idea is that this way we could patch some 95% of the data with minimal risk, after which the remaining set of data would be small enough to just update at once during a slower period while watching out for locks.
Yes, I know this sounds weird, but bear with me. How would one go about doing this?
I was thinking of something like this:
WHILE ##ROWCOUNT > 0
BEGIN
UPDATE TOP (5000) T
SET T.VALUE = 'ASD'
FROM MYTABLE T
JOIN (SELECT TOP 5000 S.ID
FROM MYTABLE S WITH (READPAST, UPDLOCK)
WHERE X = Y AND Z = W etc...) SRC
ON SRC.ID = T.ID
END
Any ideas? Basically, the last thing I want is for this query to get stuck in other potentially long-running transactions, or to do the same to others in return. So what I'm looking for here is a script that will skip locked rows, update what it can with minimal risk for getting involved in locks or deadlocks, so it can be safely run for the hour or so during uptime.
Just add WITH (READPAST) to the table for single-table updates:
UPDATE TOP (5000) MYTABLE WITH (READPAST)
SET VALUE = 'ASD'
WHERE X = Y AND Z = W etc...
If you are lucky enough to have a single table involved you can just add WITH (READPAST) and the UPDATE itself will add an exclusive lock on just the rows that get updated.
If there is more than one table involved, it may become more complicated. Also be very careful of the WHERE clause because that could add more load than expected - the first few batches are fine but become progressively worse if scanning the whole table is necessary to find enough rows to satisfy the TOP. You might want to consider a short timeout value for each batch.

SQL Server - Simultaneous Inserts to the table from multiple clients - Check Limit and Block

We are recently facing one issue with simultaneous inserts into one of our sal server tables from multiple clients. I hope you guys can help us through.
We are using stored procedure to do the transactions. In that stored procedure, for each transaction, we calculate total sales so far. If the total sales is less than the set limit,
then the transaction will be allowed. Otherwise, the transaction will be denied.
it works fine most of times. But, sometimes when multiple clients trying to do the transaction exactly at the same time, the limit check is failing as both the transactions get done.
Can you guys suggest how we can effectively enforce the limit all the time? Is there any better way to do that?
Thanks!
I don't think it is possible to do this declaratively.
If all inserts are guaranteed to go through the stored procedure and the SaleValue is not updated once inserted then the following should work (I made up table and column names as these were not supplied in the initial question)
DECLARE #SumSaleValue MONEY
BEGIN TRAN
SELECT #SumSaleValue = SUM(SaleValue)
FROM dbo.Orders WITH (UPDLOCK, HOLDLOCK)
WHERE TransactionId = #TransactionId
IF #SumSaleValue > 1000
BEGIN
RAISERROR('Cannot do insert as total would exceed order limit',16,1);
ROLLBACK;
RETURN;
END
/*Code for INSERT goes here*/
COMMIT
The HOLDLOCK gives serializable semantics and locks the entire range matching the TransactionId and the UPDLOCK prevents two concurrent transactions locking the same range thus reducing the risk of deadlocks.
An index on TransactionId,SaleValue would be best to support this query.

What does this do?

Once in a while, I need to clear out the anonymous user profiles from the database. A colleague has suggested I use this procedure because it allows a little breathing space from time to time for other procedures to run.
WHILE EXISTS (SELECT * FROM aspnet_users WITH (NOLOCK)
WHERE userID IN (SELECT UserID FROM #AspnetUsersToDelete))
BEGIN
SET ROWCOUNT 1000
DELETE FROM aspnet_users WHERE userID IN (SELECT UserID FROM #AspnetUsersToDelete )
print 'aspnet_Users deleted: ' + CONVERT(varchar(255), ##ROWCOUNT)
SET ROWCOUNT 0
WAITFOR DELAY '00:00:01'
END
This is the first time I've seen the NOLOCK keyword used and the logic for the rowcount seems backwards to me. Does anyone else use a similar sort of technique for providing windows in long running procedures and is this the best way of doing things?
Any time I anticipate deleting a very large number of rows, I'll do something similar to this to keep transaction batch sizes reasonable.
For SQL Server 2005+, you could use DELETE TOP (1000)... instead of the SET ROWCOUNT statements. I usually do:
SELECT NULL; /* Fudge ##ROWCOUNT value for first time in loop */
WHILE (##ROWCOUNT <> 0) BEGIN
DELETE TOP (1000)
...
END /* WHILE */
The SET ROWCOUNT 1000 means it will only process one thousand rows in the following statements (i.e., DELETE statement). SET ROWCOUNT 0 means each statement processes however many rows are relevant.
So basically, over all it deletes one thousand rows, waits a second, deletes another thousand, and continues that until there are no more to delete.
The WITH (NOLOCK) prevents the data from being locked, meaning that multiple queries running simultaneously can access the data. This allows your query to be a little faster. For more information about NOLOCK, consult the following link:
http://www.mollerus.net/tom/blog/2008/03/using_mssqls_nolock_for_faster_queries.html
(NOLOCK) allows dirty reads. Basically, there is a chance that if you are reading data out of the table while it is in the process of being updated, you could read the wrong data. You can also read data that has been modified by transactions that have not been committed yet as well as a slew of other problems.
Best practice is not to use NOLOCK unless you are reading from tables that really don't change (such as a table containing states) or from a data warehouse type DB that is not constantly updated.