Mysql read lock SELECT FOR UPDATE

前端 未结 1 624
小鲜肉
小鲜肉 2021-01-15 08:17

EDIT

I use node.js felixge-mysql and have a pool of mysql connections.

ORIGINAL

I have a mysql db

1条回答
  •  梦毁少年i
    2021-01-15 08:57

    It's not entirely clear what you are trying to accomplish. But as best I understand what you are asking, there isn't any native MySQL "lock" mechanism that is going to do what you need. (You want a session to be able to "lock" a row to prevent it from being "read" (or modified) by another session.

    To accomplish what you are trying to do, that sounds like an application issue, not a database integrity issue.

    The approach I would use to solve that would be to add two columns to the table:

    locked_by   - uniquely identify the session holding the row lock
    locked_at   - the date/time the row lock was placed 
    

    For a session attempting to obtain the lock on row, I'd check whether or not the row was already locked by a different session, and if not, mark the row as locked by this session:

    UPDATE mytable 
       SET locked_by = 'me'
         , locked_at = NOW()
     WHERE unique_row_identifer = someval
       AND locked_by IS NULL;
    

    If the return from the update is "zero rows updated", you know you didn't obtain a lock. If the return is non-zero, you know you obtained a lock (on at least one row).

    To check whether my session was already holding a lock on the row:

    SELECT 1
      FROM mytable t
     WHERE t.unique_row_identifier = someval
       AND locked_by = 'me';
    

    Once I knew I had an "lock" on the row, then I could retrieve it with a simple SELECT

    SELECT ... WHERE unique_row_identifier = someval`
    

    To release the lock, the session would set the locked_by and locked_at columns back to NULL.

    A "read only" session could avoid reading a locked row by checking the values in the locked_by column:

    SELECT t.*
      FROM mytable t
     WHERE t.unique_row_identifier = someval
       AND t.locked_by IS NULL
    

    The row would only be returned if it wasn't locked.

    Note that I'd do the locking and checking in a single statement, to avoid a race condition with simultaneous situations. If I ran a SELECT to do a check, followed by an UPDATE, there's a potential for another session to slip in between those two separate statements... it would be hard to really cause that to happen, without adding a significant delay. But if we're going to bother with locking rows, we'd best do it right.

    Note that the value stored in the locked_at column comes into play when we want to check for locks that have been held for a long time. Maybe a session took some locks, and that session has gone away, and those locks will never be released. A separate maintenance task could be scheduled to look through the table for really old locked_at values.

    Alternatively, you could use the locked_at to do a more sophisticated look for a lock, and consider really old locks to be expired.

     WHERE ( locked_at IS NULL OR locked_at < (NOW() + INTERVAL 24 HOUR) )
    

    ===

    NOTE:

    I've never used that approach in a production system before. The problem my team is typically concerned with is the "last one in wins" scenario, where an update will potentially overwrite a change that another session has recently made. But the problem we are solving seems to be quite different from what you are trying to accomplish.

    To resolve the "last one in wins" problem, we add a single "version" column (simple integer) to the table. When we retrieve a row, we retrieve the current value of the version column. When the session later wants to update the row, it verifies that no other update has been made to the row, by comparing the previously retrieved version value with the current value in the table. If the version numbers match, we allow the row to be updated, and increment the version number by one. (We do this all within a single UPDATE statement, so the operation is atomic, to avoid a race condition where two simultaneous sessions don't both do updates. We use this pattern because we don't really want to have a row locked by a session, and having the lock held forever. We're just preventing simultaneous updates from overwriting one another, which again, is different from it sounds like you are trying to accomplish.

    0 讨论(0)
提交回复
热议问题