MYSQL innodb中的只讀事物以及事物id的分配方式
原創水平有限,如果有誤請指出
一、只讀事物
也許有人要問一個select算不算一個事物。其實在innodb中一個innodb的select是一個事物,他有trx_t結構體,並且放到了mysql_trx_list連結串列中,關於
innodb事物系統一級的事都做了,但是這種事物叫做只讀事物
bool read_only; /*!< true if transaction is flagged
as a READ-ONLY transaction.
if auto_commit && will_lock == 0
then it will be handled as a
AC-NL-RO-SELECT (Auto Commit Non-Locking
Read Only Select). A read only
transaction will not be assigned an
UNDO log. */
在實際的使用中他沒有自己的鎖結構也沒有自己的undo segment,這一點很好理解因為這個操作
始終是非鎖定的,至少在innodb一級是這樣(lock0lock.cc lock_table 都沒呼叫),但是在MYSQL中,我們會發現實際上select語句也會
獲得MDL LOCK。(再次宣告這裡只是說innodb select沒有表級別鎖存在,但是MYSQL上層會有MDL LOCK)
對於只讀事物原始碼註釋給出的流程如下:
Auto-commit non-locking read-only:
* NOT_STARTED -> ACTIVE -> NOT_STARTED
而我們一般的2pc TRX流程如下:
XA (2PC):
* NOT_STARTED -> ACTIVE -> PREPARED -> COMMITTED -> NOT_STARTED
可以看到他實際上就是沒有commit的步驟,沒有undo reodo這些當然是不需要的。但是不可否認它是一個事物
另外當需要一個事物的時候在現在innodb版本中呼叫如下:
trx_allocate_for_mysql --> trx_allocate_for_background --> trx_create_low
這裡涉及到一個innodb 事物池的概念,trx_create_low 從事物池中取出一個事物TRX_T結構體指標給呼叫者
這個步驟完成後事物處於NOT_STARTED階段,這個時候TRX_T結構各種屬性都處於初始化階段,為什麼要說一下
事物池的概念因為後面說事物號分配的時候會用到這個概念。
然後根據呼叫者的需求適時啟用事物。實際上會呼叫,而呼叫會透過
trx_start_if_not_started_low->trx_start_low完成,在trx_start_low做好事物結構的準備工作,我們來看一
下關於原始碼中重點的部分
根據上面的註釋,我們可以看到只讀事物沒有分配undo segment也不會分配LOCK鎖結構
二、事物ID的分配
也許很多朋友不止我一個人在show engine innodb status的時候會看到如下兩種截然不同,相差很大的事物ID
(MYSQL)---TRANSACTION 329759, ACTIVE 10 sec
1 lock struct(s), heap size 1160, 0 row lock(s), undo log entries 1
MySQL thread id 3, OS thread handle 140737154152192, query id 28 localhost root cleaning up
(MYSQL)---TRANSACTION 422212177398528, not started
0 lock struct(s), heap size 1160, 0 row lock(s)
這裡事物id 329759和422212177398528相差很大,innodb是怎麼分配的呢?
其實這裡實際上的事物id只有329759,及trx_t.id,是一個正常的DML事物,而對於 not started狀態的事物
以及只讀事物,是沒有事物id的其實就是0,但是show engine innodb status的時候會呼叫時候會簡單的分配
一個而已。
其實TRX ID的分配也是在trx_start_low中呼叫(trx_assign_id_for_rw(trx);//分配事物號 )
而對於只讀事物並不會分配(trx->id = 0; //任然沒有分配事物ID給只讀事物)都在上面的程式碼解釋中,
而show engine innodb status的時候對於事物ID為0的事物做如下輸出
我們從註釋也能看出
Readonly and transactions whose intentions are unknown don't have trx_t::id assigned (it is 0 for those transactions)
實際上422212177398528這種id就是這裡列印的時候分配的,沒有什麼實際的意義
這裡的max_trx_id是一個常量281474976710655如果trx->id==0會呼叫
reinterpret_cast(trx) | (max_trx_id + 1)
將指標轉換為一個64位元組非負整數然後位或上(max_trx_id + 1),如下:
(gdb) p max_trx_id
$19 = 281474976710655
(gdb) p reinterpret_cast(trx)
$20 = 140737200690640
(gdb) p reinterpret_cast(trx) | (max_trx_id + 1)
$21 = 422212177401296
而對於這裡DML的事物號的分配如下:
這裡涉及到事物池。
而對於trx_sys_get_new_trx_id如下:
如此我們看到DML事物的事物ID是innodb分配的,而只讀事物或者not start事物的事物ID是在show engine的時候根據trx_t結構體
所在記憶體的指標演算法出來的,沒有實際的意義。
三、驗證只讀事物的存在
對於只讀事物我們在show engine innodb 只會列印出not start的事物或者活躍的已經獲得了鎖結構的事物一般是DML操作
但是可以再innodb_trx中觀察到,我這裡就簡單修改show engine innodb 原始碼列印輸出將只讀事物列印出來標記為RO TRX,
並且和innodb_trx對比
這裡看到我們的只讀事物為RO TRX,lock struct(s)為0,沒有undo entries,因為有會列印出來。
再來看看innodb_trx的輸出:
沒有問題都能觀察到,同樣我們也如我們所說只讀事物的事物ID是422212177402680,只是TRX_T結構體指標所在位置算出來的
演算法在上面。這裡注意事物也是有狀態標識的比如這裡的fetching rows。
四、其他
其實innodb中的事物比想象的要大很多,一個innodb的ddl是一個事物,一個innodb的select是一個事物,很多內部修改資料字典的操作也是一個事物
當然我們平時做的DML那更是事物了,上面說了只讀事物這裡簡單提一下ddl事物和內部事物。
這裡將trx_t結構體重關於他們標誌給出來:
innodb的ddl事物:
bool ddl; /*!< true if it is an internal transaction for DDL */
函式呼叫: trx_start_for_ddl_low-->trx_start_internal_low
可以看到一個ddl既是一個內部事物也是一個ddl事物
innodb的內部事物:
bool internal; /*!< true if it is a system/internal
transaction background task. This
includes DDL transactions too. Such
transactions are always treated as
read-write. */
函式呼叫:trx_start_internal_low 典型的innodb修改資料字典就是internal事物
關於只讀事物實際上在官方手冊也有說明具體在
作者微信:
一、只讀事物
也許有人要問一個select算不算一個事物。其實在innodb中一個innodb的select是一個事物,他有trx_t結構體,並且放到了mysql_trx_list連結串列中,關於
innodb事物系統一級的事都做了,但是這種事物叫做只讀事物
bool read_only; /*!< true if transaction is flagged
as a READ-ONLY transaction.
if auto_commit && will_lock == 0
then it will be handled as a
AC-NL-RO-SELECT (Auto Commit Non-Locking
Read Only Select). A read only
transaction will not be assigned an
UNDO log. */
在實際的使用中他沒有自己的鎖結構也沒有自己的undo segment,這一點很好理解因為這個操作
始終是非鎖定的,至少在innodb一級是這樣(lock0lock.cc lock_table 都沒呼叫),但是在MYSQL中,我們會發現實際上select語句也會
獲得MDL LOCK。(再次宣告這裡只是說innodb select沒有表級別鎖存在,但是MYSQL上層會有MDL LOCK)
對於只讀事物原始碼註釋給出的流程如下:
Auto-commit non-locking read-only:
* NOT_STARTED -> ACTIVE -> NOT_STARTED
而我們一般的2pc TRX流程如下:
XA (2PC):
* NOT_STARTED -> ACTIVE -> PREPARED -> COMMITTED -> NOT_STARTED
可以看到他實際上就是沒有commit的步驟,沒有undo reodo這些當然是不需要的。但是不可否認它是一個事物
另外當需要一個事物的時候在現在innodb版本中呼叫如下:
trx_allocate_for_mysql --> trx_allocate_for_background --> trx_create_low
這裡涉及到一個innodb 事物池的概念,trx_create_low 從事物池中取出一個事物TRX_T結構體指標給呼叫者
這個步驟完成後事物處於NOT_STARTED階段,這個時候TRX_T結構各種屬性都處於初始化階段,為什麼要說一下
事物池的概念因為後面說事物號分配的時候會用到這個概念。
然後根據呼叫者的需求適時啟用事物。實際上會呼叫,而呼叫會透過
trx_start_if_not_started_low->trx_start_low完成,在trx_start_low做好事物結構的準備工作,我們來看一
下關於原始碼中重點的部分
點選(此處)摺疊或開啟
-
trx->read_only =
-
(trx->api_trx && !trx->read_write)
-
|| (!trx->ddl && !trx->internal
-
&& thd_trx_is_read_only(trx->mysql_thd))
-
|| srv_read_only_mode; //此處獲取事物當前是否是隻讀屬性,可以看到他和我們的read_only引數設定事物是ddl事物是否是內部事物有關
-
-
if (!trx->auto_commit) { //是否自動提交否則需要設定will_Lock屬性如果時候只讀事物未TURE,如果是DML事物為flase
-
//這裡的auto_commit屬性和我們平時設定的引數感覺不是一回事
-
++trx->will_lock;
-
} else if (trx->will_lock == 0) {
-
trx->read_only = true; //如果不需要will_lock屬性它肯定是隻讀事物
-
}
-
//以上也就說明了只讀事物不需要鎖結構因為 trx->will_lock = 0(false)
-
/* We tend to over assert and that complicates the code somewhat.
-
e.g., the transaction state can be set earlier but we are forced to
-
set it under the protection of the trx_sys_t::mutex because some
-
trx list assertions are triggered unnecessarily. */
-
-
/* By default all transactions are in the read-only list unless they
-
are non-locking auto-commit read only transactions or background
-
(internal) transactions. Note: Transactions marked explicitly as
-
read only can write to temporary tables, we put those on the RO
-
list too. */
-
//當然如果是非只讀事物 我們需要開始分配undo rollback segment了 以及undo segment了
-
//並且trx->mysql_thd == 0 表示是否是MYSQL執行緒建立的innodb事物
-
//是否是讀寫事物這個是由呼叫者傳入只讀事物為false,DML事物為true,這裡的讀寫和前面
-
//trx->read_only有區別如果是隻讀事物建立臨時表也是讀寫事物
-
//是否是DDL事物 DDL也需要分配undo rollback segment了 以及undo segment
-
if (!trx->read_only
-
&& (trx->mysql_thd == 0 || read_write || trx->ddl)) {
-
-
trx->rsegs.m_redo.rseg = trx_assign_rseg_low(
-
srv_undo_logs, srv_undo_tablespaces,
-
TRX_RSEG_TYPE_REDO);
-
-
/* Temporary rseg is assigned only if the transaction
-
updates a temporary table */
-
-
trx_sys_mutex_enter();
-
-
trx_assign_id_for_rw(trx);//分配事物號
-
/*
-
(gdb) p trx_sys->max_trx_id
-
$21 = 328707
-
*/
-
-
trx_sys_rw_trx_add(trx); //將入集合
-
-
ut_ad(trx->rsegs.m_redo.rseg != 0
-
|| srv_read_only_mode
-
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
-
-
UT_LIST_ADD_FIRST(trx_sys->rw_trx_list, trx); //將事物放入rw_trx_list
-
-
ut_d(trx->in_rw_trx_list = true);
-
#ifdef UNIV_DEBUG
-
if (trx->id > trx_sys->rw_max_trx_id) {
-
trx_sys->rw_max_trx_id = trx->id;
-
}
-
#endif /* UNIV_DEBUG */
-
-
trx->state = TRX_STATE_ACTIVE; //更改事物的狀態為ACTIVE
-
-
ut_ad(trx_sys_validate_trx_list());
-
-
trx_sys_mutex_exit();
-
-
} else {
-
trx->id = 0; //任然沒有分配事物ID給只讀事物
-
-
if (!trx_is_autocommit_non_locking(trx)) { //#define trx_is_autocommit_non_locking(t) ((t)->auto_commit && (t)->will_lock == 0)
-
-
/* If this is a read-only transaction that is writing
-
to a temporary table then it needs a transaction id
-
to write to the temporary table. */
-
//如果是隻讀事物並且寫入了臨時表需要額外操作
-
-
if (read_write) {
-
-
trx_sys_mutex_enter();
-
-
ut_ad(!srv_read_only_mode);
-
-
trx_assign_id_for_rw(trx);
-
-
trx_sys->rw_trx_set.insert(
-
TrxTrack(trx->id, trx));
-
-
trx_sys_mutex_exit();
-
}
-
-
trx->state = TRX_STATE_ACTIVE;
-
-
} else {
-
ut_ad(!read_write);
-
trx->state = TRX_STATE_ACTIVE;
-
}
-
}
-
-
if (trx->mysql_thd != NULL) {
-
trx->start_time = thd_start_time_in_secs(trx->mysql_thd); //開始計時這是系統時間LINUX系統呼叫time()
-
} else {
-
trx->start_time = ut_time();
- }
根據上面的註釋,我們可以看到只讀事物沒有分配undo segment也不會分配LOCK鎖結構
二、事物ID的分配
也許很多朋友不止我一個人在show engine innodb status的時候會看到如下兩種截然不同,相差很大的事物ID
(MYSQL)---TRANSACTION 329759, ACTIVE 10 sec
1 lock struct(s), heap size 1160, 0 row lock(s), undo log entries 1
MySQL thread id 3, OS thread handle 140737154152192, query id 28 localhost root cleaning up
(MYSQL)---TRANSACTION 422212177398528, not started
0 lock struct(s), heap size 1160, 0 row lock(s)
這裡事物id 329759和422212177398528相差很大,innodb是怎麼分配的呢?
其實這裡實際上的事物id只有329759,及trx_t.id,是一個正常的DML事物,而對於 not started狀態的事物
以及只讀事物,是沒有事物id的其實就是0,但是show engine innodb status的時候會呼叫時候會簡單的分配
一個而已。
其實TRX ID的分配也是在trx_start_low中呼叫(trx_assign_id_for_rw(trx);//分配事物號 )
而對於只讀事物並不會分配(trx->id = 0; //任然沒有分配事物ID給只讀事物)都在上面的程式碼解釋中,
而show engine innodb status的時候對於事物ID為0的事物做如下輸出
點選(此處)摺疊或開啟
-
UNIV_INLINE
-
trx_id_t
-
trx_get_id_for_print(
-
const trx_t* trx)
-
{
-
/* Readonly and transactions whose intentions are unknown (whether
-
they will eventually do a WRITE) don't have trx_t::id assigned (it is
-
0 for those transactions). Transaction IDs in
-
innodb_trx.trx_id,
-
innodb_locks.lock_id,
-
innodb_locks.lock_trx_id,
-
innodb_lock_waits.requesting_trx_id,
-
innodb_lock_waits.blocking_trx_id should match because those tables
-
could be used in an SQL JOIN on those columns. Also trx_t::id is
-
printed by SHOW ENGINE INNODB STATUS, and in logs, so we must have the
-
same value printed everywhere consistently. */
-
-
/* DATA_TRX_ID_LEN is the storage size in bytes. */
-
static const trx_id_t max_trx_id
-
= (1ULL << (DATA_TRX_ID_LEN * CHAR_BIT)) - 1;
-
-
ut_ad(trx->id <= max_trx_id);
-
-
return(trx->id != 0
-
? trx->id
-
: reinterpret_cast<trx_id_t>(trx) | (max_trx_id + 1));
- }
我們從註釋也能看出
Readonly and transactions whose intentions are unknown don't have trx_t::id assigned (it is 0 for those transactions)
實際上422212177398528這種id就是這裡列印的時候分配的,沒有什麼實際的意義
這裡的max_trx_id是一個常量281474976710655如果trx->id==0會呼叫
reinterpret_cast(trx) | (max_trx_id + 1)
將指標轉換為一個64位元組非負整數然後位或上(max_trx_id + 1),如下:
(gdb) p max_trx_id
$19 = 281474976710655
(gdb) p reinterpret_cast(trx)
$20 = 140737200690640
(gdb) p reinterpret_cast(trx) | (max_trx_id + 1)
$21 = 422212177401296
而對於這裡DML的事物號的分配如下:
點選(此處)摺疊或開啟
-
void
-
trx_assign_id_for_rw(trx_t* trx)
-
{
-
ut_ad(mutex_own(&trx_sys->mutex));
-
-
trx->id = trx->preallocated_id
-
? trx->preallocated_id : trx_sys_get_new_trx_id();
-
//先判斷是否是這個事物分配過事物ID,因為從事物池中拿出來
-
//很可能以前用過,那麼就不需要再次分配了,否則新分配
-
-
if (trx->preallocated_id) { //如果是以前使用過的不一定是最大需要加入到vertor中間
-
// Maintain ordering in rw_trx_ids
-
trx_sys->rw_trx_ids.insert(
-
std::upper_bound(trx_sys->rw_trx_ids.begin(),
-
trx_sys->rw_trx_ids.end(),
-
trx->id), trx->id);
-
} else {
-
// The id is known to be greatest 新分配的肯定是最大 如果是最大加到某位即可
-
trx_sys->rw_trx_ids.push_back(trx->id);
-
}
- }
而對於trx_sys_get_new_trx_id如下:
點選(此處)摺疊或開啟
-
trx_sys_get_new_trx_id()
-
/*====================*/
-
{
-
ut_ad(trx_sys_mutex_own());
-
-
/* VERY important: after the database is started, max_trx_id value is
-
divisible by TRX_SYS_TRX_ID_WRITE_MARGIN, and the following if
-
will evaluate to TRUE when this function is first time called,
-
and the value for trx id will be written to disk-based
-
Thus trx id values will not overlap when the database is
-
repeatedly */
-
-
if (!(trx_sys->max_trx_id % TRX_SYS_TRX_ID_WRITE_MARGIN)) {
-
-
trx_sys_flush_max_trx_id(); //TRX_SYS_TRX_ID_WRITE_MARGIN為256 如果trx_sys->max_trx_id達到256的整數倍需要刷盤
-
//到TRX_SYS_TRX_ID_STORE中.
-
}
-
-
return(trx_sys->max_trx_id++);//然後自身+1返回
- }
如此我們看到DML事物的事物ID是innodb分配的,而只讀事物或者not start事物的事物ID是在show engine的時候根據trx_t結構體
所在記憶體的指標演算法出來的,沒有實際的意義。
對於只讀事物我們在show engine innodb 只會列印出not start的事物或者活躍的已經獲得了鎖結構的事物一般是DML操作
但是可以再innodb_trx中觀察到,我這裡就簡單修改show engine innodb 原始碼列印輸出將只讀事物列印出來標記為RO TRX,
並且和innodb_trx對比
點選(此處)摺疊或開啟
-
下面是我修改後show engine innodb的輸出
-
-
LIST OF TRANSACTIONS FOR EACH SESSION(1)(CHANGE BY GAOPENG ALL mysql_trx_list and rw_trx_list):
-
(MYSQL)---TRANSACTION 422212177402680, ACTIVE 3 sec fetching rows
-
mysql tables in use 1, locked 0
-
0 lock struct(s), heap size 1160, 0 row lock(s), RO TRX
-
MySQL thread id 7, OS thread handle 140737153619712, query id 411 localhost root Sending data
- select * from test.tuser
再來看看innodb_trx的輸出:
點選(此處)摺疊或開啟
-
mysql> select * from information_schema.innodb_trx \G
-
*************************** 1. row ***************************
-
trx_id: 422212177402680
-
trx_state: RUNNING
-
trx_started: 2017-07-19 16:52:53
-
trx_requested_lock_id: NULL
-
trx_wait_started: NULL
-
trx_weight: 0
-
trx_mysql_thread_id: 7
-
trx_query: select * from test.tuser
-
trx_operation_state: fetching rows
-
trx_tables_in_use: 1
-
trx_tables_locked: 0
-
trx_lock_structs: 0
-
trx_lock_memory_bytes: 1160
-
trx_rows_locked: 0
-
trx_rows_modified: 0
-
trx_concurrency_tickets: 0
-
trx_isolation_level: REPEATABLE READ
-
trx_unique_checks: 1
-
trx_foreign_key_checks: 1
-
trx_last_foreign_key_error: NULL
-
trx_adaptive_hash_latched: 0
-
trx_adaptive_hash_timeout: 0
-
trx_is_read_only: 1
- trx_autocommit_non_locking: 1
演算法在上面。這裡注意事物也是有狀態標識的比如這裡的fetching rows。
四、其他
其實innodb中的事物比想象的要大很多,一個innodb的ddl是一個事物,一個innodb的select是一個事物,很多內部修改資料字典的操作也是一個事物
當然我們平時做的DML那更是事物了,上面說了只讀事物這裡簡單提一下ddl事物和內部事物。
這裡將trx_t結構體重關於他們標誌給出來:
innodb的ddl事物:
bool ddl; /*!< true if it is an internal transaction for DDL */
函式呼叫: trx_start_for_ddl_low-->trx_start_internal_low
可以看到一個ddl既是一個內部事物也是一個ddl事物
innodb的內部事物:
bool internal; /*!< true if it is a system/internal
transaction background task. This
includes DDL transactions too. Such
transactions are always treated as
read-write. */
函式呼叫:trx_start_internal_low 典型的innodb修改資料字典就是internal事物
關於只讀事物實際上在官方手冊也有說明具體在
Optimizing InnoDB Read-Only Transactions
我就不在說明什麼了。作者微信:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7728585/viewspace-2142302/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 如何快速找到MYSQL binlog中的大事物以及生成量分佈(infobin工具)MySql
- 事物的基本流程
- Spring事物管理和hibernate事物管理的疑問Spring
- java事物Java
- MYSQL如何識別一個binlog中的一個事物MySql
- Mysql——內外連線,事物,索引MySql索引
- MySQL 配置InnoDB為只讀操作MySql
- 事物管理的概念-筆記筆記
- 分散式事物分散式
- MySQL是如何實現事物隔離?MySql
- Spring 使用註解方式進行事物管理Spring
- redis-18.事物Redis
- Spring的事物回滾問題Spring
- SAP SEGW 事物碼裡的 ABAP Editor
- SAP SEGW 事物碼裡的 Function ImportFunctionImport
- MySQL:Innodb中數字的儲存方式MySql
- springboot宣告事物Spring Boot
- spring data mongodb 如何以事物的方式進行增刪改查SpringMongoDB
- 使用Spring實現訪問主從資料庫的讀寫和只讀事務/事物的分離路由 -Vlad MihalceaSpring資料庫路由
- Spring系列之事物是如何管理的Spring
- Spring(5、基於註解的事物)Spring
- 思考-前端學習發展之外的事物前端
- 如何處理IN_DOUBT的分佈事物
- Spring Boot 事物回滾Spring Boot
- redis-18.事物簡介Redis
- oracle 檢視分散式事物Oracle分散式
- undo 事物內部結構
- MySQL必知必會:簡介undo log、truncate、以及undo log如何幫你回滾事物MySql
- mysql 大事物commit慢造成全庫堵塞問題MySqlMIT
- 事物系統的幾種異常場景
- 如何快速學習複雜事物的指南 - BaldauLDA
- 掌握一個事物時所需要學會的
- 關於ITL以及UNDO SEGMENT HEADER 事物表(tx table)闡述Header
- MySQL 配置InnoDB的記憶體分配器MySql記憶體
- spring 異常 事物不回滾Spring
- 分開分表 分散式事物分散式
- 求助spring基本事物配置Spring
- 高手請進來!spring2的事物配置Spring