Check if transaction on a innoDB row is occurring? - php

If a database transaction is occurring on one thread is there a way for other threads to check to see if this transaction is already occurring before attempting the transaction? I know innoDB has row-level locking but I want the transaction to not be attempted if its already occurring on another thread, instead of waiting for the lock to be released and then attempting it.
To make my question clearer, an explanation of what I am trying to do may help:
I am creating a simple raffle using php and a innoDB table with MySQL. When a user loads the page to view the raffle it checks the raffle's database row to see if its scheduled end time has passed and if its "processed" column in the database is true or false.
If the raffle needs to be processed it will begin a database transaction which takes about 5 seconds before being committed and marked as "processed" in the database.
If multiple users load the page at around the same time I feel that it will process the raffle more than once which is not what I want. Ideally it would only attempt to process the raffle if no other threads are processing it, otherwise it would do nothing.
How would I go about doing this? Thanks.

You could implement table level locking and handle any subsequent connections to either be run in a queue or fail quietly:
https://dev.mysql.com/doc/refman/5.0/en/lock-tables.html
From the MySQL docs:
SET autocommit=0;
LOCK TABLES t1 WRITE, t2 READ, ...;
... do something with tables t1 and t2 here ...
COMMIT;
UNLOCK TABLES;

Related

Mysql: How to lock a whole table during transaction?

I have a transaction like this (innoDB):
START TRANSACTION;
SELECT 1 FROM test WHERE id > 5; // Let's assume this returns 0 rows
// Some very long operation here
//If the previous SELECT contained 0 results, this Insert will be executed
INSERT INTO test VALUES...;
COMMIT;
Now the problem is that if more sessions execute at the same time, then they will all end up executing the INSERT, because by the time the long task in those sessions has finished, all of the sessions had plenty of time to do the SELECT, and it will return 0 row result for all of them, since the INSERT haven't been executed quite yet due to the long task running.
So basically, I need to somehow lock the whole table test (so it can't be read by other sessions and they will be forced to wait) after I execute START TRANSACTION, but I am not sure how, because I can't use the LOCK TABLES test query, because that COMMITs the transaction I have started.
I also cannot use SELECT .. FOR UPDATE, because that only prevents existing rows from being modified, but it won't prevent new rows from being inserted.
If you've got some long-running task which only needs to be run once, set a flag in a separate table to say that the task has started, and check that instead of the number of rows written to the target table, so that another instance of the service does not kick off the job twice.
This also has the advantage that you're not relying on the specifics of the task in order to know the status of it (and therefore if the nature of the task changes, the integrity of your other code doesn't collapse), and you're not trying to work round the transaction by doing something horrible like locking an entire table. One of the points of using transactions is that it's not necessary to lock everything (although of course different isolation levels can be used, but that's not the subject of this discussion).
Then set the flag back to false when the last bit of the task has finished.

Locking innodb row and get a live version

I'm writing an online game, there is a section named send troops. When two or more users on one account try to send one movement the troops get doubled.
I want to get a live version of the row from mysql and prevent any read, write, update anything on that row untill I finish.
Is it actually possible? Because I sae only select for update and lock in share mode in innodb reference.
Any help is appericiated.
BEGIN;
SELECT ... FROM t ... WHERE ... FOR UPDATE;
...
UPDATE t ...;
COMMIT;
Others can read the rows from t, but they will either be delayed or deadlocked if they try to modify the row(s) touched by the SELECT.
Do you really need to prevent all reads for a given row? Please explain your scenario further.
Please find mysql document below in order to lock a particular row in innodb:
https://dev.mysql.com/doc/refman/5.5/en/innodb-locking.html

Mysql - Row level locking deadlocks in concurrent transactions

I have a table named and spot and reservation. spot contains column spot_id and spot_status. For reservation process i start a transaction and then acquire lock on specific row using this query. I am using php and mysql.
//start transaction
SELECT * FROM spot WHERE spot_id = $id FOR UPDATE ;
//if this query is successful then
1. set spot status to 1
2. insert corresponding values in reservation table.
and then commit else rollback
//transactions ends
lets say there are 2 concurrent transactions T1 and T2 which tries to reserve the same spot. From what i learnt from other's questions and answers in this site, if the transactions are not concurrent there would not be any problem, but in concurrent operation the processor can change from schedules of T1 to T2 anytime . After acquiring the locks on row by T1, lets say processor switch to transaction T2. T2 then tries to acquire locks on that same row but it cannot as it is locked by T1.
my questions are theoritical :
When is the lock removed by mysql? or is there any explicit way of
removing lock myself?
Since T2 transaction cannot lock the row which is the first query , does it rollback? or does processor keeps T2 waiting until it can lock the row?
what is the possibility of deadlock occuring in this problem?
Your strategy for lock management is correct.
If T1 first obtains the lock on spot/spot_id=$id, then T2 will wait until T1 either commits or rolls back the transaction. If T1 crashes or times out the rollback will be implicit.
If you want a deadlock, try this.
Get T1 to lock row 1 ("fork") and then row 2 ("knife").
Get T2 to lock row 2 ("knife") and then row 1 ("fork").
Run them concurrently. Eventually you'll get T2 holding only a knife, and T1 holding only a fork. They'll be staring at each other, going hungry, each waiting for the other to set down an implement.
Lock is removed when transaction which obtained the lock is ended that is committed or rolled back. There is no way in mysql to release row level lock until end of transaction because:
InnoDB stores row locks in a format such that it cannot know afterward which lock was set by which statement
However you can use user defined locks (aka advisory locks aka cooperative locks) which can be released at any moment.
T2 transaction will wait till lock can be acquired but no more than inno_lock_wait_timeout seconds. In the later case error will happen that lock cannot be acquired.
If you lock the row by id and then modify only this row then deadlock cannot happen. For deadlock to happen you need at least two resources which are acquired by transactions in different order.

Properly locking my database during a script run

I have no knowledge of locking whatsoever. I have been looking through some MySQL documentation and can't fully understand how this whole process goes about. What I need, is for the following events in my script to happen:
step 1) table user gets locked
step 2) my script selects two rows from table user
step 3) my script makes an update to table user
step 4) table user gets unlocked because the script is done
How do I go about this exactly? And what happens when another user runs this same script while the table is locked? Is there a way for the script to know when to proceed (when the table becomes unlocked?). I have looked into start transaction and select for update but the documentation is very unclear. Any help is appreciated. And yes, the table is innodb.
I believe what you are look for is the SELECT ... FOR UPDATE syntax available for InnoDB tables. This will lock only the records you want to update. You do need to wrap it in a transaction.
http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
For example, run your queries like this:
START TRANSACTION
SELECT ... FOR UPDATE
UPDATE ...
COMMIT
Eliminate step 2 by performing your select query as part of your update call. Then MySQL takes care of the rest. Only one write query can run at the same time, others will be queued behind.

Table [tablename] is not locked

I am writing a MySQL query that locks a table:
"LOCK TABLE table_1 WRITE"
After that i am executing some functions, and in one of those functions, I am executing another query, on another table that I haven't locked:
"SELECT * FROM completely_different_table_2"
Then i get the following error message as result:
Table 'completely_different_table_2' was not locked with LOCKED TABLES
Indeed, MySql is right to tell me that the table is not locked. But why does it throws an error? Anyone any ideas how I could solve this?
Thanks in advance.
You have to lock every table, that you want to use until the LOCK is released. You can give completely_different_table_2 only a READ LOCK, which allows other processes to read this table while it is locked:
LOCK TABLES table_1 WRITE, completely_different_table_2 READ;
PS: MySQL has a reason to do so. If you request a LOCK, you want to freeze a consistent state of your data. If you read data from completely_different_table_2 inside your LOCK, your data written to table_1 will in some way depend on this other table. Therefore you don’t want anyone to change this table during your LOCK and request a READ LOCK for this second table as well. If your data written to table_1 doesn’t depend on the other table, simply don’t query it until the LOCK is released.

Categories