![]() | |
![]() |
| | Thread Tools | Display Modes |
#1
| |||
| |||
|
#2
| |||
| |||
|
|
We have a Java application that runs against a variety of backends including Oracle and MSSql 2000 and 2005. Our application has a handful of active tables that are constantly being inserted into, and some of which are being selected from on a regular basis. We have a purge job to remove unneeded records that every hour "DELETE FROM <table> WHERE <datafield> < <sometimestamp>". This is how we are purging because we need 100% up time, so we do so every hour. For some tables the timestamp is 2 weeks ago, others its 2 hours ago. The date field is indexed in some cases, in others it is not... the DELETE is always done off of a transaction (autoCommit on), but experimentation has shown doing it on one doesn't help much. This task normally functions fine, but every once in a while the inserts or counts on this table fail with deadlocks during the purge job. I'm looking for thoughts as to what we could do differently or other experience doing this type of thing, some possibilities include: - doing a select first, then deleting one by one. This is a possibility, but its SLOW and may take over an hour to do this so we'd be constantly churning deleting single records from the db. - freezing access to these tables during the purge job... our app cannot really afford to do this, but perhaps this is the only option. - doing an update of an "OBSOLETE" flag on the record, then deleting by that flag... i'm not sure we'd avoid issues doing this, but its' an option. The failures happen VERY infrequently on sql2005 and much more frequently on sql2000. Any help or guidance would be most appreciated, thanks! |
#3
| |||
| |||
|
|
Hi, We have a Java application that runs against a variety of backends including Oracle and MSSql 2000 and 2005. Our application has a handful of active tables that are constantly being inserted into, and some of which are being selected from on a regular basis. We have a purge job to remove unneeded records that every hour "DELETE FROM <table> WHERE <datafield> < <sometimestamp>". This is how we are purging because we need 100% up time, so we do so every hour. For some tables the timestamp is 2 weeks ago, others its 2 hours ago. The date field is indexed in some cases, in others it is not... the DELETE is always done off of a transaction (autoCommit on), but experimentation has shown doing it on one doesn't help much. This task normally functions fine, but every once in a while the inserts or counts on this table fail with deadlocks during the purge job. I'm looking for thoughts as to what we could do differently or other experience doing this type of thing, some possibilities include: - doing a select first, then deleting one by one. This is a possibility, but its SLOW and may take over an hour to do this so we'd be constantly churning deleting single records from the db. - freezing access to these tables during the purge job... our app cannot really afford to do this, but perhaps this is the only option. - doing an update of an "OBSOLETE" flag on the record, then deleting by that flag... i'm not sure we'd avoid issues doing this, but its' an option. The failures happen VERY infrequently on sql2005 and much more frequently on sql2000. Any help or guidance would be most appreciated, thanks! Bob |
#4
| |||
| |||
|
|
On Jul 19, 3:13 pm, "bobdu... (AT) gmail (DOT) com" <bobdu... (AT) gmail (DOT) com> wrote: Hi, We have a Java application that runs against a variety of backends including Oracle and MSSql 2000 and 2005. Our application has a handful of active tables that are constantly being inserted into, and some of which are being selected from on a regular basis. We have a purge job to remove unneeded records that every hour "DELETE FROM <table> WHERE <datafield> < <sometimestamp>". This is how we are purging because we need 100% up time, so we do so every hour. For some tables the timestamp is 2 weeks ago, others its 2 hours ago. The date field is indexed in some cases, in others it is not... the DELETE is always done off of a transaction (autoCommit on), but experimentation has shown doing it on one doesn't help much. This task normally functions fine, but every once in a while the inserts or counts on this table fail with deadlocks during the purge job. I'm looking for thoughts as to what we could do differently or other experience doing this type of thing, some possibilities include: - doing a select first, then deleting one by one. This is a possibility, but its SLOW and may take over an hour to do this so we'd be constantly churning deleting single records from the db. - freezing access to these tables during the purge job... our app cannot really afford to do this, but perhaps this is the only option. - doing an update of an "OBSOLETE" flag on the record, then deleting by that flag... i'm not sure we'd avoid issues doing this, but its' an option. The failures happen VERY infrequently on sql2005 and much more frequently on sql2000. Any help or guidance would be most appreciated, thanks! Bob Read "Analyzing deadlocks with SQL Server Profiler", "How to resolve a deadlock", "Resolving Deadlocks in SQL Server 2000". Consider clustering your tables on your timestamp columns. BTW, unlike Erland, we set deadlock priority to high so that our purging interferes less with other activities. Alex Kuznetsov, SQL Server MVPhttp://sqlserver-tips.blogspot.com/ |
#5
| |||
| |||
|
|
Alex, i will go back and re-read those articles, but we've implemented as much as we can from them. As for the clustering of the tables based on the timestamps, thats something i'm totally unfamiliar with!!! Our schema is created straight from java with as much database agnostic code as possible, could you perhaps hint at how to do this for a noob like me? |
|
The insert looks likes this: exec sp_executesql N'INSERT INTO loads (Type, CreateDate, OtherDate, ObjectID) VALUES ( @P0 , @P1 , @P2 , @P3 )',N'@P0 int,@P1 datetime,@P2 datetime,@P3 int',2,''2007-07-20 09:38:15:477'',NULL,10 |
|
Heres a purge: declare @p1 int set @p1=2 exec sp_prepare @p1 output,N'',N'DELETE FROM loads WHERE CreateDate dateadd(SECOND, -40, CURRENT_TIMESTAMP)',1 select @p1 |
#6
| |||
| |||
|
|
bobdu... (AT) gmail (DOT) com (bobdu... (AT) gmail (DOT) com) writes: Alex, i will go back and re-read those articles, but we've implemented as much as we can from them. As for the clustering of the tables based on the timestamps, thats something i'm totally unfamiliar with!!! Our schema is created straight from java with as much database agnostic code as possible, could you perhaps hint at how to do this for a noob like me? CREATE CLUSTERED INDEX CreateDateIX ON loads(CreateDate) and change this to: LoadID BIGINT NOT NULL IDENTITY PRIMARY KEY NONCLUSTERED, The insert looks likes this: exec sp_executesql N'INSERT INTO loads (Type, CreateDate, OtherDate, ObjectID) VALUES ( @P0 , @P1 , @P2 , @P3 )',N'@P0 int,@P1 datetime,@P2 datetime,@P3 int',2,''2007-07-20 09:38:15:477'',NULL,10 And CreateDate is typically now? Heres a purge: declare @p1 int set @p1=2 exec sp_prepare @p1 output,N'',N'DELETE FROM loads WHERE CreateDate dateadd(SECOND, -40, CURRENT_TIMESTAMP)',1 select @p1 40 seconds? Egads, why do you save the data at all? :-) There is no index at all on CreateDate, so it will have to scan the full table. It's surprising that you need that many concurrent processes for it to happen. If CreateDate is the time for insertion and never changed before the purge, it would be a good idea to cluster on that column. That would minimize the point of conflict between the DELETE and the INSERT (but the non-clustered indexes could still be a conflict area.) But the SELECT COUNT(*) and the "normal" SELECT is still problematic. Particularly the normal SELECT looks like it would table scan to me since the WHERE clause is "Type >= @P0". But that depends on the values actually used and the distribution in the table. You could avoid this if you use snapshot isolation, but that is only available to SQL 2005. I don't really know what Alex meant that they set their deadlock priority to high. What I meant is that the purging process sould have SET DEADLOCK_PRIORITY LOW to declare itself a voluntary victim. In any case, on SQL 2000 the only deadlock priorties are LOW and NORMAL. SQL 2005 also has HIGH and numbered priorities. -- Erland Sommarskog, SQL Server MVP, esq... (AT) sommarskog (DOT) se Books Online for SQL Server 2005 athttp://www.microsoft.com/technet/prodtechnol/sql/2005/downloads/books... Books Online for SQL Server 2000 athttp://www.microsoft.com/sql/prodinfo/previousversions/books.mspx |
#7
| |||
| |||
|
|
bobdu... (AT) gmail (DOT) com (bobdu... (AT) gmail (DOT) com) writes: Alex, i will go back and re-read those articles, but we've implemented as much as we can from them. As for the clustering of the tables based on the timestamps, thats something i'm totally unfamiliar with!!! Our schema is created straight from java with as much database agnostic code as possible, could you perhaps hint at how to do this for a noob like me? CREATE CLUSTERED INDEX CreateDateIX ON loads(CreateDate) and change this to: LoadID BIGINT NOT NULL IDENTITY PRIMARY KEY NONCLUSTERED, The insert looks likes this: exec sp_executesql N'INSERT INTO loads (Type, CreateDate, OtherDate, ObjectID) VALUES ( @P0 , @P1 , @P2 , @P3 )',N'@P0 int,@P1 datetime,@P2 datetime,@P3 int',2,''2007-07-20 09:38:15:477'',NULL,10 And CreateDate is typically now? Heres a purge: declare @p1 int set @p1=2 exec sp_prepare @p1 output,N'',N'DELETE FROM loads WHERE CreateDate dateadd(SECOND, -40, CURRENT_TIMESTAMP)',1 select @p1 40 seconds? Egads, why do you save the data at all? :-) There is no index at all on CreateDate, so it will have to scan the full table. It's surprising that you need that many concurrent processes for it to happen. If CreateDate is the time for insertion and never changed before the purge, it would be a good idea to cluster on that column. That would minimize the point of conflict between the DELETE and the INSERT (but the non-clustered indexes could still be a conflict area.) But the SELECT COUNT(*) and the "normal" SELECT is still problematic. Particularly the normal SELECT looks like it would table scan to me since the WHERE clause is "Type >= @P0". But that depends on the values actually used and the distribution in the table. You could avoid this if you use snapshot isolation, but that is only available to SQL 2005. I don't really know what Alex meant that they set their deadlock priority to high. What I meant is that the purging process sould have SET DEADLOCK_PRIORITY LOW to declare itself a voluntary victim. In any case, on SQL 2000 the only deadlock priorties are LOW and NORMAL. SQL 2005 also has HIGH and numbered priorities. -- Erland Sommarskog, SQL Server MVP, esq... (AT) sommarskog (DOT) se Books Online for SQL Server 2005 athttp://www.microsoft.com/technet/prodtechnol/sql/2005/downloads/books... Books Online for SQL Server 2000 athttp://www.microsoft.com/sql/prodinfo/previousversions/books.mspx |
#8
| |||
| |||
|
|
- Sorry again, the type >= is just a way of getting more data, those queries are typically = based, and theres never more than 20 unique values for type. |
|
We can try the clustered index, but i'm NOT convinced why this is going to resolve the page lock issue... i've checked the execution plan for some of our queries and it looks like it sorts first, then does a clustered index scan of the ID. Is this really going to have more page locks than if the index was on the date? |
|
One final thing that i don't understand at the db level is transaciton models - using jdbc we're normally using READ_COMMITTED - we could use SERIALIZABLE but i'm not sure how either of these translates at the db level. If someone has a pointer to this info with respect to SQL server (and/or oracle) i'd love to hear it. |
#9
| |||
| |||
|
|
On Jul 20, 5:50 pm, Erland Sommarskog <esq... (AT) sommarskog (DOT) se> wrote: bobdu... (AT) gmail (DOT) com (bobdu... (AT) gmail (DOT) com) writes: Alex, i will go back and re-read those articles, but we've implemented as much as we can from them. As for the clustering of the tables based on the timestamps, thats something i'm totally unfamiliar with!!! Our schema is created straight from java with as much database agnostic code as possible, could you perhaps hint at how to do this for a noob like me? CREATE CLUSTERED INDEX CreateDateIX ON loads(CreateDate) and change this to: LoadID BIGINT NOT NULL IDENTITY PRIMARY KEY NONCLUSTERED, The insert looks likes this: exec sp_executesql N'INSERT INTO loads (Type, CreateDate, OtherDate, ObjectID) VALUES ( @P0 , @P1 , @P2 , @P3 )',N'@P0 int,@P1 datetime,@P2 datetime,@P3 int',2,''2007-07-20 09:38:15:477'',NULL,10 And CreateDate is typically now? Heres a purge: declare @p1 int set @p1=2 exec sp_prepare @p1 output,N'',N'DELETE FROM loads WHERE CreateDate dateadd(SECOND, -40, CURRENT_TIMESTAMP)',1 select @p1 40 seconds? Egads, why do you save the data at all? :-) There is no index at all on CreateDate, so it will have to scan the full table. It's surprising that you need that many concurrent processes for it to happen. If CreateDate is the time for insertion and never changed before the purge, it would be a good idea to cluster on that column. That would minimize the point of conflict between the DELETE and the INSERT (but the non-clustered indexes could still be a conflict area.) But the SELECT COUNT(*) and the "normal" SELECT is still problematic. Particularly the normal SELECT looks like it would table scan to me since the WHERE clause is "Type >= @P0". But that depends on the values actually used and the distribution in the table. You could avoid this if you use snapshot isolation, but that is only available to SQL 2005. I don't really know what Alex meant that they set their deadlock priority to high. What I meant is that the purging process sould have SET DEADLOCK_PRIORITY LOW to declare itself a voluntary victim. In any case, on SQL 2000 the only deadlock priorties are LOW and NORMAL. SQL 2005 also has HIGH and numbered priorities. -- Erland Sommarskog, SQL Server MVP, esq... (AT) sommarskog (DOT) se Books Online for SQL Server 2005 athttp://www.microsoft.com/technet/prodtechnol/sql/2005/downloads/books... Books Online for SQL Server 2000 athttp://www.microsoft.com/sql/prodinfo/previousversions/books.mspx Thanks again for the thoughts, a few followups: - CreateDate is typically the time the record was created, so now yes. - 40 seconds is just a sample, the date typically goes between 2 hours and 2 weeks after the record was created. - Sorry again, the type >= is just a way of getting more data, those queries are typically = based, and theres never more than 20 unique values for type. I probably should've used this analogy before, but consider these tables like "audit" tables. We simply record lots of audits, do funky searches on these audits, and then purge these audits at a given schedule when they're no longer necessary. We can try the clustered index, but i'm NOT convinced why this is going to resolve the page lock issue... i've checked the execution plan for some of our queries and it looks like it sorts first, then does a clustered index scan of the ID. Is this really going to have more page locks than if the index was on the date? About the deadlock priority - i don't think it would be appropriate if the purge job was the one thats failing, mainly because if it starts getting behind in its work it will probably just have more and more difficulty purging the future... the guys doing the inserts/selects while important, can fail and will retry again later, but its ugly. I'm (obviously) no dba and am finding it difficult that sql server can kill clients because it's discovered a deadlock, when just looking at the competing sql could never lead me to that conclusion. One final thing that i don't understand at the db level is transaciton models - using jdbc we're normally using READ_COMMITTED - we could use SERIALIZABLE but i'm not sure how either of these translates at the db level. If someone has a pointer to this info with respect to SQL server (and/or oracle) i'd love to hear it. Thanks a million again!!! |
#10
| |||
| |||
|
|
bobdu... (AT) gmail (DOT) com (bobdu... (AT) gmail (DOT) com) writes: - Sorry again, the type >= is just a way of getting more data, those queries are typically = based, and theres never more than 20 unique values for type. For a query like: SELECT LoadID, Type, CreateDate, OtherDate, ObjectID FROM loads WHERE Type = @P0 ORDER BY loads.LoadID DESC An index on (Type, LoadID DESC) appears to be a good idea. If the query is selective as you say, the optimizer would use the index and bookmark lookups. And with the ID in the index, the result is already sorted. We can try the clustered index, but i'm NOT convinced why this is going to resolve the page lock issue... i've checked the execution plan for some of our queries and it looks like it sorts first, then does a clustered index scan of the ID. Is this really going to have more page locks than if the index was on the date? The sort is usually performed at the end of the query plan - you should read it from right to left. Sorts can appear earlier in the plan, if the optimizer settles for a merge join, which requires sorted input. I cannot guarantee that the clustered index on CreateDate saves the day, but I think it will be difficult without it. The way the query is written, without a parameter, the optimizer will assume a 30% hit-rate which is far beyond what is good NC-index seek + bookmark lookup. Of course, you could force the index with an index hint, but I don't think that would be a good thing. What is a possible alternative, though, is that the purge first performs: SELECT @loadid = MAX(LoadID) FROM loads WHERE CreateDate < dateadd(SECOND, -40, CURRENT_TIMESTAMP) And the issues "DELETE loads WHERE LoadID <= @loadid". For this to be successful, you still need an index on (CreateDate, LoadID DESC). One final thing that i don't understand at the db level is transaciton models - using jdbc we're normally using READ_COMMITTED - we could use SERIALIZABLE but i'm not sure how either of these translates at the db level. If someone has a pointer to this info with respect to SQL server (and/or oracle) i'd love to hear it. READ_COMMITTED means that once you have read data, you release the lock for it. Thus, if you read the same data twice in the same transaction, you can get different result. SERIALIZABLE means that you do not tolerate that anyone adds or changes rows that could affect data you have read. I don't think serializable will work here. First of all, things will slow down, because they are just that: serialized. Second, serializable appears to often lead to deadlocks, because SQL Server is not very good at always taking the range locks in the same order, or so it seems. There are a few more possibilities: On SQL 2005, you can use any of the two varities of SNAPSHOT isolation. With this isolation level, readers read from the version store, and cannot block writers. Yes, just like on Oracle. But watch out! If you issue a SELECT COUNT(*) while the purge is running, do you want the number of rows before or after the purge? Snapshot will give you the former, that is old and out-dated data. Then you can go for READ UNCOMMITTED, and do dirty reads. Then your SELECT COUNT(*) will give you something between before and after. Finally there is the READPAST hint which means that you skip locked rows. In this case two, you may also get something in between before and after, although it's more likely to be closer to after. ...and a completely different approach is to use partitioned tables (requires SQL 2005 Enterprise). In that case, you would only know and then alter your partition scheme and partition function. A purge would be a pure metadata operation. Switch out the old data, and then drop that table. In this case, you would purge far less often, but when you do it, it would be snap. -- Erland Sommarskog, SQL Server MVP, esq... (AT) sommarskog (DOT) se Books Online for SQL Server 2005 athttp://www.microsoft.com/technet/prodtechnol/sql/2005/downloads/books... Books Online for SQL Server 2000 athttp://www.microsoft.com/sql/prodinfo/previousversions/books.mspx |

![]() |
| Thread Tools | |
| Display Modes | |
| |