简体   繁体   中英

PHP PDO MySQL transactions not executed

I'm struggling with implementing PHP and MySQL Transactions. The script receives a SQL-statement along with some bindparameters through a blocking redisqueue. Everything is passed to a function 'do_transaction' which keeps track of the number of statements received.

I've debugged the PDO statement (after it has been processed) with PdoDebugger and the output is correct:

UPDATE bla SET processed = 1, severity_ou1 = 'low', 
severity_ou2 = 'low', severity_ou3 = 'low', severity_ou4 = 'low',
severity_ou5 = 'low', saved = '1', hname = '1', sname = '1', if = '1',
v = '1', translated = 'blablabla.', filtered = 1, repeated = '1',
excessed = '1', eventfilterid = '212', building = '1', floor = '1'
WHERE id = '121614624'
global $batchcount;
$batchcount = 1;

while(true){
    $redis = new Redis();
    $redis->connect('xxx', xxx);
    $sqlbatch = $redis->blpop('xxx:xxx:sqlfiltermatch', 0);

    // blpop returns array: 0 has key, 1 has data.
    if(is_array($sqlbatch)){
        if(isJson($sqlbatch[1])){
            $batchstatements = array();
            $batchstatements[] = json_decode($sqlbatch[1], true);
            // Get statement and bindparams.
            $sqlstatement = $batchstatements[0]['statement'];
            $bindparams = $batchstatements[0]['bindparams'];
            // Replace empty bindparams.
            foreach($bindparams as $column => $value){
                if(is_null($value)){ $bindparams[$column] = '1'; }
                if(empty($value)){ $bindparams[$column] = '1'; }
            }
        }
        $batchcount++;
        do_transaction($sqlstatement, $bindparams, $batchcount);
    }
}

function do_transaction($sqlstatement, $bindparams){
    global $batchcount;
    if($batchcount >= 4){
       try {
            // Setup DB
            $db = new PDO('mysql:host=xxx;dbname=xxx;charset=utf8', 'xxx', 'xxx', array(PDO::ATTR_PERSISTENT => true, PDO::ATTR_AUTOCOMMIT => FALSE, PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING));
            echo $db->getAttribute(PDO::ATTR_AUTOCOMMIT)."\n\n"; 
            $db->beginTransaction();
            $stmt = $db->prepare($sqlstatement);

            // Setup bindparams.
            foreach($bindparams as $column => $value){
                $stmt->bindParam(":$column", $value);
            }
            $stmt->execute() or die(print_r($stmt->errorInfo(), true));
            echo PdoDebugger::show($sqlstatement, $bindparams)."\n";
            $db->commit();

        } catch(PDOExecption $e){
            //$db->rollback();
            print_r("ERROR"); exit;              
        }
        $batchcount = 0;
    }

    $batchcount++;
}

I've made sure that AUTOCOMMIT = FALSE. Where in "do_transaction" does it go wrong?

There is no point in using transactions this way. So, just leave them alone.

function do_query($db, $sqlstatement, $bindparams){
    $stmt = $db->prepare($sqlstatement);
    $stmt->execute($bindparams);
    return $stmt;
}

is all the code you actually need.

Use it this way

$db = new PDO('mysql:host=xxx;dbname=xxx;charset=utf8', 'xxx', 'xxx',
    array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
);
while(true){
   // whatever redis code goes here
   do_query($db, $sqlstatement, $bindparams);
}

Nota bene: If you want to make your inserts faster, you should ask a question titled "How to make inserts faster", no "My transactions do not work".

But your idea of inserting transactions is wrong too.
A single transaction (in terms of the business logic) have to be written to database as soon as possible, without interfering with other transactions. Means you should never couple different business logic transactions within single database transacion. Because error in single business logic transaction will ruin whole batch. So - just write them separately.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM