views:

233

answers:

4

if you have table BankAccount that has a column Amount and the value for this column for a specific row can be modified by multiple threads at the same time so it could happen so that the last one to set the value will win.

how do you usually handle this kind of situations ?

UPDATE: I heard that in MSSQL there is update lock UPDLOCK that locks the table or the row that is being updated, could I this here somehow ?

+2  A: 

You ussually use transactions to overcome this.

Have a look at Database transaction

astander
am I gonna have a lock over that table row during a transaction ?
Omu
Any proper database server will take care of the appropriate lock depending on the granularity of your sql call for you, i.e. row lock, page, table lock etc.
Codewerks
As long as it is done as you say. The OP question left open the possibility that it is a SELECT and THEN UPDATE from 2 threads.
astander
+3  A: 

You cannot have several threads modifying the same data at exactly the same time : it will always be the last one which set the value that'll "win".

If the problem is that several threads read and set the value at almost the same time, and reads and writes don't arrive on the right order, the solution is to use Transactions :

  • start a transaction
  • read the value
  • set the new value
  • commit the transaction

This ensures the read and the write will be done consistently, and no other thread will be able to modify the data during the same transaction.


Quoting the wikipedia page about Database Transactions :

A database transaction comprises a unit of work performed within a database management system (or similar system) against a database, and treated in a coherent and reliable way independent of other transactions. Transactions in a database environment have two main purposes:

  1. To provide reliable units of work that allow correct recovery from failures and keep a database consistent even in cases of system failure, when execution stops (completely or partially) and many operations upon a database remain uncompleted, with unclear status.

  2. To provide isolation between programs accessing a database concurrently. Without isolation the programs' outcomes are typically erroneous.

Pascal MARTIN
do I have a lock over this row during the transaction ? I mean if I write a SP that adds +5 to that cell and two threads (one started the transaction during the execution of the first one so the value of the cel was the initial) execute this SP it could happen that the result will be +5 instead of +10 ?
Omu
I suppose it depends on the exact DBMS you are using ; but, in the general situations, the first thread should lock the table or the row for a short instant, and the second thread should wait until the lock is released (or until there's a timeout)
Pascal MARTIN
+3  A: 

An update statement which references the current value would prevent overwriting. So, instead of doing something like

SELECT Amount FROM BankAccount WHERE account_id = 1

(it comes back as 350 and you want to subtract 50)...

UPDATE BankAccount SET Amount = 300 WHERE account_id = 1

do

UPDATE BankAccount SET Amount = Amount - 50 WHERE account_id = 1
Conspicuous Compiler
+1  A: 

You should have a database function/procedure which makes operations with the "Amount". This function/procedure should return if the operation was succeeded or failed (for example, you want take $1000, but current AMount is only $550, so operation can not be proceede).

Expamle in T-SQL:

UPDATE BankAccount SET Amount = Amount - 1000 WHERE BankAcountID = 12345 AND Amount >= 1000
RETURN @@ROWCOUNT

If the amaount was changed, the return value will be 1, otherwise 0.

Know, you can safely run this functions/procedures (in several threads too):

DECLARE @Result_01 int, Result_02 int, Result_03 int
EXEC @Result_01 = ChangeBankAccountAmount @BankAcountID = 12345, @Amount = 1000
EXEC @Result_02 = ChangeBankAccountAmount @BankAcountID = 12345, @Amount = 15
EXEC @Result_03 = ChangeBankAccountAmount @BankAcountID = 12345, @Amount = 600, @Amount = -2000

EDIT: Whole procedure in T-SQL:

CRATE PROC ChangeBankAccountAmount
@BankAccountID int,
@ChangeAmount int,
@MMinAmount int = 0
AS BEGIN
  IF @ChangeAmount >= 0
    UPDATE BankAccount SET Amount = Amount + @ChangeAmount WHERE BankAcountID = 12345
  ELSE
    UPDATE BankAccount SET Amount = Amount + @ChangeAmount WHERE BankAcountID = 12345 AND Amount >= @MMinAmount

  RETURN @@ROWCOUNT
END

Of course - the "int" datatype is not good for money, you should change it to datatype used in your table.

TcKs
"Transact-SQL" is the term Microsoft uses for the variant of SQL that Microsoft SQL Server uses, not MSSQL. (Not a company shill, just pedantic, I promise.)
Conspicuous Compiler
MSSQL is shortcut for Microsoft SQL Server product, which uses T-SQL as language. I don't think, the difference between "on MSSQL" and "in T-SQL" is important for this case, however, if you (or somebody else) want, you can change it.
TcKs
King of you to invite me to project my pedantry further, but I fear I lack sufficient reputation on SO to have that power just yet.
Conspicuous Compiler
Ok :). Is it right enough now :)?
TcKs