1、背景需求:
给表里插入数据时,判断user表有无username为‘wecot’的数据,无则插入,有则提示“数据已存在”,目的就是想只插入一条username为‘wecot’的记录,并且保证其唯一性。
2、通常实现方式:
$conn = mysqli_connect('127.0.0.1', 'root', 'root') or die(mysqli_error()); mysqli_select_db($conn, 'wecot'); $res = mysqli_query($conn, 'SELECT count(*) as total FROM user WHERE username = "wecot" '); $row = mysqli_fetch_array($res); if($row['total']>0){ exit('data exist'); } mysqli_query($conn, "insert into user(username) values ('wecot')"); var_dump('error:'.mysqli_errno($conn)); $insert_id = mysqli_insert_id($conn); echo 'insert_id:'.$insert_id.'<br>'; mysqli_free_result($res); mysqli_close($conn);
3、问题所在:
以上做法在一般少量请求情况,程序逻辑不会有问题。但是一旦高并发请求执行的话,程序并没有按预期执行,由于数据库会出现脏读的情况,可能会插入多条username为‘wecot’的记录。
4、解决方案:
利用 Mysql的FOR UPDATE 语句和事务的隔离性。注意的是FOR UPDATE仅适用于InnoDB,且必须在事务(BEGIN/COMMIT)中才能生效。
优化后代码:
$conn = mysqli_connect('127.0.0.1', 'root', 'root') or die(mysqli_error()); mysqli_select_db($conn, 'wecot'); mysqli_query($conn, 'BEGIN'); $res = mysqli_query($conn, 'SELECT count(*) as total FROM user WHERE username = "wecot" FOR UPDATE'); $row = mysqli_fetch_array($res); if($row['total']>0){ exit('data exist'); } mysqli_query($conn, "insert into test(username) values ('wecot')"); var_dump('error:'.mysqli_errno($conn)); $insert_id = mysqli_insert_id($conn); mysqli_query($conn, 'COMMIT'); echo 'insert_id:'.$insert_id.'<br>'; mysqli_free_result($res); mysqli_close($conn);
转载请注明:微刻 blog.wecot.cn » Mysql 高并发加锁处理