1. 程式人生 > 其它 >MySQL Innodb Engine--MVCC程式碼瞎猜

MySQL Innodb Engine--MVCC程式碼瞎猜

MVCC實現

在InnoDB儲存引擎中,每個表都是索引組織表,如果建表時沒有指定主鍵索引,會自動建立一個6位元組的自增列來作為隱藏主鍵。

每條聚簇索引的索引記錄都包含下面兩個隱藏列:

  • 事務ID列,用於標識該版本記錄由那個事務修改產生。
  • 回滾指標列,用於指向“存放上一個版本記錄資料”的Undo Log指標。

PS:對於存放在Undo Log上的“版本記錄”,除包含變化前的使用者資料外,也包含事務ID列和回滾指標列

InnoDB儲存引擎基於事務ID列回滾指標列來實現多版本併發控制(multi version concurrencey control),為判斷事務應讀取記錄的那個版本,會為事務分配一個read view資料結構,存放如下資訊:

  • m_low_limit_id:當前事務能看到的"最大事務ID",超過m_low_limit_id的事務產生的修改對當前事務不可見。
  • m_up_limit_id:當前事務能看到的”最小事務ID“,小於m_up_limit_id的事務產生的修改對當前事務可見。
  • m_creator_trx_id:當前事務的事務ID,當前事務產生的修改對當前事務可見。
  • m_ids:建立read view時活躍事務ID連結串列,該活躍例項連結串列中的事務產生的修改對當前事務不可見(事務沒提交)。

InnoDB儲存引擎的事務系統使用資料結構trx_sys_t來存放一些全域性事務資訊,

 /*!< The smallest number not yet
 assigned as a transaction id or
 transaction number. This is declared
 volatile because it can be accessed
 without holding any mutex during
 AC-NL-RO view creation. */
 volatile trx_id_t max_trx_id;
 
 /*!< Array of Read write transaction IDs
 for MVCC snapshot. A ReadView would take
 a snapshot of these transactions whose
 changes are not visible to it. We should
 remove transactions from the list before
 committing in memory and releasing locks
 to ensure right order of removal and
 consistent snapshot. */
 trx_ids_t rw_trx_ids; 

trx_sys_t->max_trx_id 用來儲存當前未分配的最小事務編號,通過trx_sys_t->max_trx_id能快速獲取到read view的m_low_limit_id初始值。

trx_sys->rw_trx_ids 用來儲存當前活躍的事務連結串列,該全域性事務連線按照事務ID逆序存放,最小的事務ID存放在連結串列尾部,通過trx_sys->rw_trx_ids 來初始化m_up_limit_id和m_ids。

read view結構

private:
  // Disable copying
  ReadView(const ReadView &);
  ReadView &operator=(const ReadView &);

 private:
  /** The read should not see any transaction with trx id >= this
  value. In other words, this is the "high water mark". */
  /* 最高水位 */
  trx_id_t m_low_limit_id;

  /** The read should see all trx ids which are strictly
  smaller (<) than this value.  In other words, this is the
  low water mark". */
  /* 最小水位 */
  trx_id_t m_up_limit_id;

  /** trx id of creating transaction, set to TRX_ID_MAX for free
  views. */
  /* 當前事務id */
  trx_id_t m_creator_trx_id;

  /** Set of RW transactions that was active when this snapshot
  was taken */
  /* 活躍事務id */
  ids_t m_ids;

  /** The view does not need to see the undo logs for transactions
  whose transaction number is strictly smaller (<) than this value:
  they can be removed in purge if not needed by other views */
  /* m_low_limit_no 用於undo log的purge操作 */
  trx_id_t m_low_limit_no;

#ifdef UNIV_DEBUG
  /** The low limit number up to which read views don't need to access
  undo log records for MVCC. This could be higher than m_low_limit_no
  if purge is blocked for GTID persistence. Currently used for debug
  variable INNODB_PURGE_VIEW_TRX_ID_AGE. */
  trx_id_t m_view_low_limit_no;
#endif /* UNIV_DEBUG */

  /** AC-NL-RO transaction view that has been "closed". */
  bool m_closed;

  typedef UT_LIST_NODE_T(ReadView) node_t;

  /** List of read views in trx_sys */
  byte pad1[64 - sizeof(node_t)];
  node_t m_view_list;
};

#endif

初始化read_view

/**
Opens a read view where exactly the transactions serialized before this
point in time are seen in the view.
@param id		Creator transaction id */

void ReadView::prepare(trx_id_t id) {
  ut_ad(mutex_own(&trx_sys->mutex));

  m_creator_trx_id = id;

  m_low_limit_no = m_low_limit_id = m_up_limit_id = trx_sys->max_trx_id;

  if (!trx_sys->rw_trx_ids.empty()) {
    copy_trx_ids(trx_sys->rw_trx_ids);
  } else {
    m_ids.clear();
  }

  ut_ad(m_up_limit_id <= m_low_limit_id);

  if (UT_LIST_GET_LEN(trx_sys->serialisation_list) > 0) {
    const trx_t *trx;

    trx = UT_LIST_GET_FIRST(trx_sys->serialisation_list);

    if (trx->no < m_low_limit_no) {
      m_low_limit_no = trx->no;
    }
  }

  ut_d(m_view_low_limit_no = m_low_limit_no);
  m_closed = false;
}

判斷記錄是否可見

/* storage\innobase\include\read0types.h */

/** Check whether transaction id is valid.
@param[in]	id		transaction id to check
@param[in]	name		table name */
static void check_trx_id_sanity(trx_id_t id, const table_name_t &name);

/** Check whether the changes by id are visible.
@param[in]	id	transaction id to check against the view
@param[in]	name	table name
@return whether the view sees the modifications of id. */
bool changes_visible(trx_id_t id, const table_name_t &name) const
    MY_ATTRIBUTE((warn_unused_result)) {
  ut_ad(id > 0);

  if (id < m_up_limit_id || id == m_creator_trx_id) {
    return (true);
  }

  check_trx_id_sanity(id, name);

  if (id >= m_low_limit_id) {
    return (false);

  } else if (m_ids.empty()) {
    return (true);
  }

  const ids_t::value_type *p = m_ids.data();

  return (!std::binary_search(p, p + m_ids.size(), id));
}