Without using explicit transactions, is:
update tokens set tokens = tokens + 1
guaranteed to be atomic in InnoDB?
My understanding is that in autocommit mode (i.e. "without explicit transactions") a single statement is a single transaction. As a transaction is per definition atomic, so your single statement is atomic as well.
However, when it comes to evaluating constraints, this is not done on the statement (i.e. transaction) level, but row by row while the statement is processed.
If you have a unique constraint (index) on the tokens
column, that update will most probably fail because of that. The same is true for foreign keys referencing the same table.
I think it isn't. Don't have my MySQL isolation levels/cheat sheet available, but I think it would be atomic on a per-row level (UPDATE would use range-locks IIRC), but not on a per-table level.
Now, a more interesting question than whether UPDATE is atomic, is when the non-atomicity of UPDATE is observable. The answer is that single statements in READ UNCOMMITTED isolation level can observe the non-atomicity of UPDATE, and a series of related statements in a single transaction in READ COMMITTED isolation level can observe different updates. REPEATABLE READ and SERIALIZABLE see UPDATEs as though they were atomic and consistent.
Imagine two sessions. Session A has isolation level SERIALIZABLE and does: UPDATE tokens SET tokens = tokens + 1
in a table with 1000 rows.
SELECT sum(tokens) FROM tokens
→ This select can see a partial update (i.e: some rows updated and other rows not updated).SELECT sum(tokens) FROM tokens
→ This select cannot see a partial update, so it sees the UPDATE as though it was atomic.SELECT sum(tokens) FROM tokens WHERE id BETWEEN 1 AND 100; SELECT sum(tokens) FROM tokens WHERE id BETWEEN 501 AND 600
and then some program logic adds those two values → These SELECTs can see different MVCC snapshots → different updates, SELECT sum(tokens) FROM tokens WHERE id BETWEEN 1 AND 100; SELECT sum(tokens) FROM tokens WHERE id BETWEEN 501 AND 600
and then some program logic adds those two values → These SELECTs cannot see different MVCC snapshots → they see the same update,