目的
在学习代码的过程中经常看到attachable transaction,它到底是做什么的,目的是什么呢。这篇文章简单的介绍一下它的作用和用法,以帮助大家理解代码。
简介
Attachable transaction是从5.7引入的一个概念,主要用来对事务类型的系统表访问的接口,从事务的系统表查询得到一致的数据。目前主要是对innodb类型的系统表访问的接口,也只有innodb引擎实现了attachable transaction的支持。Attachable transaction 主要是为访问事务类型的系统表而设计的,它是一个嵌入用户事务的内部事务,当用户事务用到元信息时就需要开启一个attachable transaction去访问数据字典,得到用户表的元信息后,要结束attachable transaction,用户会话要能恢复到用户事务之前的状态。 Attachable transaction 是一个AC-RO-RC-NL (auto-commit, read-only, read-committed, non-locking) 事务。引入Attachable transaction主要有以下几个原因: 1) 如果用户开启的一个事务需要访问系统表获取表的元信息,而访问系统表可能和用户事务指定的隔离级别不一致,这时就需要开启一个独立的访问数据字典的事务,要求访问数据字典事务的隔离级别必须是READ COMMITTED,其隔离级别可能和用户指定的隔离级别不一致。 2) 对数据字典的访问必须是非锁定的。 3) 即时用户事务已经打开和锁定了用户表,在执行SQL语句的在任何时候也应该能对数据字典打开,来查询用户表的各种元信息。
核心数据结构
在每个会话的THD结构里,添加了一个 Attachable_trx *m_attachable_trx;字段,用来指向当前会话的内嵌事务。Attachable_trx 类型定义如下:
1. /**
2. Class representing read-only attachable transaction, encapsulates
3. knowledge how to backup state of current transaction, start
4. read-only attachable transaction in SE, finalize it and then restore
5. state of original transaction back. Also serves as a base class for
6. read-write attachable transaction implementation.
7. */
8. class Attachable_trx
9. {
10. public:
11. Attachable_trx(THD *thd);
12. virtual ~Attachable_trx();
13. virtual bool is_read_only() const { return true; }
14. protected:
15. /// THD instance.
16. THD *m_thd;
18. /// Transaction state data.
19. Transaction_state m_trx_state;
21. private:
22. Attachable_trx(const Attachable_trx &);
23. Attachable_trx &operator =(const Attachable_trx &);
24. };
其中最重要的是m_trx_state字段,期存放着attachable transaction的重要信息,就是用它来保存外部用户事务的状态,以便在着attachable transaction结束后能恢复到原来的用户事务状态。其定义如下:
1. /** An utility struct for @c Attachable_trx */
2. struct Transaction_state
3. {
4. void backup(THD *thd);
5. void restore(THD *thd);
7. /// SQL-command.
8. enum_sql_command m_sql_command;
10. Query_tables_list m_query_tables_list;
12. /// Open-tables state.
13. Open_tables_backup m_open_tables_state;
15. /// SQL_MODE.
16. sql_mode_t m_sql_mode;
18. /// Transaction isolation level.
19. enum_tx_isolation m_tx_isolation;
21. /// Ha_data array.
22. Ha_data m_ha_data[MAX_HA];
24. /// Transaction_ctx instance.
25. Transaction_ctx *m_trx;
27. /// Transaction read-only state.
28. my_bool m_tx_read_only;
30. /// THD options.
31. ulonglong m_thd_option_bits;
33. /// Current transaction instrumentation.
34. PSI_transaction_locker *m_transaction_psi;
36. /// Server status flags.
37. uint m_server_status;
38. };
核心接口API
启动一个attachable transaction
主要有这几个函数THD::begin_attachable_transaction()/begin_attachable_ro_transaction()/begin_attachable_rw_transaction(), 启动attachable transaction的过程中主要完成以下功能: 1) 在开始一个attachable_transaction之前,先要保存当前已经开始的用户的正常事务状态。 2) 开始设置一个新的事务所需要的各种状态。 3) 重新设置THD::ha_data。通过重制THD::ha_data值使InnoDB在接下来的操作去创建以下新的事务。 4) 执行对系统表的操作。 5) 当执行到存储引擎层时,InnoDB从传进来的THD指针中发现事务还没开启 (因为THD::ha_data重置了),InnoDB就会新启一个事务。 6) InnoDB通过调用trans_register_ha()通知server它已经创建了一个新事务。 7) InnoDB执行请求的操作,返回给server层。
1. THD::Attachable_trx::Attachable_trx(THD *thd)
2. :m_thd(thd)
3. {
4. m_trx_state.backup(m_thd); //保存当前已经开始的用户的正常事务状态
6. ......
8. m_thd->reset_n_backup_open_tables_state(&m_trx_state.m_open_tables_state); //保存一些打开表的状态信息,并且重新为新的事物重置表状态
10. // 为attachable transaction创建一个新的事物上下文
11. m_thd->m_transaction.release(); // it's been backed up.
12. m_thd->m_transaction.reset(new Transaction_ctx());
14. ......
16. for (int i= 0; i < MAX_HA; ++i)
17. m_thd->ha_data[i]= Ha_data(); //重新设置THD::ha_data
19. m_thd->tx_isolation= ISO_READ_COMMITTED; // attachable transaction 必须是read committed
21. m_thd->tx_read_only= true; // attachable transaction 必须是只读的
23. // attachable transaction 必须是 AUTOCOMMIT
24. m_thd->variables.option_bits|= OPTION_AUTOCOMMIT;
25. m_thd->variables.option_bits&= ~OPTION_NOT_AUTOCOMMIT;
26. m_thd->variables.option_bits&= ~OPTION_BEGIN;
28. ......
29. }
结束一个attachable transaction
THD::end_attachable_transaction()函数。因为attachable transaction事务是一个只读的自提交事务,所以它不需要调用任何事务需要提交火回滚的函数,比如: ha_commit_trans() / ha_rollback_trans() / trans_commit() / trans_rollback()。所以定义了此函数用来结束当前的attachable transaction。它主要完成以下功能: 1) 调用close_thread_tables()关闭在attachable transaction中打开的表。 2) 调用close_connection()通知引擎层去销毁为attachable transaction创建的事务。 3) InnoDB调用trx_commit_in_memory()去销毁readview等操作。 4) 最后要恢复之前正常的用户事务,包括THD::ha_data的恢复,这个通过调用下面提到的事务状态的backup()/restore()接口。
1. THD::Attachable_trx::~Attachable_trx()
2. {
3. ......
5. close_thread_tables(m_thd); //调用close_thread_tables()关闭在attachable transaction中打开的
7. ha_close_connection(m_thd); //调用close_connection()通知引擎层去销毁为attachable transaction创建的事务
9. // 恢复之前正常的用户事状态
10. m_trx_state.restore(m_thd);
12. m_thd->restore_backup_open_tables_state(&m_trx_state.m_open_tables_state);
14. ......
15. }
事务的保存、恢复接口函数 Transaction_state::backup()/restore()
由于一个会话同时只允许有一个活跃事务,当需要访问内部的事务系统表时,就需要开启一个Attachable transaction事务,这时就要先把外部的主事务状态先保存起来,等内部开启的Attachable transaction事务执行完,再把外部用户执行的事务恢复回来。为了实现这个功能,提供了backup和restore的接口。
Handler API的改动
为了让存储层知道server层开启的是一个attachable transaction,handler API新加了一个HA_ATTACHABLE_TRX_COMPATIBLE 标志。设置这个标志的存储引擎类型表示引擎层已经意识到开启了attachable transaction的事务类型。目前InnoDB和MyISAM引擎都能处理这种attachable transaction。但处理方式不同: 1)对InnoDB而言,完全支持attachable transaction事务,能够感知到THD::ha_data 的变化并开启一个attachable transaction事务。在close_connection时结束一个attachable transaction事务,然后恢复用户正常的事务继续处理。 2) 对MyISAM而言,虽然知道server开启了一个attachable transaction事务但也不做任何处理,就是简单但忽律掉 THD::ha_data and close_connection handlerton相关但处理。
在初始化一个InnoDB表时,设置HA_ATTACHABLE_TRX_COMPATIBLE 标志的代码如下:
1. ha_innobase::ha_innobase(
2. /*=====================*/
3. handlerton* hton,
4. TABLE_SHARE* table_arg)
5. :handler(hton, table_arg),
6. m_prebuilt(),
7. m_prebuilt_ptr(&m_prebuilt),
8. m_user_thd(),
9. m_int_table_flags(HA_REC_NOT_IN_SEQ
10. ......
11. | HA_ATTACHABLE_TRX_COMPATIBLE
12. | HA_CAN_INDEX_VIRTUAL_GENERATED_COLUMN
13. ),
14. m_start_of_scan(),
15. m_num_write_row(),
16. m_mysql_has_locked()
17. {}
