在MySQL中递增一个字段是原子的吗?

时间:2010-12-05 12:18:43

标签: mysql thread-safety atomic increment

我正在建立一个网站,我希望在标准的MyISAM表中增加一个计数器。

简化示例:

UPDATE votes SET num = num + 1;

如果多个连接正在执行相同的查询,这会导致问题,还是MySQL会处理它并锁定表或某些内容以确保没有冲突?

6 个答案:

答案 0 :(得分:23)

写入是原子的,但增量也需要读取。所以问题是:你确定读取是安全的吗?换句话说,你确定另一个做增量的线程最终不会以相同的值递增吗?我有疑问。这样做的100%正确方法是。

-- begin transaction here

select counter from myCounters where counter_id = 1 FOR UPDATE;

-- now the row is locked and nobody can read or modify its values

update myCounters set counter = ? where id = 1;

-- set ? to counter + 1 programmatically

commit; -- and unlock...

答案 1 :(得分:14)

MyISAM表使用表级锁定。这意味着在执行更新查询期间将锁定整个表。因此,简化用例的答案是:是的,这是线程安全的。但是,如果您使用其他存储引擎或者您的更新包含多个表,则可能不是这种情况。

以下是MySQL手册的引用,以便更清晰:

  

表锁定可启用多个会话   同时从表中读取,   但如果一个会话想要写一个   表,它必须先获得独家   访问。在更新期间,所有其他   想要访问它的会话   特定的表必须等到   更新完成。

如果适合您的设计,您还可以考虑使用自动增量列,事务或外部同步。

干杯!

答案 2 :(得分:5)

是的,执行更新查询时,表(或InnoDB格式数据库中的行)会自动锁定。

答案 3 :(得分:4)

这种形式的UPDATE是原子的。通过使用UPDATE的交易,可以使其他形式的SELECT ... FOR UPDATE成为原子。

答案 4 :(得分:2)

有同样的问题,虽然查询更复杂:

UPDATE CLB_SYNC_T SET PENDING_MESSAGES = PENDING_MESSAGES + ? WHERE USER_ID = ?

使用 MyISAM 作为默认引擎无效,因此我回退到 SELECT FOR UPDATE 使用。

SELECT FOR UPDATE 性能提高了~10倍,因为MySQL没有锁定整个表,所以要进行行更新。

答案 5 :(得分:0)

使用InnoDB时的另一种方法是在多列上使用唯一索引,如下所示:

表'会话' {   unique_key(browser_session_id,profile_id)//确保每个会话插入一个条目一次 }

从会话中选择计数(browser_session_id)

将保证唯一会话的结果,因为不允许每个用户使用多个会话。

<强>结论

  • 优势

    每个插入都需要预先选择。

  • 缺点

    并不适合所有情况。

    可能会降低写入性能,并需要额外的管理