I'm having trouble getting a table to lock in MySQL. I've tried testing for concurrent requests by running two scripts at the same time on different browsers.
Script 1:
mysql_query("lock tables id_numbers write");
$sql = "select number from id_numbers";
$result = mysql_query($sql);
if ($record = mysql_fetch_array($result))
{
$id = $record['number'];
}
sleep(30);
$id++;
$sql = "update id_numbers set number = '$id'";
$result = mysql_query($sql);
mysql_query("unlock tables");
Script 2:
$sql = "select number from id_numbers";
$result = mysql_query($sql);
if ($record = mysql_fetch_array($result))
{
$id = $record['number'];
}
echo $id;
I start Script 1 first, then start Script 2. Theoretically, Script 2 should wait 30+ seconds until Script 1 unlocks the table, and then output the updated ID. But it immediately outputs the original ID, so the lock is obviously not taking effect. What could be going wrong?
BTW, I know I shouldn't still be using mysql_*, but I'm stuck with it for now.
EDIT: I've discovered that the lock does happen on the live site, but not on my dev site. They are both on shared hosts, so I'm assuming there's some setting that's different between the two. Any idea what that could be?
Using the code you have here, I get exactly the behavior you expect: script 2 will block until script 1 issues the unlock tables.
Your problem must lie elsewhere. Things to check:
Does the table actually exist on the database? (Check your mysql_query() return values and use mysql_error() if you get any FALSE returns.)
Are both scripts connecting to the same database?
Are you sure the table is not a temporary (thus connection-local) table?
Could script 1's mysql connection (or the script itself) be timing out during the sleep(30), thus releasing the lock earlier than you expect?
Related
I have an script of a queue like situation which each user can post a request which is added to the queue and others can accept these requests by the order of that queue but each request should only be accepted by one user .
but if I implement it with two queries one for the select other for the delete
something Like this
<?php
$sql = "SELECT id,CreationDate FROM `RandomQueue` order by CreationDate limit 1 ;";
$result = $conn->query($sql);
if ($result->num_rows == 1) {
$row = $result->fetch_assoc();
echo "this request is accepted".$row['id'];
$sqlDel = "DELETE FROM `RandomQueue` WHERE id = {$row['id']}";
$conn->query($sql);
}
?>
it would not work because many problems may occur like two user accepting one request and so on..(mutex problem) is it possible to achieve this by one sql query(to make it atomic)? and if not then any suggestion on how to implementing this logic would be nice ,thanks a lot;
Atomicity is one of the database transaction properties (ACID), so you need make sure your database engine support transaction.
In MySQL, MyISAM doesn't support transaction and you must use InnoDB.
If you are using InnoDB, there are two ways to start database transaction for MySQL in PHP
/* Begin a transaction, turning off autocommit */
$dbh->beginTransaction();
// your business logic goes here
$dbh->commit();
or
$mysqli->begin_transaction(MYSQLI_TRANS_START_READ_ONLY);
// your business logic goes here
$mysqli->commit();
I have a quite heavy SQL search query that takes a few minutes to complete, called from a PHP script that's called by an ajax request.
Problem is, users often click the search button many times from impatience, and each time it creates a new ajax request, and a new PHP script execution. But the old ones continue executing, even though the connection has been dropped. This causes the SQL server load to constantly be at 100% CPU usage.
So I tested my theory that script execution continues even after the browser tab is closed. I used 2 types of queries, an ad hoc query and a stored procedure execution, both methods do the same thing, inserting the numbers 1-9 into a table, with a 2 second delay between each number.
Ad hoc query:
for($i = 1; $i < 10; $i++){
$sql = "INSERT INTO t (i) VALUES(?)";
$res = pdoQuery($sql, array($i));
if($res === false){
echo $pdo->getErrorMessage();
http_response_code(500);
exit();
}
sleep(2);
}
SP call:
$sql = "EXEC sp_Slow";
$res = pdoQuery($sql);
if($res === false){
echo $pdo->getErrorMessage();
http_response_code(500);
exit();
}
How I tested: using buttons that trigger ajax calls to each script, I tested them, by clicking the button and immediately closing the tab. And then monitoring the data in the table. And just as I suspected, new data gets inserted every 2 seconds. (This also happens if I directly open the script in the browser and closing the tab, instead of requesting it through ajax)
I need a way to completely kill both PHP and SQL execution whenever the user disconnects, transactions are not important because it's just a select operation.
You can change this behaviour using php.ini directive or at runtime with ignore_user_abort() function.
Here's what I did, from the comment by #Robbie Toyota, thanks!
if(!empty($_SESSION['SearchSPID'])){
$sql = "KILL " . $_SESSION['SearchSPID'];
$res = pdoQuery($sql);
if($res === false){
exit('Query error: failed to kill existing spid:' . $_SESSION['SearchSPID']);
}
$_SESSION['SearchSPID'] = null;
}
$sql = "SELECT ##spid AS SPID";
$res = pdoQuery($sql);
$spid = $res->row_assoc()['SPID'];
$_SESSION['SearchSPID'] = $spid;
// do long query here
$_SESSION['SearchSPID'] = null;
Of course using this method you have to be careful about session file locking, which if happens will make this whole thing pointless, because then the requests will be sequential and not parallel
This is a problem I'm having for quite some time now.
I work for a banking institute: our service provider lets us access our data via ODBC through a proprietary DB engine.
Since I need almost a hundred tables for our internal procedures and whatnot, I set up some "replication" scripts, put em in a cron and basically reloading from scratch the tables I need every morning.
When the number of records is small (approx. 50.000 records and 100 columns or so) everything goes smooth, but whenever I get a medium-big table (approx. 700.000 records), more often than not the script restarts itself (I look at my MySQL tables while the import scripts are running and I see them going 400k, 500k... and back from 1).
This is an example of one of my import scripts:
<?php
ini_set('max_execution_time', '0');
$connect = odbc_connect('XXXXX', '', '') or die ('0');
$empty_query = "TRUNCATE TABLE SADAS.".$NOME_SCRIPT;
$SQL->query($empty_query);
$select_query = " SELECT...
FROM ...";
$result = odbc_exec($connect, $select_query);
while($dati = odbc_fetch_object($result)) {
$insert_query = " INSERT INTO ...
VALUES ...";
$SQL->Query($insert_query);
}
// Close ODBC
odbc_close($connect);
?>
Any ideas?
I have this problem.. i am trying to use the lock record for the update. I am using php, and i use oracle as the database. i didnt understand fully but i have managed to copy the previous developer code and make it working.
It is using the odbc_exec(), odbc_fetch_row(). I think that this is suppose to be use for microsoft access data base. But i don't know. And i am also using jquery to fetch this php file(using jquery post) and append to the current page.
But now my senior request me to make sure that if a person is editing one record. then the other cant edit it anymore. They said that something about not closing the connection. can php not close connection to database after 1 page is loaded (when it is using jquery to get the page like mine does)? from what i know, if a page is loaded. the connection to the database is closed.
I want to try this code but i am afraid if i am wrong and the connection would hang.
Now my sql is like this for select all company.
$sqlStr ="select * from company";
$rs = odbc_exec($GblConnOra , $sqlStr);
while (odbc_fetch_row($rs)){
$company_name=odbc_result($rs,"company_name");
$company_id=odbc_result($rs,"company_id");
}
And for select the company to edit
$sqlStr ="select * from company where company_id='34'";
$rs = odbc_exec($GblConnOra , $sqlStr);
while (odbc_fetch_row($rs)){
$company_name=odbc_result($rs,"company_name");
}
The updating
$sqlStr ="update compay set company_name=? where company_id=?";
$stmt = odbc_prepare($GblConnOra , $sqlStr);
$res = odbc_execute($stmt, array($company_name, $company_id));
i think the sql will be like this. to lock the record.
$sqlStr ="select * from company for update skip locked";
$rs = odbc_exec($GblConnOra , $sqlStr);
while (odbc_fetch_row($rs)){
$company_name=odbc_result($rs,"company_name");
$company_id=odbc_result($rs,"company_id");
}
can i do this?
what would my sql be for selecting a row to edit?
what would my sql be for updating the row?
And from my understanding, this lock would be release after an update commit or rollback is call.
I'm trying to increment +1 impression every time an ad is displayed on my site, however the variable increments +2 to +3 arbitrarily. I've removed everything that's working correctly and I made a page with only this code in it:
<?php
require "connect_to_mydb.php";
echo 'Hello***** '.$testVariable=$testVariable+1;
mysql_query("UPDATE `imageAds` SET `test`=`test`+1 WHERE `id`='1'");
?>
Every time the page is refreshed the, test increments arbitrarily either +2 or +3 and my page displays Hello***** 1 (Just to show its not looping). Access is restricted to this page so it's not other users refreshing the page.
Also, id and test are int(11) in the DB.
My DB required connection has nothing in it that would interfere.
Edit
Here is an updated code:
<?php
require "connect_to_mydb.php";
mysql_query("UPDATE `imageAds` SET `test`=`test`+1 WHERE `id`='1'");
$sql = mysql_query("SELECT * FROM imageAds WHERE id='1' LIMIT 1");
$check = mysql_num_rows($sql);
if($check > 0){
$row = mysql_fetch_array($sql);
echo $row['test'];
}
?>
Increments by +2 everytime
Edit
This is whats in connect_to_mydb.php
<?php
$db_host = "*************************";
$db_username = "*********";
$db_pass = "**********";
$db_name = "**************";
mysql_connect("$db_host","$db_username","$db_pass") or die ("could not connect to mysql");
mysql_select_db("$db_name") or die ("no database");
?>
Either there's a bug in MySQL's implementation of UPDATE, or you're doing something wrong in some code you haven't posted.
Hint: It's very unlikely to be a bug in MySQL. Other people would have noticed it.
From what you've shown, it looks like your page is being loaded multiple times.
This attempt to prove that the code is only being called once doesn't prove anything:
echo 'Hello***** '.$testVariable=$testVariable+1;
This will always print the same thing (Hello***** 1) even if you open this page multiple times because the value of $testVariable is not preserved across seperate requests.
This +2/+3 error is occurring only with Chrome and my Mobile Android browser and the code is solid. I looked to see if there is any issue with Chrome sending more than one http request (thx user1058351) and there is which is documented here:
http://code.google.com/p/chromium/issues/detail?id=39402
So since this way was unreliable I just completed a work around that is solid. Instead of including a PHP file that updates the amount of ad impressions on reload, I now have it so when the page loads, an AJAX request is sent to a separate PHP file which updates the ad stats and returns the appropriate data. The key I think is to send it through the JS code so only one http request can be sent to increment the data.
Thank you to all who responded especially user1058351 and Mark Byers (not a bug in MYSQL but possibly appears to be a bug in Chrome).