MySQL Verion: v5.0.95
Basically I have clients trying to get data - each client should only get unique rows.
START TRANSACTION;
SELECT id where result='new';
UPDATE SET result='old' WHERE id=$id;
COMMIT;
LOCK IN SHARED MODE on the select statement still lets other clients read the data, which seems like a problem.
Basically I need the data selected once, updated, and not read again by another client.
SELECT FOR UPDATE will block another read, while LOCK IN SHARED MODE will allow the read, but won't allow update from another client
Related
I have a website, which has a method (in the backing bean) which does several READ-requests (to check e.g. the users' rights) and then to insert some data.
the prodecure looks like this:
1.) SELECT * FROM userLocks (table which logs the users which are exclusively working on a topic, to avoid redundancy). it checks if there is currently no lock (user working) for the topic.
2.) SELECT * FROM ... (some other selects for further checking)
3.) INSERT INTO userLocks (curUser, timeout) values (...) (if everything was ok, create a new lock for the current user for the topic)
The problem I am facing now is: Between the first select and the insert many other users can request the same website which manipulates the data of the table userLocks. It seems to be not thread-safe.
Is there a solution for this problem? all I need is that all the used Tables are locked during this procedure for other users. (the other request just should wait until the lock is released, which dont take more than half a second...)
(I use InnoDB)
I would suggest you need to research SQL Transactions a little and row and table locks for InnoDB. This was an article that helped me https://blogs.oracle.com/mysqlinnodb/entry/introduction_to_transaction_locks_in
Basically our user provisioning algorithm does something like
-query for a new user
-update database to show you have that user
I'm wondering how to lock the ability for other instances of the process to do the "read" step while one has already started. So it's a little more aggressive than a typical transaction, because it needs to be a read-read lock, and of course unrelated processes should be able to read without being affected by the lock.
You can simply run the UPDATE query immediately to "steal" all inactive users for the current server.
Since individual UPDATE queries are always atomic, this will ensure that each user is only grabbed by one server.
Since MySQL does not allow you to return the updated rows from an UPDATE, you will need to add an identifier column to tell you which rows were "stolen".
Every time you provision users, pick a GUID, set the identifier column to that GUID in the UPDATE statement, then SELECT rows WHERE they still have that GUID.
I work with mySQL InnoDB Table.
Mysql database queries with php script.
When A user select a record in specific table I'd like to lock this and allow only read process for other user.
So I try to use
SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;
So my question is when user B try to select the same record how to know that the record is locked and when user A try to update record how to authorize this operation.
Thanks for helping
InnoDB row locks are only intended for temporary use during a transaction (e.g, when you want to temporarily hold up anyone else who's trying to look at or modify the row). If you want to allow users of your application to "lock" records, row locking isn't suited for this purpose. Instead, do it at the application level: create a column in the table to represent which of your users has locked the record.
I have a table of "commands to do" with a status ('toprocess', 'processing', 'done')
I have several instances (amazon ec2) with a daemon asking for "commands to do".
The daemon asks for rows with status 'toprocess', then it processes, and at the end of each loop it changes the status to 'done'.
The thing is that, before starting that loop, I need to change all rows 'toprocess' to status 'processing', so other instances will not take the same rows, avoiding conflict.
I've read about innodb row locks, but I don't understand them very well ...
SELECT * from commands where status = 'toprocess'
then I need to take the ID's of these results, and update status to 'processing' , locking these rows until they are updated.
How can i do it ?
Thank you
You'd use a transaction , and read the data with FOR UPDATE, which will block other selects that include the FOR UPDATE on the rows that gets selected
begin transaction;
select * from commands where status = 'toprocess' for update;
for each row in the result:
add the data to an array/list for processing later.
update commands set status='processing' where id = row.id;
commit;
process all the data
Read a bit about the FOR UPDATE , and InnoDB isolation levels.
A possible (yet not very elegant) solution may be to first UPDATE the record, then read its data:
Each deamon will have a unique ID, and the table will have a new column named 'owner' for that ID.
Then the deamon will run something like "UPDATE table SET status='processing', owner='theDeamonId' where status='toprocess' ... LIMIT 1"
While the update runs the row is locked, so no other deamon can read it.
After the update this row is Owned by a specific deamon, then it can run a SELECT to fetch all necessary data from that row (WHERE status='processing' AND owner= 'theDeamonId').
Finally, the last UPDATE will set the row to 'processed', and may (or may not) remove the owner field. Keeping it there will also enable some statistics about the deamons' work.
As far as I know you can't use MySQL to lock a row (using a built-in method). You have two options though:
If your table should not be read by any other process until the locks are released then you can use table level locking as described here
You can implement your own basic row locking by updating a value in each row you're processing, and then have all your other daemons checking whether this property is set (a BIT data type would suffice).
InnoDB locks at a row level for reading and updating anyway, but if you want to lock the rows for an arbitrary period then you may have to go with the second option.
How to prevent a race condition in MySQL database when two connections want to update the same record?
For example, connection 1 wants to increase "tries" counter. And the second connection wants to do the same. Both connections SELECT the "tries" count, increase the value and both UPDATE "tries" with the increased value. Suddenly "tries" is only "tries+1" instead of being "tries+2", because both connections got the same "tries" and incremented it by one.
How to solve this problem?
Here's 3 different approaches:
Atomic update
update table set tries=tries+1 where condition=value;
and it will be done atomically.
Use transactions
If you do need to first select the value and update it in your application, you likely need to use transactions. That means you'll have to use InnoDB, not MyISAM tables.
Your query would be something like:
BEGIN; //or any method in the API you use that starts a transaction
select tries from table where condition=value for update;
.. do application logic to add to `tries`
update table set tries=newvalue where condition=value;
END;
if the transaction fails, you might need to manually retry it.
Version scheme
A common approach is to introduce a version column in your table. Your queries would do something like:
select tries,version from table where condition=value;
.. do application logic, and remember the old version value.
update table set tries=newvalue,version=version + 1 where condition=value and version=oldversion;
If that update fails/returns 0 rows affected, someone else has updated the table in the mean time. You have to start all over - that is, select the new values, do the application logic and try the update again.
Use a single statement instead of two. A single UPDATE statement that performs both the read and the write will be atomic and won't conflict with another simultaneous update.
UPDATE table SET tries = tries + 1 WHERE ...
Or you can use transactions to make the two operations atomic.
BEGIN
SELECT ...
UPDATE ...
COMMIT
Or, more primitively, lock the table while you're reading/writing to it.
LOCK TABLES table WRITE
SELECT ...
UPDATE ...
UNLOCK TABLES