how to lock mysql myisam table with php - php

Can anyone test mysql table lock using 2 php scripts. And mysql_query().
I tried for a day but i couldn't get table locked.
I want when one php script uses mysql table all other scripts wouldn't have access to it.
Can you provide 2 simple tested php scripts. And if you can show how they work online it would be perfect.
But it should be so that when first script works and locks mysql table other scripts should wait for its turn.
Like a queue only one script can access myisam mysql table at the same time. But please test your script before answer because i tried many things many advises and nothing works.

I wouldn't advise locking db tables explicitly if it is not aimed to manage complex db logic at transaction level. The queries will still be sent out, but fail due to the lock or worse other transactions become deadlocked because of a lock acquired at the wrong time.
As a consequence you are like to tank any semblance of performance in the application.
Edit:
http://dev.mysql.com/doc/refman/5.1/en/lock-tables.html
The documentation for mysql gives a detailed explanation of how the locks operate. Locks are acquired for the session using it, so if you want your session to not have access to certain tables and aliases, then you want to lock everything besides the tables you want to deny your session access to.
Not sure what locks you wish to test as an example.
define("READ_LOCK", 0)
define("WRITE_LOCK",1)
function lock_on_to_tables ($Tables, $lockType=0)
{
$sql = "LOCK TABLES "
foreach ($Tables as $table)
$sql .= $table . " ,";
$sql = substr($sql, 0, -1); // cut off last comma
$sql .= $lockType ? " WRITE" : " READ;
mysqli_query($sql); // or pdo or whatever is in use.
}
Unlock is just
mysqli_query("UNLOCK TABLES") ;

You do not lock table with PHP. You do lock with mysql query. So basically you do a query with syntax as described in mysql docs and that's it. For example:
mysqli_query("LOCK TABLES my_table READ");
And drop mysql_ in favour of mysqli_ or PDO

There are two kinds of locks. One is what #WebnetMobile.com posted.
The other is called an advisory lock - you put up a flag, and everyone is required to check the flag before being allowed access. It's on you to check for the flag everywhere that needs it. But the advantage is you can exactly tune the locking to your needs.
Also, with InnoDB you can lock specific rows of the table, without locking the entire table.
See: http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

Related

Confusion on timestamping: solution for concurrency issue

I'm currently developing a website and while working on database design, i had some concern on concurrency issue, I'm considering using timestamping to avoid this.
My understanding in timestamping is that it works this way:
There is a field for let's say "DateModified" wherein be updated every update on that specific row.
Then whenever there are 1 or more users accessing that row like reading first then eventually update it.
In my understaning of timestamping for this to work, I need a condition that will read first the "DateModified" like in my code.
readdatemodified = Select DateModified From Transaction where ID = ?
datemodified = Select DateModified From Transaction where ID = ?
IF datemodified == readdatemodified
UPDATE Transaction where ID = ?
ELSE
Message "There's someone updated the record. Please try again".
IF: UPDATE the record successfully
ELSE: Here the record will be retrieve again by accessing the database to ensure that the record is the updated one.
I solved the concurrency issue here but my new concern is how I access the database.
I will accessed the database multiple times every update?
Is there a way wherein I could minimize the database access using timestamping?
If you want the concurrency / checks in application logic, then try a CAS (Check And Set) algorithm, if you want concurrent changes to not happen, use transactions (as mentioned by Acyclic Tau)
Have you considered not using timestamping, but using transactions and locking reads:
http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
This might be a better solution to your problem. MySQL 'select for update' behaviour shows some examples of behaviour in the question.
The capabilities provided by locking are dependent on the underlying database engine you use:
MyISAM - Table level locking
InnoDB - Row level locking
A good overal description of capabilities and advantages can be found on the MySQL site here: http://dev.mysql.com/doc/refman/5.0/en/internal-locking.html

Locking row for two updates

I need to do two updates to rows but I need to make sure they are done together and that no other query from another user could interfere with them. I know about SELECT...FOR UPDATE but I imagine after the first update it will of course be unlocked which means someone could interfere with the second update. If someone else updates that row first, the update will work but will mess up the data. Is there anyway to ensure that the two updates happen how they are supposed to? I have been told about transactions but as far as I know they are only good for making sure the two updates actually happen and not whether they happen "together," unless I am mistaken and the rows will be locked until the transaction is committed?
Here are the queries:
SELECT z FROM table WHERE id='$id'
UPDATE table SET x=x+2 WHERE x>z
UPDATE table SET y=y+2 WHERE y>z
I made a mistake and didn't give full information. That was my fault. I have updated the queries. The issue I have is that z can be updated as well. If z is updated after the SELECT but before the other two updates, the data can get messed up. Does doing the transaction BEGIN/COMMIT work for that?
Learn about TRANSACTION
http://dev.mysql.com/doc/refman/5.0/en/commit.html
[... connect ...]
mysql_query("BEGIN");
$query1 = mysql_query('UPDATE table SET x=x+2 WHERE x>y');
$query2 = mysql_query('UPDATE table SET y=y+2 WHERE y>y');
if($query1 && $query2) {
mysql_query("COMMIT");
echo 'Save Done. All UPDATES done.';
} else {
mysql_query("ROLLBACK");
echo 'Error Save. All UPDATES reverted, and not done.';
}
There are various levels of transaction, but basically as per ACID properties you should expect that within a given transaction, all reads and updates are performed consistently meaning it will be kept in a valid state, but more importantly a transaction is isolated in that work being done in another transaction (thread) will not interfere with your transaction (your grouping of select & update SQL statements): this allows you to take a broad assumption that you are the only thread of execution within the system allowing you to commit that group of work (atomically) or roll it all back.
Each database may handle the semantics differently (some may lock rows or columns, some may re-order, some may serialize) but that's the beauty of a declarative database interface: you worry about the work you want to get done.
As stated, on MySQL InnoDB is transactional and will support what is mentioned above so ensure your tables are organized with InnoDB, other non-transactional engines (e.g. MyISAM) are not transactional and thus will force you to manage those transactional semantics (locking) manually.
One approach would be to lock the entire table:
LOCK TABLE `table` WRITE;
SELECT z FROM `table` WHERE id='$id';
UPDATE `table` SET x=x+2 WHERE x>z;
UPDATE `table` SET y=y+2 WHERE y>z;
UNLOCK TABLES;
This will prevent other sessions from writing, and reading, from the table table during the SELECTs and UPDATEs.
Whether this is an appropriate solution does depend on how appropriate it is for sessions to wait to read or write from the table.

How to run multiple sql queries using php without giving load on mysql server?

I have a script that reads an excel sheet containing list of products. These are almost 10000 products. The script reads these products & compares them with the products inside mysql database, & checks
if the product is not available, then ADD IT (so I have put insert query for that)
if the product is already available, then UPDATE IT (so I have put update query for that)
Now the problem is, it creates a very heavy load on mysql server & it shows a message as "mysql server gone away..".
I want to know is there a better method to do this excel sheet work without making load on mysql server?
I am not sure if this is the case, but judging from your post, I assume it could be the case that for every check you initilize a new connection to the MySQL server. If that indeed is the case you can simply connect once before you do this check, and run all future queries trought this connection.
Next to that a good optimization option would be to introduce indexes in MySQL that would significantly speed up product search, introduce index for those product table columns, that you reference most in your php search function.
Next to that you could increase MySQL buffer size to something above 256 MB in order to cache most of the results, and also use InnoDB so you do not need to lock whole table every time you do the check, and also the input function.
I'm not sure why PHP has come into the mix. Excel can connect directly to a MySql database and you should be able to do a WHERE NOT IN query to add items and a UPDATE statements of ons that have changed Using excel VBA.
http://helpdeskgeek.com/office-tips/excel-to-mysql/
You could try and condense your code somewhat (you might have already done this though) but if you think it can be whittled down more, post it and we can have a look.
Cache data you know exists already, so if a products variables don't change regularly you might not need to check them so often. You can cache the data for quick retrieval/changes later (see Memcached, other caching alternatives are available). You could end up reducing your work load dramatically.
Have you seperated your mysql server? Try running the product checks on a different sub-system, and merge the databases to your main, hourly or daily or whatever.
Ok, here is quick thought
Instead of running the query, after every check, where its present or not, add on to your sql as long as you reach the end and then finally execute it.
Example
$query = ""; //creat a query container
if($present) {
$query .= "UPDATE ....;"; //Remember the delimeter ";" symbol
} else {
$query .= "INSERT ....;";
}
//Now, finally run it
$result = mysql_query($query);
Now, you make one query at the last part.
Update: Approach this the another way
Use the query to handle it.
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
UPDATE table SET c=c+1 WHERE a=1;
Reference

MYSQL table locking with PHP

I have mysql table fg_stock. Most of the time concurrent access is happening in this table. I used this code but it doesn't work:
<?php
mysql_query("LOCK TABLES fg_stock READ");
$select=mysql_query("SELECT stock FROM fg_stock WHERE Item='$item'");
while($res=mysql_fetch_array($select))
{
$stock=$res['stock'];
$close_stock=$stock+$qty_in;
$update=mysql_query("UPDATE fg_stock SET stock='$close_stock' WHERE Item='$item' LIMIT 1");
}
mysql_query("UNLOCK TABLES");
?>
Is this okay?
"Most of the time concurrent access is happening in this table"
So why would you want to lock the ENTIRE table when it's clear you are attempting to access a specific row from the table (WHERE Item='$item')? Chances are you are running a MyISAM storage engine for the table in question, you should look into using the InnoDB engine instead, as one of it's strong points is that it supports row level locking so you don't need to lock the entire table.
Why do you need to lock your table anyway?????
mysql_query("UPDATE fg_stock SET stock=stock+$qty_in WHERE Item='$item'");
That's it! No need in locking the table and no need in unnecessary loop with set of queries. Just try to avoid SQL Injection by using intval php function on $qty_in (if it is an integer, of course), for example.
And, probably, time concurrent access is only happens due to non-optimized work with database, with the excessive number of queries.
ps: moreover, your example does not make any sense as mysql could update the same record all the time in the loop. You did not tell MySQL which record exactly do you want to update. Only told to update one record with Item='$item'. At the next iteration the SAME record could be updated again as MySQL does not know about the difference between already updated records and those that it did not touched yet.
http://dev.mysql.com/doc/refman/5.0/en/internal-locking.html
mysql> LOCK TABLES real_table WRITE, temp_table WRITE;
mysql> INSERT INTO real_table SELECT * FROM temp_table;
mysql> DELETE FROM temp_table;
mysql> UNLOCK TABLES;
So your syntax is correct.
Also from another question:
Troubleshooting: You can test for table lock success by trying to work
with another table that is not locked. If you obtained the lock,
trying to write to a table that was not included in the lock statement
should generate an error.
You may want to consider an alternative solution. Instead of locking,
perform an update that includes the changed elements as part of the
where clause. If the data that you are changing has changed since you
read it, the update will "fail" and return zero rows modified. This
eliminates the table lock, and all the messy horrors that may come
with it, including deadlocks.
PHP, mysqli, and table locks?

How to implement database locking across several function in a code igniter model?

I'm creating a system that involves the reservation of tickets by many users within a short period of time with only a certain number of reservations possible in total. Say 600 tickets available, potentially all being reserved in 3 hour period or less.
Ideally I want to ensure the limit of reservations is not reached so before creating a reservation I am checking whether it's possible to make the reservation against the number of tickets available. Crucially I need to make sure no updates take places between that check and assigning the tickets to a user, to be sure the ticket limit won't be exceeded.
I'm trying to use mysql table write locks to achieve this however am running into problems implementing this within the codeigniter framework. Within the model handling this I've created several functions, one for creating the reservation and others for counting numbers of different types of tickets. The problem is that they don't seem to be sharing the same database sessions as they ticket counting functions are locking up.
The order of execution is
run $this->model_name->create_reservation in controller
run lock query in model_name->create_reservation
call counting method in model_name->create_reservation
counting function (which is a method in the model_name class) locks up, presumably because using different database session?
The database library is loaded in the model __construct method with $this->load->database();
Any ideas?
In mysql, you run these commands on your DB handle before running your queries the tables will auto lock :
begin work;
You then run your queries or have code igniter run your various selects and updates using that db handle.
Then you either
commit;
or
rollback;
Any rows you select from will be locked and can't be read by other processes. If you specifically want the rows to still be readable, you can do:
Select ... IN SHARE MODE
From Mysql docs:
http://dev.mysql.com/doc/refman/5.5/en/select.html
If you use FOR UPDATE with a storage engine that uses page or row locks, rows examined by the query are write-locked until the end of the current transaction. Using LOCK IN SHARE MODE sets a shared lock that permits other transactions to read the examined rows but not to update or delete them. See Section 13.3.9.3, “SELECT ... FOR UPDATE and SELECT ... LOCK IN SHARE MODE Locking Reads”.
Another person said this in comments already, but from the CI docs:
$this->db->trans_start();
$this->db->query('AN SQL QUERY...');
$this->db->query('ANOTHER QUERY...');
$this->db->query('AND YET ANOTHER QUERY...');
$this->db->trans_complete();
trans_start and trans_complete will run those queries for you on your handle...
there is probably a trans_rollback too...

Categories