Imagine I have a table with two columns that have a point register from two different teams, like the one below:
TABLE:
first_team
second_team
first_team_points
second_team_points
The table has the relation of the two teams and the points they get when they finish a level during a period of time.
The two teams can play a level when they want and update their points when they finish. So it is possible that they update their own column of points at the same time. Like, A team updates first_team_points and B team updates second_team_points.
I know that InnoDB has a row-level locking, so I suppose that in that case the two updates will be realized in a sequencial order.
Im I wrong? Do I need to configurate something? Will the second update request cause a deadlock?
Thanks in advance!
Please provide the code for critique. Meanwhile, in general...
BEGIN; -- start the transaction
SELECT ... FOR UPDATE; -- if you need to look at something before updating
...
INSERT/UPDATE/etc -- make whatever changes
COMMIT;
There are several issues:
You want data integrity; transactions help a lot.
You want to avoid deadlocks -- without further details, I cannot assure that all deadlocks will be prevented. Be ready to re-do the transaction if you do get a deadlock.
One connection could get a "lock_wait_timeout". Think of this is as a deadlock that can be resolved by having one of the contenders wait. But, if the other connection takes too long, you could timeout. This is usually avoidable by making things run faster. (50 seconds is the default wait time; it is rare to hit it.)
Related
Assume a MySQL table called, say, results. results is automatically updated via cron every day, around 11AM. However, results is also updated from a user-facing front-end, and around 11AM, there are a lot of users performing actions that also update the results table. What this means is that the automatic cron and the user updates often fail with 'deadlock' errors.
Our current solution:
We have implemented a try/catch that will repeat the attempt 10 times before moving on the next row. I do not like this solution at all because, well, it isn't a solution, just a workaround, and a faulty one at that. There's still no guarantee that the update will work at all if the deadlock persists through 10 attempts, and the execution time is potentially multiplied by 10 (not as much of an issue on the cron side, but definitely on the user side).
Another change we are about to implement is moving the cron to a different time of day, so as to not have the automatic update running at the same time as heavy platform usage. This should alleviate much of the problems for now, however I still don't like it, as it is still just a workaround. If the usage patterns of our users changes and the platform sees heavy use during that period, then we'll encounter the same issue again.
Is there a solution, either technical (code) or architectural (database design) that can help me alleviate or eliminate altogether these deadlock errors?
Deadlocks happen when you have one transaction that is acquiring locks on multiple rows in a non-atomic fashion, i.e. updates row A, then a split-second later it updates row B.
But there's a chance other sessions can split in between these updates and lock row B first, then try to lock row A. It can't lock row A, because the first session has got it locked. And now the first session won't give up its lock on row A, because it's waiting on row B, which the second session has locked.
Solutions:
All sessions must lock rows in the same order. So either session 1 or 2 will lock row A, the other will wait for row A. Only after locking row A does any session proceed to request a lock for row B. If all sessions are locking rows in ascending order, then they will never deadlock (descending order works just as well, the point is that all sessions must do the same).
Make one atomic lock-acquiring operation per transaction. Then you can't get this kind of interleaving effect.
Use pessimistic locking. That is, lock all resources the session might need to update in one atomic lock request at the beginning of its work. One example of doing this broadly is the LOCK TABLES statement. But this is usually considered a hinderance to concurrent access to the tables.
You might like my presentation InnoDB Locking Explained with Stick Figures. The section on deadlocks starts on slide 68.
In my code I need to do the following:
Check a MySQL table (InnoDB) if a particular row (matching some criteria) exists. If it does, return it. If it doesn't, create it and then return it.
The problem I seem to have is race conditions. Every now and then two processes run so closely together, that they both check the table at the same time, don't see the row, and both insert it - thus duplicate data.
I'm reading MySQL documentation trying to come up with some way to prevent this. What I've come up so far:
Unique indexes seem to be one option, but they're not universal (it only works when the criteria is something unique for all rows).
Transactions even at SERIALIZABLE level don't protect against INSERT, period.
Neither do SELECT ... LOCK IN SHARE MODE or SELECT ... FOR UPDATE.
A LOCK TABLE ... WRITE would do it, but it's a very drastic measure - other processes won't be able to read from the table, and I need to lock ALL tables that I intend to use until I unlock them.
Basically, I'd like to do either of the following:
Prevent all INSERT to the table from processes other than mine, while allowing SELECT/UPDATE (this is probably impossible because it make so little sense most of the time).
Organize some sort of manual locking. The two processes would coordinate among themselves which one gets to do the select/insert dance, while the other waits. This needs some sort of operation that waits until the lock is released. I could probably implement a spin-lock (one process repeatedly checks if the other has released the lock), but I'm afraid that it would be too resource intensive.
I think I found an answer myself. Transactions + SELECT ... FOR UPDATE in an InnoDB table can provide a synchronization lock (aka mutex). Have all processes lock on a specific row in a specific table before they start their work. Then only one will be able to run at a time and the rest will wait until the first one finishes its transaction.
I have a situation where a specific transaction has to update the DB before any other transaction can look at a given table. Specifically, there is a prizing mechanism whereby there is a limited number of prizes and I'm concerned that if two requests arrive virtually at the same time the second request might find the prize still available by virtue of the first request having not had the time it needs to mark it as not-available.
I'm looking at the documentation for lock tables and it's not clear to me what's going on, and since it's extremely difficult to test this feature (as it requires two requests arriving at the same time), I was hoping for some advice.
My needs are extremely simple. There is only one table that I need to lock, while all the others can go about their business.
**request 1**:
lock prizes;
select from prizes;
mark prize as unavailable;
unlock prizes;
simultaneous **request n**
find the prizes table locked and wait for it to unlock //this is not critical, so long as they can just fail gracefully
select [no prize available]
As I said, it's CRITICALLY important that the other tables in this DB are completely unaffected by my lock, I got the sense from the documentation that when I lock one table, selecting another table will produce an error that says "other table isn't locked"... I'm probably not understanding this correctly, as that would seem idiotic, but just need to make sure that locking prizes doesn't affect anything else.
TIA
This is a perfect case for a transaction. Note that your tables need to be InnoDB for this to work.
start transaction;
select [your_fields] from [prizes_table] WHERE [your_where] FOR UPDATE;
// if is a valid recipient and prize gets taken:
update prizes set available=0 where id=[used_prize_id];
commit;
This should do exactly what you expect.
You should simply LOCK prizes WRITE to get the semantics you need. Since locking one or more tables prevents you from accessing any tables you have not locked for the lock's duration, you will also need to lock -- either for reading or for writing -- all other tables that you need to do the "mark prize as unavailable" step, if any.
Be aware that if you intend to access tables using an alias, you also need to supply the same alias in the LOCK statement. This topic is covered in the documentation, but I mention it explicitly because it could be overlooked.
If you need to lock only the record, you can use the select ... for update setting autocommit=0 and using the transaction.
This way the first request select the record and set the lock, then the second request is blocked until the first request commit (or rollback) the transaction or the wait exceeded the timout
I have a situation where I need to lock 2 tables, until some operations are done on those 2 tables individually. For that I have chosen "TRANSACTIONS".
So, between "START TRANSACTION" and "COMMIT".. if anyone tries to insert into those tables, what happens to those queries? will they be maintained in the queue and will be executed after the transaction is completed?
Ideally, whats my requirement is.. they should not get inserted until my transaction has got commited.
Please anyone tell me the scenario.
Thanks in advance!
SuryaPavan
When a transaction is initiated, it's isolated from the rest of the world. That's the I in ACID.
During the transaction, if anyone tries to insert anything - the insert will occur but it won't break the transaction you are performing in any way. Same rule applies in the other direction.
If you have the requirement to literally lock the entire table for insert until your transaction succeeds - that smells like a bad design and you should reconsider if what you're doing is really optimal.
If they got inserted before your transaction was committed (that is, in the middle of your transaction), that would make transactions fairly useless.
I assume you're using mysql (since your question is tagged with that)... why not open two command line sessions to your database and try it to see what happens? That would take far less time than posting your question and waiting for an answer, and you'll likely learn more in the process.
I'm writing a strategy-kind of multi user game for the web. It has a playfield (X by Y squares) that I plan on serialize and store in a BLOB in a MySQL (innodb) database, one row for each ongoing game.
I now try to figure out a good way of keeping the database updated with any changes to the playfield, and at the same time finding a convenient solution to how to handle things that happen to the playfield in the time frame between loading the page and actually making a move.
I don't use AJAX.
There will be at most 20 players in each game, each player making between 1 and 10 moves in 24 hours, so it is a "slow" game.
My plan (so far) is to also store a kind of checksum for the playfield next to the blob and compare the databases state with the state loaded before trying to make changes to the playfield.
What I worry about is how to prevent race conditions.
Is it enough to:
Begin transaction.
load playfield from table
if checksum differs - rollback and update the users view
if checksum unchanged - update table and commit changes
Is the BEGIN TRANSACTION enough to block the race, or do I need to do something more in step 2 to show my intent to update the table?
Thankful for all advice.
If you use SELECT ... FOR UPDATE when you load the playfield from the database, it will block other selects until you commit or rollback the transaction.
No. You will need to issue a LOCK TABLES command for the tables you need to protect against conflicting updates. This would look something like...
LOCK TABLE my_table WRITE;
More details may be found here... http://dev.mysql.com/doc/refman/5.1/en/lock-tables.html
Don't forget to UNLOCK them afterwards!