我正在建立一个网站,我希望在标准的MyISAM表中增加一个计数器。
简化示例:
UPDATE votes SET num = num + 1;
如果多个连接正在执行相同的查询,这会导致问题,还是MySQL会处理它并锁定表或某些内容以确保没有冲突?
答案 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)
将保证唯一会话的结果,因为不允许每个用户使用多个会话。
<强>结论强>
优势
每个插入都需要预先选择。
缺点
并不适合所有情况。
可能会降低写入性能,并需要额外的管理