laravel 我如何使选择和插入查询线程安全没有唯一的索引?

vdzxcuhz  于 2023-06-24  发布在  其他
关注(0)|答案(2)|浏览(88)

我需要检查一行是否存在,如果存在,则更新某列,如果不存在,则创建一条新记录。
但是我使用的查询不是线程安全的,因为我在那个表上没有唯一的索引,因为我可以根据last_update值创建具有相同值的新行:

$row = DB::table('some_table')
    ->where('last_update', '>=', now()->subMinutes(5))
    ->where('user_id', '=', $user_id)
    ->where('comment_type', '=', $comment_type)
    ->first();

if ($row === null) {
     // record not found, create new
     DB::table('some_table')->insert([
        'user_id' => $user_id,        
        'comment_type' => $comment_type,     
        'created_at' => $created_at,     
        'last_update' => $last_update   
    ]);
} else {
     // record found, update existing
     DB::table('some_table')
          ->where('id', '=', $row->id)
          ->update(['last_update' => now()]);     
}

有没有办法让它更安全?
编辑:我所说的安全是指,在上面的代码中,可能会有两个线程几乎同时到达代码,它们都为$row获取null值,然后它们将继续插入新行,最终得到两条记录

kx5bkwkv

kx5bkwkv1#

我不知道如果没有unique约束是否可行,但有一种方法可以做到这一点,即创建行的浅副本。
在您的例子中,无论数据库中是否存在user_id和注解类型,您都将直接插入,并向行添加时间戳。当你想读取行时,执行SELECT ... ORDER BY timestamp DESC LIMIT 1.使用cronjob或类似的东西来每隔一段时间进行数据库清理。

myzjeezk

myzjeezk2#

使用DB::transaction来保证一个块被排他地执行

DB::transaction(function () {

    $row = DB::table('some_table')
    ->where('last_update', '>=', now()->subMinutes(5))
    ->where('user_id', '=', $user_id)
    ->where('comment_type', '=', $comment_type)
    ->first();

   if ($row === null) {
   // record not found, create new
   DB::table('some_table')->insert([
      'user_id' => $user_id,        
      'comment_type' => $user_id,     
      'created_at' => $user_id,     
      'last_update' => $user_id,     
   ]);

   } else {
   // record found, update existing
   DB::table('some_table')
      ->where('id', '=', $row->id)
      ->update(['last_update' => now()]);     
   }

});

事务将锁定您的表,因此要注意性能问题。你也可以检查laravel的updateOrInsert或者updateOrCreate方法来合并条件和查询

相关问题