I'd like to insert a record in SQLserver, but I'm worried about how to set the main key.
I used to use max (main key) +1, but with this method, when multiple processes are inserted at the same time,
You may try to register the same primary key.
I looked into it a little bit, but it seems that there is a TransactionScope that can be exclusive.
However, when I tried it
"Unhandled Exception: System.Data.SqlClient.SqlException:
Transaction (Process ID 65) deadlocks with other processes with locked resources and
This transaction was subject to that deadlock.Please try the transaction again."
This exception has occurred, and it doesn't work.
Here is the code we tried (C#).The above exception occurred when I started the same program almost simultaneously.
class program
{
static void Main (string[]args)
{
DwhDBAaccess.DwhTableAdaptors.workSampleTableAdapter=newDwhDBAaccess.DwhTableAdaptors.workSampleTableAdapter();
TransactionOptions = new TransactionOptions {IsolationLevel=IsolationLevel.Serializable};
using(TransactionScopes=newTransactionScope(TransactionScopeOption.RequiresNew,o))
{
int max = 0;
try
{
max=adapter.GetData().Max(x=>x.id);
}
catch (Exception e)
{
max = 0;
}
System.Threading.Threading.Sleep(10000);
adapter.InsertQuery(max+1, "hello world.");
Console.WriteLine("Insert id="+(max+1).ToString());
ts.Complete();
}
}
}
How do I avoid registering the same id by using exclusive control well?
Thank you for your cooperation.
If there is no particular inconvenience, it is recommended that the identity attribute be added to the PK column.
The exception to the problem is a deadlock detected and is occurring correctly.
So what you need is an implementation that will retry the action if this exception occurs.
First of all, SqlException.Number
of the error is 1205
, so we will catch it under the following conditions:
try
{
// transaction processing
}
catch(SqlException ex)
{
if (ex.Number==1205)
{
// retry processing
}
else
{
throw;
}
}
The retry method may depend on the requirements, but the simplest method is through the for
loop.
// Number of retries
constint RETRY=5;
for (vari=0;i<RETRY;i++)
{
try
{
// transaction processing
break;
}
catch(SqlException ex)
{
if (ex.Number==1205&i<RETRY-1)
{
continue;
}
throw;
}
}
It has nothing to do with the question, but SELECT
has occurred in the following parts, so you should rewrite it to SQL such as SELECT COALESCE(MAX(id), 0)FROM...
.
int max=0;
try
{
max=adapter.GetData().Max(x=>x.id);
}
catch (Exception e)
{
max = 0;
}
Describes the concept of IsolationLevel.Serializable
.
max=adapter.GetData().Max(x=>x.id);
You get the maximum value in , but of course you will refer to all the records.The transaction retrieves the shared lock for all referenced records.After that,
adapter.InsertQuery(max+1, "hello world.");
INSERT
runs after retrieving the exclusive lock
Now, shared and exclusive locks have the following properties:
As you can see, locking is only a waiting list, not a deadlock in itself.However, if you examine the behavior of the code in question based on the nature of this lock, you will see that the following conditions exist:
Multiple sessions run max=adapter.GetData().Max(x=>x.id)
, each holding a shared lock, and each holding a shared lock after Thread.Sleep(10000)
and each holding an exclusive lock in adapter.InsertQuery(max+1, "hello world.")
.This is the deadlock.
Based on the above mechanisms of deadlock occurrence, we will consider countermeasures.
Lowering the consistency level and using IsolationLevel.ReadCommitted
will prevent you from continuing to maintain the shared lock.This allows for exclusive lock acquisition, eliminating deadlocks and allowing for the original primary key duplication error.If you encounter duplicate errors, try again.
SELECT
) and insert (INSERT
) simultaneouslyWrite SQL statements directly without using the TableAdapter
feature.
Use the IDENTITY
property.It is different from the sequence number proposed by Yukihane.
© 2024 OneMinuteCode. All rights reserved.