我需要检查一行是否存在,如果存在,则更新某列,如果不存在,则创建一条新记录。
但是我使用的查询不是线程安全的,因为我在那个表上没有唯一的索引,因为我可以根据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
值,然后它们将继续插入新行,最终得到两条记录
2条答案
按热度按时间kx5bkwkv1#
我不知道如果没有unique约束是否可行,但有一种方法可以做到这一点,即创建行的浅副本。
在您的例子中,无论数据库中是否存在user_id和注解类型,您都将直接插入,并向行添加时间戳。当你想读取行时,执行SELECT ... ORDER BY timestamp DESC LIMIT 1.使用cronjob或类似的东西来每隔一段时间进行数据库清理。
myzjeezk2#
使用DB::transaction来保证一个块被排他地执行
事务将锁定您的表,因此要注意性能问题。你也可以检查laravel的updateOrInsert或者updateOrCreate方法来合并条件和查询