首页 > 数据库 > MySQL > MySQL 大字段问题
开发规范中,一般不让使用大字段,大部分关于给出的原因有影响缓存,高并发查询的情况会打满带宽,溢出存储,查询的时候多了一次指针查找,在姜老师的书中还看到了一个原因,就是大字段的dml 会使用悲观的方式处理,这种对二叉树加x-latch,会导致并发性能的下降。
姜老师的书中描述是大字段的插入,更新,删除会使用悲观操作,但是我测试的情况是针对insert没有使用悲观操作,update是使用了悲观操作
update的堆栈如下
btr_cur_pessimistic_update(unsigned long flags, btr_cur_t * cursor, big_rec_t ** big_rec, upd_t * update, unsigned long cmpl_info, que_thr_t * thr, mtr_t * mtr) (/root/mysql-5.0.15/innobase/btr/btr0cur.c:1766) row_upd_clust_rec(upd_node_t * node, dict_index_t * index, que_thr_t * thr, mtr_t * mtr) (/root/mysql-5.0.15/innobase/row/row0upd.c:1530) row_upd_clust_step(upd_node_t * node, que_thr_t * thr) (/root/mysql-5.0.15/innobase/row/row0upd.c:1742) row_upd(que_thr_t * thr, upd_node_t * node) (/root/mysql-5.0.15/innobase/row/row0upd.c:1819) row_upd_step(que_thr_t * thr) (/root/mysql-5.0.15/innobase/row/row0upd.c:1948) row_update_for_mysql(unsigned char * mysql_rec, row_prebuilt_t * prebuilt) (/root/mysql-5.0.15/innobase/row/row0mysql.c:1386) ha_innobase::update_row(ha_innobase * const this, const mysql_byte * old_row, mysql_byte * new_row) (/root/mysql-5.0.15/sql/ha_innodb.cc:3622) mysql_update(THD * thd, TABLE_LIST * table_list, List<Item> & fields, List<Item> & values, COND * conds, uint order_num, ORDER * order, ha_rows limit, enum_duplicates handle_duplicates, bool ignore) (/root/mysql-5.0.15/sql/sql_update.cc:429) mysql_execute_command(THD * thd) (/root/mysql-5.0.15/sql/sql_parse.cc:3192) mysql_parse(THD * thd, char * inBuf, uint length) (/root/mysql-5.0.15/sql/sql_parse.cc:5536) dispatch_command(enum_server_command command, THD * thd, char * packet, unsigned int packet_length) (/root/mysql-5.0.15/sql/sql_parse.cc:1697) do_command(THD * thd) (/root/mysql-5.0.15/sql/sql_parse.cc:1498) handle_one_connection(void * arg) (/root/mysql-5.0.15/sql/sql_parse.cc:1143) libpthread.so.0!start_thread (Unknown Source:0) libc.so.6!clone (Unknown Source:0)
在更新的时候,先加的s index->lock,然后加了x锁
堆栈如下
mysqld!btr_cur_search_to_nth_level(dict_index_t*, unsigned long, dtuple_t const*, page_cur_mode_t, unsigned long, btr_cur_t*, unsigned long, char const*, unsigned long, mtr_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/btr/btr0cur.cc:996) mysqld!btr_pcur_open_with_no_init_func(dict_index_t*, dtuple_t const*, page_cur_mode_t, unsigned long, btr_pcur_t*, unsigned long, char const*, unsigned long, mtr_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/include/btr0pcur.ic:528) mysqld!btr_pcur_restore_position_func(unsigned long, btr_pcur_t*, char const*, unsigned long, mtr_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/btr/btr0pcur.cc:365) mysqld!row_upd_clust_rec(unsigned long, upd_node_t*, dict_index_t*, unsigned long*, mem_block_info_t**, que_thr_t*, mtr_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/row/row0upd.cc:2694) mysqld!row_upd_clust_step(upd_node_t*, que_thr_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/row/row0upd.cc:2957) mysqld!row_upd(upd_node_t*, que_thr_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/row/row0upd.cc:3054) mysqld!row_upd_step(que_thr_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/row/row0upd.cc:3200) mysqld!row_update_for_mysql_using_upd_graph(unsigned char const*, row_prebuilt_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/row/row0mysql.cc:2582) mysqld!row_update_for_mysql(unsigned char const*, row_prebuilt_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/row/row0mysql.cc:2672) mysqld!ha_innobase::update_row(unsigned char const*, unsigned char*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/handler/ha_innodb.cc:8257) mysqld!handler::ha_update_row(unsigned char const*, unsigned char*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/handler.cc:8134) mysqld!mysql_update(THD*, List<Item>&, List<Item>&, unsigned long long, enum_duplicates, unsigned long long*, unsigned long long*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_update.cc:894) mysqld!Sql_cmd_update::try_single_table_update(THD*, bool*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_update.cc:2906) mysqld!Sql_cmd_update::execute(THD*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_update.cc:3037) mysqld!mysql_execute_command(THD*, bool) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:3616) mysqld!mysql_parse(THD*, Parser_state*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:5584) mysqld!dispatch_command(THD*, COM_DATA const*, enum_server_command) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:1491) mysqld!do_command(THD*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:1032) mysqld!::handle_connection(void *) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/conn_handler/connection_handler_per_thread.cc:313) mysqld!::pfs_spawn_thread(void *) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/perfschema/pfs.cc:2197)
在btr_cur_search_to_nth_level函数中,执行下面的case分支,加x
switch (latch_mode) {
case BTR_MODIFY_TREE:
/* Most of delete-intended operations are purging.
Free blocks and read IO bandwidth should be prior
for them, when the history list is glowing huge. */
if (lock_intention == BTR_INTENTION_DELETE
&& trx_sys->rseg_history_len > BTR_CUR_FINE_HISTORY_LENGTH
&& buf_get_n_pending_read_ios()) {
mtr_x_lock(dict_index_get_lock(index), mtr);
blob,text 更新都会执行mtr_x_lock(dict_index_get_lock(index), mtr);
即使是更新表中的其他字段,比如varchar 字段也会加x锁,不包含text,blob类型的表更新是不会有的。
所以在规范说明中,可以并发影响这一条。
5.7中的 btr_cur_search_to_nth_level 伪代码
用s_latch_by_caller 判断caller是否已经有了s锁
设置btr_op类型
通过modify_external判断caller在修改叶子结点的时候是否要分配或释放外部存储的字段
如果有搜索latch,先释放
根据latch_mode判断对index加什么锁
如果是修改tree,删除操作,如果history list过大超过10万,并且buffer pool有pending的io ,那么会对index tree加x锁,如果是spatial index悲观删除,加x锁,否则加sx锁,这个是5.7优化的。
其他的如果caller没有s 锁,不修改外部存储,加s锁,修改外部存储加sx锁
在root开始搜索
在非页节点搜索的时候,搜索的模式会改变下,在搜索leaf的时候,使用原本的搜索模式。
进入循环搜索直到需要的level
开始读取page,tree_bocks维护整个树的block
在重试搜索之前节点的时候,对left兄弟也是加latch,在修改节点的时候,也会获取父的lock,否则不会更改的。
如果是叶节点,就对叶节点加latch ,btr_cur_latch_leaves
如果已经到了想要的level推出循环
不是想要的level,如果删除的是页中的第一个或最后一个记录,那么要修改上层的node_ptr,要反向修改,这个会释放root以及所有block,将block标记unfix , 释放锁上的latch。
边界条件的处理
到了指定的level,如果root latch已经释放,重新加sx锁。
对branch block加x latch
如果是叶子节点,为了防止其他修改tree操作,对tree 加sx-latch,因为有了sx,所以上层的块加的锁可以释放了。
在对叶子节点加锁的时候,最左右兄弟节点也是加了x-latch的。
sx latch已经降低了对读的影响。
在释放tree的s锁时候,如果是修改外部存储字段,那么会继续保持tree的sx锁。
在悲观更新的时候,是持有tree的x-latch以及page的x-latch的,如果更新发生在leaf,那么page的兄弟节点也会加x-latch,这种的并发性比sx低。
有兴趣学习源码的加群一起学习啊 QQ: 700072075
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/25719946/viewspace-2898118/ ,如需转载,请注明出处,否则将追究法律责任。