(称为栈顶(top))
对数据项进行插入和删除。要点:堆,顺序随意。栈,后进先出(Last-In/First-Out)。
针对栈这种数据结构的基本 *** 作有两种:压栈和d出,
在栈帧中包含两个标志----栈底和栈顶,其中栈顶标识着要push或pop
的数据的地址,而栈底则表示栈帧中最后一个数据的内存地址。
在Win32中,寄存器esp存放着栈底指针,栈是向低地址方向生长,
因此esp指向栈顶元素
堆栈对比( *** 作系统):
由编译器自动分配释放,存放函数的参数值,局部变量的值等。其
*** 作方式类似于数据结构中的栈栈使用的是一级缓存,
通常都是被调用时处于存储空间中,调用完毕立即释放
堆( *** 作系统):
一般由程序员分配释放,
若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些
堆(数据结构)
:堆可以被看成是一棵树,如:堆排序
栈(数据结构)
:一种后进先出的的数据结构
具体不同语言有不同的描述,可查看各种语言的api
堆栈是一种执行“后进先出”算法的数据结构。设想有一个直径不大、一端开口一端封闭的竹筒。有若干个写有编号的小球,小球的直径比竹筒的直径略小。现在把不同编号的小球放到竹筒里面,可以发现一种规律:先放进去的小球只能后拿出来,反之,后放进去的小球能够先拿出来。所以“先进后出”就是这种结构的特点。
堆栈就是这样一种数据结构。它是在内存中开辟一个存储区域,数据一个一个顺序地存入(也就是“压入——push”)这个区域之中。有一个地址指针总指向最后一个压入堆栈的数据所在的数据单元,存放这个地址指针的寄存器就叫做堆栈指示器。开始放入数据的单元叫做“栈底”。数据一个一个地存入,这个过程叫做“压栈”。在压栈的过程中,每有一个数据压入堆栈,就放在和前一个单元相连的后面一个单元中,堆栈指示器中的地址自动加1。读取这些数据时,按照堆栈指示器中的地址读取数据,堆栈指示器中的地址数自动减 1。这个过程叫做“d出pop”。如此就实现了后进先出的原则。
官方版本测试到8.0.21依旧存在这个问题,Percona已经修复了,修复如下https://www.percona.com/blog/2018/03/27/analyze-table-is-no-longer-a-blocking-operation/
能力有限简单记录。
问题如下:
乍一看来,很是奇怪,这里没有出现我们经常遇到的flush table/flush table with read lock 堵塞,直接出现了 Waiting for table flush的堵塞,有点像
https://www.jianshu.com/p/b141585cd844
以前记录的文章中的案例2,但是其实并不一样,这里是由于analyze table语句造成的。构造非常简单(必须是社区版本,我使用的8.0.21),如下:
此时堵塞的情形就是Waiting for table flush
analyze table 除了更新我们的统计数据,实际上最后做了一个 *** 作如下(栈):
大概看一下做了什么,如下:
这里判断了是否当前table share正在使用,如果正在使用(很显然我们这个table share是不能直接释放的,因为有select一直持有它)那么将share版本的设置为0(share->clear_version(),实际上这个版本由全局变量refresh_version初始化),目的在于下次如果有使用表定义的时候需要重新打开table share。然后释放了当前没有使用的table cache(类型TDC_RT_REMOVE_UNUSED),如下:
当再次访问表的时候(open_table),会去判断如下是否有老的table share存在,如果存在则需要等待释放:
首先如果存在判断是否存在的老版本,判断是通过table share的版本和当前全局版本refresh_version进行比对,前面我们知道这里table share的版本已经设置为0,因此这里必然进入release_table_share环节,然后等待持有者的释放(案例窗口1的select查询),然后再次获取table share。等待栈如下:
进入waiting for table flush状态
Percona在上文中已经提到问题如下:
一个关键的修改点如下
官方版本:
Percona版本:
如此修改后analyze不会进入tdc_remove_table函数,那么table share的版本不会设置为0。因此如果使用官方版本小心本问题。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)