数据结构深化浅出-MySQL-从记载-页到索引揭秘数据存储神秘 (数据结构深化什么意思)
引言
本文在引见内存中记载、页、索引、游标的数据结构的基础上,经过便捷剖析拔出操作环节中行格局的转换引见了不同数据结构的相关,其中不触及加锁相关逻辑。
原理
记载
概念
InnoDB存储引擎基于记载(record)存储,标明记载是依据行(row)格局存储。
MySQL中有行格局有以下三种存储方式:
因此:
团体了解存取数据时成功二进制到字符串的相互转换(疏忽其余数据类型),相当于客户端与服务端交互时的编码与解码。
数据结构
内存中逻辑记载与其中的字段区分对应数据结构dtuple_t与dfield_t。
结构体定义如下所示。
/**StructureforanSQL>/**StructureforanSQL>/*Wedefinethephysicalrecordsimplyasanarrayofbytes*/typedefbyterec_t;
页
概念
页和记载相似雷同可以分为以下两种方式:
页分多种类型,比如indexpage、undologpage等,本文关注的是indexpage。
由于InnoDB是索引组织树,索引即数据,因此可以将索引页了解为数据页。
InnoDB中页是一个无序堆,因此页中的记载无序寄存,记载间经过recordheader中的nextrecord组成单向链表。
数据结构
bufferpool中与页相关的有两个数据结构,包括buf_block_t和buf_page_t。
buf_block_t是数据页控制体的一种。
/**Thebuffercontrolblockstructure*/structbuf_block_t{buf_page_tpage;/*!<pageinformation;thismustbethefirstfield,sothatbuf_pool->page_hashcanpointtobuf_page_torbuf_block_t*/byte*frame;BPageLocklock;/*!<read-writelockofthebufferframe*/
其中:
buf_page_t称为数据页控制体。
/**Thecommonbuffercontrolblockstructureforcompressedanduncompressedframes*/classbuf_page_t{public:page_id_tid;//pageidbuf_page_t*hash;/*!<nodeusedinchningtobuf_pool->page_hashorbuf_pool->zip_hash*/lsn_tnewest_modification;//以后Page最新修正lsnlsn_toldest_modification;//以后Page最老修正lsn,即第一条修正lsn};
其中:
索引
概念
索引是基于以空间换期间的思维用于减速查问的数据结构。
InnoDB中索引基于B+树成功,因此每个索引都是一棵B+树。索援用于组织页,页用于组织行记载。
数据结构
每个B+树索引在内存中对应数据结构dict_index_t。
/**>/**Thetreecursor:thedefinitionearshereonlyforthecompilertoknowstructsize!*/structbtr_cur_t{btr_cur_t(){memset(this,0,sizeof(*this));}dict_index_t*index;/*!<indexwherepositioned*/page_cur_tpage_cur;/*!<pagecursor*/purge_node_t*purge_node;/*!<purgenode,forBTR_DELETE*/buf_block_t*left_block;/*!<thisfieldisusedtostoreapointertotheleftneighborpage,inthecasesBTR_SEARCH_PREVandBTR_MODIFY_PREV*//*------------------------------*/que_thr_t*thr;/*!<thisfieldisonlyusedwhenbtr_cur_search_to_nth_leveliscalledforanindexentryinsertion:thecallingquerythreadispassedheretobeusedintheinsertbuffer*//*------------------------------*/};
其中:
pagecursor对应page_cur_t结构体。
/**Indexpagecursor*/structpage_cur_t{constdict_index_t*index;rec_t*rec;/*!<pointertoarecordonpage*/ulint*offsets;buf_block_t*block;/*!<pointertotheblockcontainingrec*/};
其中:
persistentcursor对应btr_pcur_t数据结构。
/*ThepersistentB-treecursorstructure.ThisisusedmainlyforSQLselects,updates,anddeletes.*/structbtr_pcur_t{btr_pcur_t(){memset(this,0,sizeof(*this));}/**aB-treecursor*/btr_cur_tbtr_cur;rec_t*old_rec;/**Returntheindexofthispersistentcursor*/dict_index_t*index()const{return(btr_cur.index);}};
其中:
上方以insert语句的口头为例,剖析不同数据结构之间的相关。
insert语句
build_template
从server层调用存储引擎的接口handler开局,详细是ha_innobase::write_row函数。
//storage/innbase/handler/ha_innodb.cc/*StoresarowinanInnoDB>typedefunsignedcharuchar;/*Shortforunsignedchar*//*NotethatinsideMySQL'byte'isdefinedascharon!*/#definebyteunsignedchar
m_prebuilt变量是row_prebuilt_t结构体指针类型,其中局部成员如下所示,包括表、索引、游标等各种消息。
/**SaveCPUtimewithprebuilt/cached>//storage/innobase/row/row0mysql.ccstaticdberr_trow_insert_for_mysql_using_ins_graph(constbyte*mysql_rec,/*rowintheMySQLformat*/row_prebuilt_t*prebuilt)/*prebuiltstructinMySQLhandle*/{que_thr_t*thr;ins_node_t*node=prebuilt->ins_node;//关键结构用于口头拔出操作的2个对象://1.ins_node_t对象,保留在prebuilt->ins_node中//2.que_fork_t对象,保留在prebuilt->ins_graph中row_get_prebuilt_insert_row(prebuilt);node=prebuilt->ins_node;//把server层的记载格局转换为InnoDB的记载格局row_mysql_convert_row_to_innobase(node->row,prebuilt,mysql_rec,&blob_heap);thr->run_node=node;thr->prev_node=node;//口头拔出操作,拔出记载到主键索引、二级索引(蕴含惟一索引、非惟一索引)/*拔出记载*/row_ins_step(thr);}
其中:
这里又出现了一个关键的结构体ins_node_t,该结构体局部成员如下所示。
/*Insertnodestructure*/structins_node_t{dtuple_t*row;/*!<rowtoinsert*/dict_table_t*table;/*!<tablewheretoinsert*/sel_node_t*select;/*!<selectinsearchedinsert*/que_node_t*values_list;/*listofexpressionstoevaluateandinsertinanINS_VALUESinsert*/ulintstate;/*!<nodeexecutionstate*/dict_index_t*index;/*!<NULL,orthenextindexwheretheindexentryshouldbeinserted*/dtuple_t*entry;/*!<NULL,orentrytoinsertintheindex;afterasuccessfulinsertoftheentry,thisshouldberesettoNULL*/}
其中:
row_mysql_convert_row_to_innobase函数中遍历索引的一切列(prebuilt->n_template)启动赋值。
staticvoidrow_mysql_convert_row_to_innobase(/*==============================*/dtuple_t*row,/*!<in/out:Innobaserowwherethefieldtypeinformationisalreadycopiedthere!*/row_prebuilt_t*prebuilt,/*!<in:prebuiltstructwheretemplatemustbeoftypeROW_MYSQL_WHOLE_ROW*/constbyte*mysql_rec,/*!<in:rowintheMySQLformat;*/mem_heap_t**blob_heap)/*!<in:FIX_ME,removethisafterserverfixesitsissue*/{constmysql_row_templ_t*templ;for(i=0;i<prebuilt->n_template;i++){templ=prebuilt->mysql_template+i;//失掉fielddfield=dtuple_get_nth_field(row,n_col);//格局转换,从rec写入dtuple_trow_mysql_store_col_in_innobase_format(/*dfield_t*dfield,calltosetfield*/dfield,/*byte*buf,bufferforaconvertedintegervalue*/prebuilt->ins_upd_rec_buff+templ->mysql_col_offset,/*iboolrow_format_col,TRUEifthemysql_dataisfromaMySQLrow,FALSEiffromaMySQLkeyvalue;*/TRUE,/*byte*mysql_data,MySQLrowformat>constbyte*ptr=mysql_data;constdtype_t*dtype;ulinttype;dtype=dfield_get_type(dfield);type=dtype->mtype;//计算col_len...dfield_set_data(dfield,ptr,col_len);
其中:
/*Setspointertothe>//创立ins_node_tnode=static_cast<ins_node_t*>(thr->run_node);//给表加上意向锁err=lock_table(0,node->table,LOCK_IX,thr);/*DOTHECHECKSOFTHECONSISTENCYCONSTRAINTSHERE*///调用row_ins()拔出记载到主键索引、二级索引err=row_ins(node,thr);
row_ins函数中遍历表的每个index,拔出indexentry。
//遍历一切索引,向每个索引中拔出记载while(node->index!=NULL){/*向索引中拔出记载*/err=row_ins_index_entry_step(node,thr);//拔出记载到主键索引或二级索引成功后失掉下一个索引//node->index、entry指向表中的下一个索引node->index=dict_table_get_next_index(node->index);node->entry=UT_LIST_GET_NEXT(tuple_list,node->entry);
其中:
row_ins_index_entry_step函数中向指定索引中拔出记载。
//storage/innobase/row/row0insert.cc/*Insertsasingleindexentrytothetable.*/staticMY_ATTRIBUTE((nonnull,warn_unused_result))dberr_trow_ins_index_entry_step(/*=====================*/ins_node_t*node,/*!<in:rowinsertnode*/que_thr_t*thr)/*!<in:querythread*/{/*给索引项赋值*/err=row_ins_index_entry_set_vals(node->index,node->entry,node->row);/*拔出索引项*/err=row_ins_index_entry(node->index,node->entry,thr);}
其中:
/**Setsthevaluesofthedtuplefieldsinentryfromthevaluesofappropriatecolumnsinrow.*/dberr_trow_ins_index_entry_set_vals(constdict_index_t*index,dtuple_t*entry,constdtuple_t*row){n_fields=dtuple_get_n_fields(entry);for(i=0;i<n_fields+num_v;i++){dfield_t*field;constdfield_t*row_field;ulintlen;//失掉fieldfield=dtuple_get_nth_field(entry,i);row_field=dtuple_get_nth_field(row,ind_field->col->ind);len=dfield_get_len(row_field);//写入fielddfield_set_data(field,dfield_get_data(row_field),len);}
其中:
到这里每个索引的数据也预备好了,然而还不知道数据拔出的位置,通常上是插在最后一个小于等于该dtuple的rec_t后。
row_ins_index_entry函数中以拔出主键索引为例。
row_ins_clust_index_entry_low函数中以失望拔出为例。
//storage/innbase/row/row0ins.ccdberr_trow_ins_clust_index_entry_low(/*==========================*/ulintflags,/*!<in:undologgingandlockingflags*/ulintmode,/*!<in:BTR_MODIFY_LEAForBTR_MODIFY_TREE,dependingonwhetherwewishoptimisticorpessimisticdescentdowntheindextree*/dict_index_t*index,/*!<in:clusteredindex*/ulintn_uniq,/*!<in:0orindex->n_uniq*/dtuple_t*entry,/*!<in/out:indexentrytoinsert*/ulintn_ext,/*!<in:numberofexternallystoredcolumns*/que_thr_t*thr,/*!<in:querythread*/booldup_chk_only)/*!<in:iftrue,justdoduplicatecheckandreturn.don'texecuteactualinsert.*/{btr_pcur_tpcur;btr_cur_t*cursor;//offsetsulintoffsets_[REC_OFFS_NORMAL_SIZE];ulint*offsets=offsets_;/*NotethatweusePAGE_CUR_LEasthesearchmode,becausethenthefunctionwillreturninbothlow_matchandup_matchofthecursorsensiblevalues*///拔出操作的search_mode自动是PAGE_CUR_LE,即插在最后一个小于等于该dtuple的rec_t后//btr_pcur_open函数外部调用btr_cur_search_to_nth_level函数btr_pcur_open(index,entry,PAGE_CUR_LE,mode,&pcur,&mtr);//从btr_pcur_t中失掉btr_cur_t(treecursor)cursor=btr_pcur_get_btr_cur(&pcur);cursor->thr=thr;rec_t*insert_rec;/*失望拔出*/err=btr_cur_optimistic_insert(flags,cursor,&offsets,&offsets_heap,entry,&insert_rec,&big_rec,n_ext,thr,&mtr);}
其中:
btr_pcur_open函数中调用btr_cur_search_to_nth_level函数用于自顶向下查找整个B-tree。
page_cursor=btr_cur_get_page_cur(cursor);//初始的,取得索引的根节点(space_id,page_no)constulintspace=dict_index_get_space(index);constpage_size_tpage_size(dict_table_page_size(index->table));/*Startwiththerootpage.*///从dict_index_t元消息中拿到rootpage在物理文件的pageno(自动是4)page_id_tpage_id(space,dict_index_get_page(index));//从buffer_pool中依据pageno拿page(buf_page_get_gen),bufferpool模块会依据page能否被缓存来选择是从内存中还是磁盘中读取,并依据加锁战略对page加锁block=buf_page_get_gen(page_id,page_size,rw_latch,guess,buf_mode,file,line,mtr);tree_blocks[n_blocks]=block;/*Searchforcompleteindexfields.*/up_bytes=low_bytes=0;//对page外部的recordlist启动二分搜查(page_cur_search_with_match)//page_cur_search_with_match:在一个数据页内二分查找(经常使用数据页中的directoryslot),定位到recordpage_cur_search_with_match(block,index,tuple,page_mode,&up_match,&low_match,page_cursor,need_path?cursor->rtr_info:NULL);
其中:
/*Performbinarysearchuntilthelowerandupperlimitdirectoryslotscometothedistance1ofeachother*///在索引页外调找关于指定的tuple,满足某种条件(依赖于传入的mode,比如PAGE_CUR_L)的record//PAGE_CUR_G(>),PAGE_CUR_GE(>=),PAGE_CUR_L(<),PAGE_CUR_LE(<=)//1.二分查找//在稠密的PageDirectory内经常使用二分查找low=0;up=page_dir_get_n_slots(page)-1;/*Performlinearsearchuntiltheupperandlowerrecordscometodistance1ofeachother.*///二分查找完结后,low/up是邻近的两个slot,这两个slot指向的record记为//low_rec和up_rec,满足:low_rec<=tuple<=up_rec,切记tuple为待拔出的(逻辑)记载//2.线性查找,慢慢夹逼//在两个相邻的directory内,启动线性查找。线性查找的成功即始终"增大low","减小up"
其中:
//storage/innobase/rem/rem0cmp.cc/**Comparea>page_cur_t*page_cursor;buf_block_t*block;//经过cursor失掉blockblock=btr_cur_get_block(cursor);//buf_block_t::frame中保留pagepage=buf_block_get_frame(block);index=cursor->index;//btr_cur_t->page_cursorpage_cursor=btr_cur_get_page_cur(cursor);/*Checklocksandwritetotheundolog,ifspecified*///真正拔出entry前,会审核事务锁和记载undo//其中修正inherit值,假设下一条记载上没有锁,就不须要锁决裂err=btr_cur_ins_lock_and_undo(flags,cursor,entry,thr,mtr,&inherit);if(err!=DB_SUCCESS){gotofail_err;}//拔出数据*rec=page_cur_tuple_insert(page_cursor,entry,index,offsets,heap,n_ext,mtr);
其中:
page_cur_tuple_insert函数中调用rec_convert_dtuple_to_rec_new函数将逻辑记载转换成物理记载。
/*********************************************************//**Buildsanew-stylephysicalrecordoutofa>/*Storethe>//真正拔出记载rec=page_cur_insert_rec_low(cursor->rec,index,rec,*offsets,mtr);
到这里就成功了一条数据的拔出。
总结
insert环节中用到了多个数据结构启动数据的传递。
其中:
insert环节中行记载格局出现了屡次转换。
其中:
拔出流程的详细函数堆栈可以参考文章【InnoDB--insert操作剖析】。
拔出流程可以简化为下图。
其中:
论断
MySQL中有行格局有三种存储方式,包括Server层的格局、索引元组格局(逻辑记载,tuple)、物理存储记载格局(record)。
相似的,数据页分为两种方式,包括物理页(block)、内存页(page)。
经过B+树索引启动查找(lookup)是最为经常出现的操作,详细经过游标成功。游标分为三种类型,包括B-treecursor、pagecursor、persistentcursor。
存取数据时须要启动行格局的转换,要素是IO时经常使用二进制,内存操作时经常使用逻辑记载。
以拔出数据为例,关键流程为:
![数据结构深化浅出](https://tyye.cn/zb_users/theme/tpure/style/images/lazyload.png)
其中数据格局的转换包括:
数据格局转换环节中触及较少数据结构,其中:
待办
参考教程
mysql的innodb数据库引擎详解
一.mysql体系结构和存储引擎1.1、数据库和实例的区别数据库:物理操作系统或其他形式文件类型的集合。 在mysql下数据库文件可以是frm,myd,myi,ibd结尾的文件。 数据库实例:由数据库后台进程/线程以及一个共享内存区组成。 数据库实例才是真正用来操作数据库文件的。 mysql数据库是单进程多线程的程序,与sql server比较类似。 也就是说,Mysql数据库实例在系统上的表现就是一个进程。 1.2、mysql的体系结构mysql由连接池组件、管理服务和工具组件、sql接口组建、查询分析器组件、优化器组件、缓存组件、插件是存储引擎、物理文件。 1.3、mysql存储引擎1.3.1、innodb存储引擎,特点支持外键、行锁、非锁定读(默认情况下读取不会产生锁)、mysql-4.1开始支持每个innodb引擎的表单独放到一个表空间里。 innodb通过使用MVCC来获取高并发性,并且实现sql标准的4种隔离级别,同时使用一种被称成next-key locking的策略来避免换读(phantom)现象。 除此之外innodb引擎还提供了插入缓存(insert buffer)、二次写(double write)、自适应哈西索引(adaptive hash index)、预读(read ahead)等高性能技术。 1.3.2、myisam存储引擎,myisam特点是不支持事物,适合olap应用,myisam表由MYD和MYI组成。 mysql-5.0版本之前,myisam默认支持的表大小为4G,从mysql-5.0以后,myisam默认支持256T的表单数据。 myisam只缓存索引数据。 1.3.3、NDB存储引擎,特点是数据放在内存中,mysql-5.1版本开始可以将非索引数据放到磁盘上。 NDB之前的缺陷是join查询是mysql数据库层完成的,而不是存储引擎完成的,复杂的join查询需要巨大的网络开销,速度很慢。 当前mysql cluster7.2版本中已经解决此问题,join查询效率提高了70倍。 1.3.4、memeory存储引擎,将数据放到内存中,默认使用hash索引,不支持text和blob类型,varchara是按照char的方式来存储的。 mysql数据库使用memory存储引擎作为临时表还存储中间结果集(intermediate result),如果中间集结果大于memorg表的容量设置,又或者中间结果集包含text和blog列类型字段,则mysql会把他们转换到myisam存储引擎表而放到磁盘上,会对查询产生性能影响。 1.3.5、archive存储引擎,压缩能力较强,主要用于归档存储。 1.3.6、federated存储引擎,不存储数据,他指向一台远程mysql数据库上的表。 1.3.7、maria存储引擎,myisam的后续版本,支持缓存数据和索引,行锁设计,支持mvcc,支持事务和非事务安全的选项,以及更好的BLOG字符类型的处理性能。 1.3.8、其他存储引擎,sphinx用于全文索引,infobright用于数据仓库。 1.4连接Mysql1.4.1、TCP/IP:基于网络的连接,连接进行权限检查。 1.4.2、命名管道和共享内存:Windows系统上同一服务器上的两进程可通过命名管道连接,需在配置文件中启用--enable-named-pipe选项。 1.4.3、Unix套接字:客户端与服务端位于同一服务器时才可使用,可以在中指定-socket=/tmp/,连接时指定./mysql -S/tmp/。 二.InnoDB存储引擎2.2、innodb引擎架构InnoDB的多个内存块组成了内存池,负责如下工作:1).维护所有进程/线程需要访问的多个内部数据结构。 2).缓存磁盘上的数据,方便快速的读取,并且在对磁盘文件的数据进行修改之前在这里缓存。 3).重做日志缓存。 后台线程的主要作用是负责刷新内存池中的数据,保证缓冲池中的内存缓存是最近的数据,此外、将已经修改的数据文件刷新到磁盘文件2.2.1、后台线程innodb存储引擎后台有7个线程,—–4个IO线程(insert buffer thread,log thread,read thread,write thread),1个master thread,一个lock监控线程,一个错误监控线程。 2.2.2、内存innodb存储引擎内存由以下三个部分组成:缓冲池(buffer pool),重做日志缓存(redo log buffer),额外的内存池(additional memory pool)。 可以使用 show engine innodb status来查看innodb_buffer_pool的使用情况。 innodb_buffer_pool_size:具体看,缓冲池中的数据库类型有:索引页、数据库页、undo页、插入缓存页(insert buffer)、自适应hash(adaptive hashindex)、innodb存储的锁信息(lock info)、数据字典信息(data dictionary)。 InnoDB工作方式:将数据文件按页(每页16K)读入InnoDBbuffer pool,然后按最近最少使用算法(LRU)保留缓存数据,最后通过一定频率将脏页刷新到文件。 2.3、master thread2.3.1、master thread源码分析2.3.2、master thread的潜在问题1、由于硬件的发展,现在的硬件性能已经提高了很多,如果innodb每秒最大刷新100个脏页,那么效率会很低,为了解决这个问题,innodb plugin提供了一个参数innodb_io_capacity,用来表示磁盘IO的吞吐量,默认值是200,规则如下:在合并插入缓存时,合并插入缓存的数量为innodb_io_capacity的5%;在从缓冲区刷新脏页时,啥新脏页的数量为innodb_io_capacity。 2、关于innodb_max_dirty_pages_pct值的争议,如果值过大,内存也很大或者服务器压力很大,那么效率很降低,如果设置的值过小,那么硬盘的压力会增加,建议是在75-80.并且innodb plugin引进了innodb_adaptive_flushng(自适应的刷新),该值影响每秒刷新脏页的数量。 2.4、关键特性,为innodb提高性能的技术2.4.1、插入缓存当一个表有非聚集索引时,对于非聚集索引的叶子节点的插入不是顺序的,这时候需要离散的访问非聚集索引页,性能就在这里降低了,这是由于b+树的原理导致的。 插入缓存就是用来解决这个问题的。 对于非聚集索引的插入和更新操作,不是每一次都直接插入索引页,而是先判断插入的非聚集索引页是否在缓存中,如果在就直接插入,如果不在就放入到一个插入缓冲区中,好似欺骗数据库这个非聚集索引已经插入到叶子节点了。 然后再以一定的频率插入缓存和非聚集索引页字节点的合并操作。 插入缓存的使用需要满足以下两个条件(也就是非唯一的辅助索引):索引是辅助索引;索引不是唯一的。 2.4.2、两次写两次写给innodb带来的是可靠性,主要用来解决部分写失败(partial page write)。 在应用重做日之前,我们需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,再进行重做,这就是doublewrite。 doublewrite有两部分组成,一部分是内存中的doublewrite buffer,大小为2M,另外一部分就是物理磁盘上的共享表空间中联系的128个页,即两个区,大小同样为2M。 当缓冲池的张也刷新时,并不直接写硬盘,而是回通过memcpy函数将脏页先拷贝到内存中的doublewrite buffer,之后通过doublewrite buffer再分两次写,每次写入1M到共享表空间的物理磁盘上,然后马上调用fsync函数,同步磁盘。 2.4.3、自适应哈西索引由于innodb不支持hash索引,但是在某些情况下hash索引的效率很高,于是出现了 adaptive hash index功能,innodb存储引擎会监控对表上索引的查找,如果观察到建立hash索引可以提高性能的时候,则自动建立hash索引。 2.5、启动、关闭、恢复innodb_fast_shutdown影响InnoDB表关闭。 该参数有0、1、2三个参数。 0 MySQL关闭时 完成所有的full purge和merge insertbuffer操作1默认值 只将缓冲池内的一些脏页刷新至磁盘2将日志都写入日志文件不会有任何事务丢失但下次启动时会进行recoveryinnodb_force_recovery影响整个innodb存储引擎的恢复状况,该值默认为0,表示当需要恢复时,需要执行所有的恢复操作,当不能进行有效恢复时,如数据页发生了corruption,mysql数据库可能宕机,并把错误写入错误日志中。 三.文件3.1参数文件Mysql实例可以不需要参数文件,这是所有的参数值取决于编译Mysql时指定的默认值和源代码中指定参数的默认值。 其参数文件是。 3.1.1、什么是参数参数是一个键/值对。 可以使用show variables like命令查看,也可以通过information_schema的GLOBAL_VARIABLES视图来查找。 3.1.2、参数类型参数文件分为两类:动态参数和静态参数。 动态参数意味着你可以在Mysql实例运行中进行更改;静态参数说明在整个实例生命周期内都不得进行更改,好像是只读的。 对于动态参数,又可以分为global和session关键字,表明该参数的修改是基于当前会话还是真格实例的生命周期。 有些动态参数只能在会话中进行修改,如autocommit;有些参数修改完后,在整个实例生命周期中都会生效,如binlog_cache_size;而有些参数既可以在会话又可以在整个实例的生命周期内生效,如read_buffer_size。 3.2、日志文件3.2.1、错误日志错误日志对Mysql的启动、运行、关闭过程进行了记录。 出现Mysql不能正常启动时,第一个必须查找的文件应该就是错误日志文件。 使用show variables like ‘log_error’来定位文件。 3.2.2、慢查询日志慢查询能为SQL语句的优化带来很好的帮助。 设定一个阀值,将运行时间超过该值的所有SQL语句都记录到慢查询日志文件中。 用参数long_query_time来设置。 另一个参数log_queries_not_using_indexes,若运行的SQL语句没有使用索引,则这条SQL语句会被记录下来。 3.2.3、查询日志查询日志记录了所有对Mysql请求的信息,不论这些请求是否得到正确的执行。 默认文件名为:主机。 3.2.4、二进制日志二进制记录了对数据库执行更改的所有操作,但是不包括SELECT和SHOW操作,还包括了执行时间和更改操作时间。 可用来恢复某些数据,同时也可以用来复制同步远程数据库。 将binlog_format设置成row,可以支持事务隔离级别为READ COMMITTED,以获得更好的并发性。 在使用MIXED格式下,mysql采用STATEMENT格式进行二进制日志文件的记录,但是有一些情况下会使用ROW格式,可能的情况如下:1、表的存储引擎为NDB,这个时候DML操作都会以ROW格式记录。 2、使用了uuid()、user(),current_user(),found_rows(),row_count(),等不确定函数。 3、使用了insert delay语句4、使用了用户定于的函数(UDF)5、使用了临时表(temporary table)注意:针对系统库mysql里面的表发生变化的处理规则如下:1、 如果采用insert,update,delete直接操作表,则日志根据binlog_format设定的格式记录。 2、 如果使用grant,revoke,set password等DCL语句,那么无论如何都会使用SBR模式记录。 3、 blockhole引擎不支持row格式,ndb引擎不支持statement格式。 3.3、套件字文件Unix系统下本地连接Mysql可以采用Unix套接字方法,需要一个套接字文件,可以使用show variableslike ‘socket’查询。 3.4、pid文件和表结构定义文件pid文件是实例启动是记录自己进程ID号的文件,表结构定义文件是以frm为后缀名的文件,还可以用来存放视图的定义。 3.5、innodb引擎文件3.5.1、表空间文件默认表空间文件为ibdata1文件innodb_data_file_path存储数据,innodb_file_per_table可以按表分别产生一个表空间文件,但仅存该表的数据索引和插入缓冲等信息,其他信息如undo信息,系统事务信息,double write buffer等还是存放在默认表空间(ibdata1或表空间组)里。 3.5.2、重做日志文件redo log是在实例或者介质失败的时候,用来保证数据完整性。 每个innodb存储引擎至少有一个重做日志组,每个重做日志文件组下至少又2个重做日志文件,如默认的ib_logfile0、ib_logfile1.为了得到更高的可靠性,你可以设置多个重做镜像日志组。 因为重做日志条目先被写到日志缓冲中,然后根据一定条件刷新到磁盘重做日志文件中。 与redo log相关的就是innodb_flush_log_at_trx_commit的值,对innodb的性能影响很大。 他有0,1,2三个值,0代表提交事务时,并不同步写redo log,而是等master threas每秒写。 1代表commit的时候就将redo log缓存写入磁盘,2代表commit的时候将redo log缓存异步的写入磁盘。
mysql索引的数据结构是什么
一、简介mysql索引的数据结构是树,常用的存储引擎innodb采用的是B+Tree。 这里对B+Tree及其相关的查找树进行简要介绍。 二、各种查找树1、二叉排序树(也称为二叉查找树)二叉排序树是最简单的查找树,特点:a)是一棵二叉树;b)左子树所有结点的值小于它的父结点的值,右子树所有结点的值大于它的父结点的值。 2、平衡二叉树(又称AVL树)平衡二叉树是二叉排序树的基础上,对树的深度进行了限制,从而减少了查找比较的次数,特点:a)是一棵二叉树;b)左子树所有结点的值小于它的父结点的值,右子树所有结点的值大于它的父结点的值;c)左子树与右子树的深度差在-1、0、1内,否则对子树进行旋转调整。 3、B-树(B-Tree)B-树是多路平衡查找树,相对于平衡二叉树,对父结点的直接子结点个数,不再仅限于2,可以指定m(自定义),这样可以在树的深度不大量增加的前提下,保存更多的结点。 B-树是通常在文件系统中使用。 特点:a)树的每个结点最多有m(自定义)子结点;b)若根结点不是叶子结点,则至少有两个子结点;c) 除根结点外的所有非叶子结点,至少有m/2上取整个子结点;d)父结点下的最左边子树所有结点的值均小于父结点最小值,最右边子树所有结点的值均大于父结点最大值,其余中间子树所有结点的值则介于指针的父结点两边的值;e)所有叶子结点都在同一层;注意:所有结点均带有值4、B+树(B+Tree)B+树是B-树变体,相对于B-树,叶子结点的值包含了所有的值,所有父结点的值是重复了叶子结点的值,父结点只起索引查找的作用,同时所叶子结点也也构成了一条有序的链表。 mysql中存储引擎为innodb的索引,采用的数据结构即是B+树。 特点:a)有m个子结点的父结点就有m个关键字;b)所有叶子结点包含了所有关键字(值),且构成由小到大的有序链表;c) 所有非叶子结点起索引作用,结点仅包含子树所有结点的最大值;d)所有叶子结点都在同一层;注意:叶子结点包含了所有的关键字(值)。 5、B*树(B*Tree)B*树是B+树的变体,相对B+树,增加了对同一层非叶子结点的指针,即同一层非叶子结点也构成了一条链表。 三、总结综上,上述各种查找树是相互关联的。 归结到mysql中innodb索引,采用的是B+树,如聚簇索引,是通过主键来聚集数据,采用B+树实现,这即是一种索引,也是mysql的一种数据存储结构,叶子结点包含了所有的数据,非叶子结点仅起索引作用(若没有定义主键,则innodb会隐式定义一个主键来作为聚簇索引)。 更多MySQL的相关技术文章,请访问MySQL教程栏目进行学习!
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。