WL#8919: InnoDB: Implement NOWAIT and SKIP LOCKED
Affects: Server-8.0
—
Status: Complete
When we do locking reads, either SELECT ... LOCK IN SHARE MODE or SELECT ... FOR UPDATE, if a row has been locked by another transaction, we will wait until the transaction ends(see http://dev.mysql.com/doc/refman/5.7/en/innodb-locking- reads.html). What if we just want to know whether a row is locked, or just skip the locked without waiting? Let's take seats in the cinema for example: there is table named seats with two column(no, state), state(0: free, 1: sold). 1. I like seat 10 because its good view, just want to reserve the seat without waiting. 2. I need one free seat, so just reserve a free seat without waiting. This worklog will solve the above issues. #1: SELECT * FROM seats WHERE no = 10 and state = 0 FOR UPDATE NO_WAIT; if the query returns error(e.g. ER_NO_WAIT_LOCK), the seat is reserved by someone; if the query returns empty set, the seat is sold already. #2: SELECT * FROM seats WHERE no = 10 AND state = 0 LIMIT 1 FOR UPDATE SKIP LOCKED; if the query returns empty set, all seats are sold or reserved. The exact SQL syntax refers to WL#3597:Implement NOWAIT and SKIP LOCKED. Note: Skipping locked rows provides an inconsistent view of the data, so this is not suitable for general purpose work, but can be used to avoid lock contention with multiple consumers accessing a queue-like table.
F-1: Query on a table with NO_WAIT must return directly, if it can't lock a row immediately, without waiting for the row lock to be released. F-2: Query on a table with SKIP LOCKED must skip a row, if it can't lock the row immediately, without waiting for the row lock to be released. F-3: NO_WAIT/SKIP LOCKED shall apply only to the row level lock(s). Non-Functional requirements: NF-1: Implicit requirements: must work on all platforms, do not break replication, backup, partitioning, FK, or any other exiting features.
1. NO_WAIT/SKIP LOCKED process steps: a. Set select mode in handler interfaces:SELECT_ORDINARY, SELECT_SKIP_LOCKED, and SELECT_NO_WAIT. b. When acquiring a lock holding by another transaction, return error DB_SKIP_LOCKED or DB_NO_WAIT before adding to wait queue. c. In row mvcc search, if DB_SKIP_LOCKED, go to next record; if DB_NO_WAIT, stop search and return the error. 2. For spatial index, we finally goes to record lock. Don't need to handle predicated lock because we always lock successfully in SELECT. In rtree search we always buffer all matched records in a page, and lock them, no matter how may records we need(such as LIMIT 2). Suppose there are 10 matched records on a page, and we only need 2 records in select-1, but we don't see the remaining 8 records on the page in select-2 with SKIP LOCKED. 3. Ignore NO_WAIT/SKIP_LOCKED for high priority transaction.
1. Define select mode in storage/innobase/include/lock0types.h enum select_mode { SELECT_ORDINARY = 0, /* default behaviour */ SELECT_SKIP_LOCKED, /* skip the row if row is locked */ SELECT_NO_WAIT /* return immediately if row is locked */ }; 2. Set select mode in storage/innobase/handler/ha_innodb.cc ha_innobase::store_lock(): /* Set select mode for SKIP LOCKED / NO_WAIT */ switch (lock_type) { case TL_READ_SHARED_SKIP_LOCKED: case TL_WRITE_SKIP_LOCKED: m_prebuilt->select_mode = SELECT_SKIP_LOCKED; break; case TL_READ_SHARED_NO_WAIT: case TL_WRITE_NO_WAIT: m_prebuilt->select_mode = SELECT_NO_WAIT; break; default: m_prebuilt->select_mode = SELECT_ORDINARY; break; } 3. Return corresponding errors when locking rows in storage/innobase/lock/lock0lock.cc lock_rec_lock_slow(): if (wait_for != NULL) { switch (sel_mode) { case SELECT_SKIP_LOCKED: err = DB_SKIP_LOCKED; break; case SELECT_NO_WAIT: err = DB_LOCK_NOWAIT; break; Note: we don't try to jump the wait queue for high priority transactions. 4. Handle DB_SKIP_LOCKED in storage/innobase/row/row0sel.cc row_search_mvcc(): case DB_SKIP_LOCKED: goto next_rec; 5. Handle DB_LOCK_NOWAIT a. Rollback the latest statement in row_mysql_handle_errors() b. Return ER_LOCK_NOWAIT to mysql in convert_error_code_to_mysql() 6. Main lock functions with select mode: sel_set_rec_lock() sel_set_rtr_rec_lock() lock_clust_rec_read_check_and_lock() lock_sec_rec_read_check_and_lock()
Copyright (c) 2000, 2024, Oracle Corporation and/or its affiliates. All rights reserved.