问题
Suppose we have a table with an auto-increment primary key. I want to load all IDs greater than the last ID I have seen.
SELECT id
FROM mytable
WHERE id > 10;
With the naive approach, I risk skipping IDs:
- Transaction 1 claims ID 11.
- Transaction 2 claims ID 12.
- Transaction 2 commits.
- I read all IDs >10. I see 12, and next time I will read all IDs >12. I have skipped 11.
- Transaction 1 commits. For all intents and purposes, ID 11 now exists.
As a solution, I propose to do a double check to ensure that no intermediate IDs are about to be committed:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT COUNT(*)
FROM mytable
WHERE id > 10
AND id <= 12; -- Where 12 was the max seen in the first query
If the count is greater than the number of IDs seen in the first query, then it is definitely possible that additional IDs will be committed in the gaps in that sequence.
The question is: does the reverse hold true? If the count is equal to (or less than) the number of IDs seen in the first query, is it guaranteed that there will be no values in between? Or am I missing some possible scenario where the IDs are being claimed, yet the READ UNCOMMITTED
query does not see them yet?
For this question, please disregard:
- Manual ID insertions.
- Rewinding the auto-increment counter.
回答1:
Mysql locks the table during auto_increment.
See
https://dev.mysql.com/doc/refman/5.7/en/innodb-auto-increment-handling.html
So that normally that problem doesn't occur, if there is no bug in the Version.
The lock works like a semaphore/critical Section.
回答2:
I've been developing with SQL databases since 1992, and I have never seen an occasion where using READ UNCOMMITTED was the right solution to any problem.
I guess you are using MySQL as a kind of queue. That is, you rely on the auto-increment ID as the head of the queue.
I don't think you can do this in the way you describe, because of the fact that the order in which transactions generate their auto-inc ID's is not the same order as they commit.
I suggest you need to have another column called processed
or something like that. Then you can query for records you have not yet processed:
SELECT id FROM mytable WHERE processed = false ORDER BY id
This way, the query will always return any records you have not seen yet. If ID 11 is committed after you have already seen ID 12, it will show up the next time you run this query.
Once you have done whatever you are going to do with a record, then:
UPDATE mytable SET processed = true WHERE id = ?
An even better solution, without the need to have a processed
column, is to use a message queue to complement the SQL database.
When a client adds a record, they should also post the ID of the record they just inserted into the message queue. It's important that this client post to the message queue after they commit the record, or else a consumer of the message queue could get notified of an ID they can't see yet.
来源:https://stackoverflow.com/questions/59106154/can-readuncommitted-know-all-lower-auto-increment-ids-that-will-exist