MySQL locking question - php

I have something similar to an online chat client. When one user sends a message it will be stored in a MySQL database table with id=currentID+1. I have another client long polling and waiting for message 'id=currentID+1'.
After this exchange, that row is never used again.
Do I need a lock in this case? The thing I'm worried about is that the reading PHP side will be able to see the row and read its values before the writing PHP side finishes creating the row.

MySQL won't make the row available until it's done reading (it automatically acquires a table lock in the case of MyISAM, or a row lock in the case of INNODB. So no, you should be ok so long as you're only inserting the row (and not later calling updates on it, etc)...

Writes in MySQL are atomic. Other queries cannot "see" the row until it is completely written, assuming you're using a single INSERT statement to do this.

the new row will only be seen by the select query after its inserted.
inserts are atomic
and yes myisam imploys table level locking, while innodb imploys row level locking.
and set of statements in a transaction are considered atmoic, there effect is not visible to any read until the transaction is committed, u can do a select shared to see uncommitted data.

Related

Partially lock a table for a specific attribute

We want to prevent some concurrency issues in a database (we use event sourcing and want to insert an event to the event log in case the event is a valid one).
The problem is that we need to check if a certain operation is allowed (requiring a SELECT query and some checks in php) and then run a INSERT query to actually perform the operation.
Now, we can simply LOCK the entire table, do our checks and if they succeed, then insert (and remove the lock).
The problem with this is that it locks the entire table, which is overkill (there will be lots of queries on this table). What I would like to do is to lock all queries that want to do this select-insert operation for a specific object_id, but allow queries for all other object_id's to continue as if there is no lock.
I searched a bit but couldn't find a lock attribute command. There seems to be a lock row command in innoDB, but it's not really what we want (I think I'm not 100% sure what it does).
We can of course try to manually handle the locks (check if there exists some column with object_id in some seperate lock table and wait untill there is none), but that feels a bit fishy and error prone.
So, here's the actual question: is it possible to lock a table for a specific value of a column (object_id)?
It would be awesome if the lock only held for the specific SELECT-INSERT queries, and not for standalone SELECT's, but that doesn't matter that much for now.
Consider manual arbitrary locks with GET_LOCK();
Choose a name specific to the rows you want locking. e.g. 'xxx_event_id_y'. Where 'xxx' is a string specific to the procedure and table and 'y' is the event id.
Call SELECT GET_LOCK('xxx_event_id_y',30) to lock the name 'xxx_event_id_y'.. it will return 1 and set the lock if the name becomes available, or return 0 if the lock is not available after 30 seconds (the second parameter is the timeout).
Use DO RELEASE_LOCK('xxx_event_id_y') when you are finished.
Be aware; You will have to use the same names in each transaction that you want to wait and calling GET_LOCK() again in a transaction will release the previously set lock.
GET_LOCK() docs
I actually use this method to lock our application cache too (even when it doesn't use the DB), so it has scope outside the database as well.
Migrate tables to innodb if not already done, and use transactions.

Update a whole table with PHP every x minutes according to a CSV

I have to update a big table (products) in a MySQL database, every 10 minutes with PHP. I have to run the PHP script with cron job, and I get the most up to date products from a CSV file. The table has currently ~18000 rows, and unfortunately I can not tell how much it will change in a 10 min period. The most important thing is of course I do not want the users to notice the update in the background.
These are my ideas and fears:
Idea1: I know that there is a way to load a csv file into a table with MySQL, so maybe I can use a transaction to truncate the table, and import the CSV. But even if I use transactions, as long as the table is large, I'm afraid that there will be a little chance for some users to see the empty database.
Idea2: I could compare the old and the new csv file with a library and only update/add/remove the changed rows. This way I think there it's not possible for a user to see an empty database, but I'm afraid this method will cost a lot of RAM and CPU, and I'm on a shared hosting.
So basically I would like to know which method is the most secure to update a table completely without the users noticing it.
Assuming InnoDB and default isolation level, you can start a transaction, delete all rows, insert your new rows, then commit. Before the commit completes, users will see the previous state.
While the transaction is open (after the deletes), updates will block, but SELECTs will not. Since it's a read only table for the user, it won't be an issue. They'll still be able to SELECT while the transaction is open.
You can learn the details by reading about MVCC. The gist of it is that any time someone performs a SELECT, MySQL uses the data in the database plus the rollback segment to fetch the previous state until the transaction is committed or rolled back.
From MySQL docs:
InnoDB uses the information in the rollback segment to perform the
undo operations needed in a transaction rollback. It also uses the
information to build earlier versions of a row for a consistent read.
Only after the commit completes will the users see the new data instead of the old data, and they won't see the new data until their current transaction is over.

Manually Lock a Single Table Row

I've locked entire tables before in MySQL but what I need to do is lock a specific row. More specifically, when my PHP script executes, I need it to Lock a specific row so it cannot be read from (or written to), run the rest of the code, then unlock the row when it's finished.
I know I need to use InnoDB for this, but I've been unable to find how to do this. I'm not sure if it requires transactions or not, and if so, how to use them for row level locking.
Update
Perhaps I'm thinking of this problem the wrong way. From the reading I've done, and been provided with, InnoDB tables auto lock rows when read/written, which I get. My concern though is that I want to introduce a delay in my PHP code via a sleep. I want any and all read attempts on the row that was previously read from to be locked until that sleep finishes, and the script finishes the rest of it's functions.
If I just run:
$result = $mysqli->prepare('...');
$result->bind_param('...', $...);
$result->execute();
$result->bind_result($...);
$result->fetch();
sleep('15');
//More script execution
$result->close();
Will that prevent other MySQL queries from accessing that selected row until I close the connection?
MySQL uses only table-level locking from MyISAM tables.For row-leve locking you would need to switch to innoDB.
Please see the documentation which tells about the lock functioning in mysql.

MySQL and UNIX_TIMESTAMP insert error

I have a problem with a project I am currently working on, built in PHP & MySQL. The project itself is similar to an online bidding system. Users bid on a project, and they get a chance to win if they follow their bid by clicking and cliking again.
The problem is this: if 5 users for example, enter the game at the same time, I get a 8-10 seconds delay in the database - I update the database using the UNIX_TIMESTAMP(CURRENT_TIMESTAMP), which makes the whole system of the bids useless.
I want to mention too that the project is very database intensive (around 30-40 queries per page) and I was thinking maybe the queries get delayed, but I'm not sure if that's happening. If that's the case though, any suggestions how to avoid this type of problem?
Hope I've been at least clear with this issue. It's the first time it happened to me and I would appreciate your help!
You can decide on
Optimizing or minimizing required queries.
You can cache queries do not need to update on each visit.
You can use Summery tables
Update the queries only on changes.
You have to do this cleverly. You can follow this MySQLPerformanceBlog
I'm not clearly on what you're doing, but let me elaborate on what you said. If you're using UNIX_TIMESTAMP(CURRENT_TIMESTAMP()) in your MySQL query you have a serious problem.
The problem with your approach is that you are using MySQL functions to supply the timestamp record that will be stored in the database. This is an issue, because then you have to wait on MySQL to parse and execute your query before that timestamp is ever generated (and some MySQL engines like MyISAM use table-level locking). Other engines (like InnoDB) have slower writes due to row-level locking granularity. This means the time stored in the row will not necessarily reflect the time the request was generated to insert said row. Additionally, it can also mean that the time you're reading from the database is not necessarily the most current record (assuming you are updating records after they were inserted into the table).
What you need is for the PHP request that generates the SQL query to provide the TIMESTAMP directly in the SQL query. This means the timestamp reflects the time the request is received by PHP and not necessarily the time that the row is inserted/updated into the database.
You also have to be clear about which MySQL engine you're table is using. For example, engines like InnoDB use MVCC (Multi-Version Concurrency Control). This means while a row is being read it can be written to at the same time. If this happens the database engine uses something called a page table to store the existing value that will be read by the client while the new value is being updated. That way you have guaranteed row-level locking with faster and more stable reads, but potentially slower writes.

MySQL locking problem

I have a simple setup of a set of writers and a set of readers working with a MySQL ISAM table. The writers are only inserting rows while the readers are only checking for new rows.
OK, so I know that I don't need a lock in this situation, since I'm not modifying existing rows. However my Writers are accessing one more table that does need a lock. I piece of information seems irrelevant except for the following limitation stated in the MySQL documentation:
A session that requires locks must
acquire all the locks that it needs in
a single LOCK TABLES statement. While
the locks thus obtained are held, the
session can access only the locked
tables. For example, in the following
sequence of statements, an error
occurs for the attempt to access t2
because it was not locked in the LOCK
TABLES statement:
So to access the table I want to insert rows into, I NEED to lock it, which is causing me performance problems. Any suggestions of how to get around this?
Typically you lock and unlock immediately around the queries which need locking. The documentation is simply stating that for any set of queries run while you have a lock, all tables involved must be locked. You can unlock as soon as you're done and touch any other tables.
Also consider that InnoDB supports row-level locking, which is often preferable to table-locking for performance since other queries on other rows will not be locked out for reading while you're also writing.

Categories