问题描述:已经在编译选项中加入了-g,但是查看coredump文件时,函数名还是一堆问号,使用的命令为:gdb -c core
解决方案:由于gdb -c core这样的使用在有些系统下支持不是很好,所以推荐用如下两种方法:
1)gdb exe (gdb) core-file core 2)gdb -c core (gdb) file exe
而其中第二种方法在某些系统上也是不好用的,所以就用第一种即可。
问题描述:已经在编译选项中加入了-g,但是查看coredump文件时,函数名还是一堆问号,使用的命令为:gdb -c core
解决方案:由于gdb -c core这样的使用在有些系统下支持不是很好,所以推荐用如下两种方法:
1)gdb exe (gdb) core-file core 2)gdb -c core (gdb) file exe
而其中第二种方法在某些系统上也是不好用的,所以就用第一种即可。
本文链接地址: 打开tcp_tw_recycle引起的一个问题
今天普空说了一个问题就是如果设置了tcp_tw_recycle ,那么如果客户端是NAT出来的,那么就可能会出现连接被直接rst的情况。然后我google了下,在内核列表也有人说了这个问题 https://lkml.org/lkml/2008/11/15/67
The big problem is that both are incompatible with NAT. So if you
ever talk to any NATed clients don’t use it.
源码之前了无秘密,我们来看代码,为什么会出现这种问题,我这里是3.4.4的内核。核心代码是在tcp_v4_conn_request中,这个函数是什么时候被调用呢,是当listen socket收到syn包的时候被调用。直接来看涉及到tw_recycle的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
#define TCP_PAWS_MSL 60 /* Per-host timestamps are invalidated * after this time . It should be equal * (or greater than) TCP_TIMEWAIT_LEN * to provide reliability equal to one * provided by timewait state. */ #define TCP_PAWS_WINDOW 1 /* Replay window for per-host * timestamps. It must be less than * minimal timewait lifetime. /* VJ's idea. We save last timestamp seen * from the destination in peer table, when entering * state TIME-WAIT, and check against it before * accepting new connection request. * * If "isn" is not zero, this request hit alive * timewait bucket, so that all the necessary checks * are made in the function processing timewait state. */ if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle && (dst = inet_csk_route_req(sk, &fl4, req)) != NULL && fl4.daddr == saddr && (peer = rt_get_peer(( struct rtable *)dst, fl4.daddr)) != NULL) { inet_peer_refcheck(peer); if ((u32)get_seconds() - peer->tcp_ts_stamp < TCP_PAWS_MSL && (s32)(peer->tcp_ts - req->ts_recent) > TCP_PAWS_WINDOW) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED); goto drop_and_release; } } |
可以看到当满足下面所有的条件时,这个syn包将会被丢弃,然后释放相关内存,并发送rst。
1 tcp的option有 time stamp字段.
2 tcp_tw_recycle有设置。
3 在路由表中是否存在完全相同的流(如果打开了xfrm的话,还要比较端口,默认xfrm应该是打开的),如果存在则直接返回.
4 并且数据包的源地址和新请求的源地址相同.
5 根据路由表以及源地址能够查找到保存的peer(这个可以看我以前的blog,也就是保存了一些连接统计信息).
6 当前时间(接收到syn)比最后一次的时间(time stamp)小于60秒.
7 已经存在peer的最近一次时间戳要大于当前请求进来的时间戳.
从上面可以看到,上面的条件中1/2都是 server端可以控制的,而其他的条件,都是很容易就满足的,因此我们举个例子。
如果客户端是NAT出来的,并且我们server端有打开tcp_tw_recycle ,并且time stamp也没有关闭,那么假设第一个连接进来,然后关闭,此时这个句柄处于time wait状态,然后很快(小于60秒)又一个客户端(相同的源地址,如果打开了xfrm还要相同的端口号)发一个syn包,此时linux内核就会认为这个数据包异常的,因此就会丢掉这个包,并发送rst。
而现在大部分的客户端都是NAT出来的,因此建议tw_recycle还是关闭,或者说server段关闭掉time stamp(/proc/sys/net/ipv4/tcp_timestamps).
by zhangxinxu from http://www.zhangxinxu.com
本文地址:http://www.zhangxinxu.com/wordpress/?p=3520
我大学时候在图书馆翻过一本很破旧的书,讲生物理论的,主要内容就是探讨生命的产生是偶然还是必然。里面很多亚里士多德都看不懂的公式计算什么的,还有模拟原始地球环境出现了有机物的实验什么的 。总之,书论述的观点是:“在当时的地球环境下,生命的产生是必然的!” 无数次机会的偶然条件、无数次化合物的相遇反应等必定会产生有机物,再有N多偶然,有机物必然形成了有机体……
这种理论类似于,你是个过马路非常小心的人,且你万寿无疆,除了怕被汽车撞。给你100万年的寿命,你最后必然还是被车撞死。
如果以这种理论来看jQuery的出现,结论也应该是必然的!
一个成熟的东西显然不是一口气就出来的,所谓“一铲子挖不了一口井”,我想jQuery的原作者再天才,也是循序渐进过来的,如何个循序渐进法,我想,很有可能就是需求驱动而产生的,好比上图刀削面机器人,据说现在已经第八代了!
1. gelElementById太长了
页面上有个按钮,还有个图片,我想点击按钮图片隐藏,如下HTML:
<button id="button">点击我</button> <img id="image" src="xxx.jpg">
于是,我的脚本可能就这样:
var button = document.getElementById("button") , image = document.getElementById("image") button.onclick = function() { image.style.display = "none"; };
有何问题?人几乎都是天生的“懒惰者”,document.getElementById
名称长且重复出现,好像到了公司发现卡没带又回家重新拿卡的感觉,我希望越简单越好。恩, 我很喜欢钱,
$
这个符号我很喜欢,我打算改造一番,简化我的工作:
var $ = function(id) { return document.getElementById(id); }; <span style="color: #cd0000;">$("button")</span>.onclick = function() { <span style="color: #cd0000;">$("image")</span>.style.display = "none"; };
这里的$()
就是最简单的包装器,只是返回的是原生的DOM对象。
2. 我需要一个简洁的暗号,就像“芝麻开门”
后来页面复杂了,点击一个按钮,有2张图片要隐藏。
$("button").onclick = function() { $("image1").style.display = "none"; $("image2").style.display = "none"; };
好像又看见长长的重复的东西,xxx.style.display = "none"
, 为什么每次开门都要从包里找到钥匙、对准插口插进去、还要左扭扭右扭扭呢?一次还好,天天经常老是这样怎受得了。设想,要是有个芝麻开门的暗号就好了,“open开”,声音识别,门自动开了,多省心。
这里每次隐藏都要xxx.style.display = "none"
, 比每天拿钥匙开门还要烦,我希望有一个快捷的方式,例如,“hide隐”,语句识别,元素自动隐藏,多省心。
就是要变成下面的效果:
$("button").onclick = function() { $("image1")<span style="color: #cd0000;">.hide()</span>; $("image2")<span style="color: #cd0000;">.hide()</span>; };
3. 如何识别“芝麻开门”的暗号
$("image1")
本质是个DOM元素,$("image1").hide()
也就是在DOM元素上扩展一个hide
方法,调用即隐藏。
哦,扩展,立马想到了JS中的prototype
原型。//zxx: 老板,现在满大街什么菜最便宜。老板:原型啊,都泛滥了!
<span style="color: #cd0000;">HTMLElement.prototype</span>.hide = function() { this.style.display = "none"; };
上面代码的demo地址应该不会被人看到吧……
虽然在身体上钻了个窟窿插进入了一个方法,毕竟浏览器有有效果啊,切肤之痛就不算什么了。但是,我们是在泱泱天朝,很多IE6~IE8老顽固,这些老东西不认识HTMLElement
,对于HTMLElement
自残扩展之法根本理解不了,而这些老家伙掌管了半壁江山。唉,面对现实,元素直接扩展是行不通了。
因此,由于兼容性,我们需要想其他扩展方法。
4. 条条大路通罗马,此处不留爷,自有留爷处
虽IE6~IE8不识HTMLElement
原型扩展,但是,Function
的原型扩展其认识啊。管不管用,暂时不得而知,先随便搞个简单的试试呗~
var F = function() {}; F.prototype.hide = function() { this?.style.display = "none"; }; new F().hide(); <span style="color: green;">// 这个实现隐藏?</span>
本文至少还有一半的内容,但是,全文的最难点就在这里的,对new F()
的认识和理解。
上面的代码,new F()您可以看做是this?.style
这里的this
. 您可能会跳起来抢答道:“那new F()
的return
值 = DOM元素
不就完事OK啦!—— this.style.hide
= new F().style.hide
= DOM.style.hide
”!
只要
new
表达式之后的constructor
返回(return
)一个引用对象(数组,对象,函数等),都将覆盖new创建的匿名对象,如果返回(return
)一个原始类型(无return
时其实为return
原始类型undefined
),那么就返回new
创建的匿名对象。
上面的引用来自这里。什么意思呢?说白了就是,new F()
如果没有返回值(Undefined
类型),或返回值是5种基本型(Undefined
类型、Null
类型、Boolean
类型、Number
类型、String
类型)之一,则new F()
我们可以看成是原型扩展方法中的this
; 如果返回是是数组啊、对象啊什么的,则返回值就是这些对象本身,此时new F()
≠ this
。
举例说明:
var F = function(id) { return document.getElementById(id); }; new F("image1") == document.getElementById("image1"); <span style="color: green;">// true 说明看上去返回DOM对象,实际确实就是DOM对象</span>
var F = function(id) { return id; }; new F("image1") == "image1"; <span style="color: green;">// false 说明看上去返回字符串值,实际并不是字符串</span>
回到上面天真的想法。要想使用prototype.hide
方法中的this
, 偶们就不能让F
函数有乱七八糟的返回值。
因此,new F()
直接返回DOM是不可取的,但我们可以借助this
间接调用。比方说:
var F = function(id) { <span style="color: #cd0000;">this.element</span> = document.getElementById(id); }; F.prototype.hide = function() { <span style="color: #cd0000;">this.element.style.display</span> = "none"; }; new F("image").hide(); <span style="color: green;">// 看你还不隐藏</span>
上面代码的demo地址应该不会被人看到吧……
5. 我不喜欢太暴露
F()
中的this.element
实际上将element
这个属性直接暴露在了new F("image")
上!
new F("image").hasOwnProperty("element"); <span style="color: green;">// true</span>
太暴露了,我不喜欢~~
如何隐藏?代码如下:
var F = function(id) { return this.getElementById(id); }; F.prototype.getElementById = function(id) { <span style="color: #cd0000;">this.element</span> = document.getElementById(id); return <span style="color: #cd0000;">this</span>; }; F.prototype.hide = function() { this.element.style.display = "none"; }; new F("image").hide(); <span style="color: green;">// 看你还不隐藏</span>
元素获取方法放在prototype
上,通过F()
执行。你可能会奇怪了,你刚明明说“new F()
直接返回DOM是不可取的”,怎么现在又有return
呢?大家务必擦亮眼睛,F.prototype.getElementById
的返回值是this
,也就是new F()
的返回值是this
. 形象点就是new F("image")
出了一拳,又反弹到自己脸上了。
于是乎,现在就没有直接暴露的API了。
上面代码的demo地址应该不会被人看到吧……
6. 我不喜欢new, 我喜欢$
new F("image")
这种写法我好不喜欢,我喜欢$
, 我就是喜欢$
, 我要换掉。
好吧,把new
什么什么藏在$
方法中把~
var $ = function(id) { return new F(id); };
于是,上面的图片隐藏的直接执行代码就是:
$("image").hide();
上面代码的demo地址应该不会被人看到吧……
IE6浏览器也是支持的哦!是不是已经有些jQuery的样子啦!
7. 你怎么就一种姿势啊,人家都腻了诶
循序渐进到现在,都是拿id
来举例的,实际应用,我们可能要使用类名啊,标签名啊什么的,现在,为了接下来的继续,有必要支持多个“姿势”。
在IE8+浏览器中,我们有选择器API,document.querySelector
与document.querySelectorAll
,前者返回唯一Node
,后者为NodeList
集合。大统一起见,我们使用后者。于是,就有:
var F = function(selector, context) { return this.getNodeList(selector, context); }; F.prototype.getNodeList = function(selector, context) { context = context || document; this.element = context.<span style="color: #cd0000;">querySelectorAll</span>(selector); return this; }; var $ = function(selector, context) { return new F(selector, context); };
此时,我们就可以使用各种选择器了,例如,$("body #image")
, this.element
就是选择的元素们。
8. IE6/IE7肿么办?
IE6/IE7不认识querySelectorAll
,咋办?
jQuery就使用了一个比较强大的选择器框架-Sizzle
. 知道就好,重在演示原理,因此,下面还是使用原生的选择器API示意,故demo效果需要IE8+浏览器下查看。
8. 遍历是个麻烦事
this.element
此时类型是NodeList
, 因此,直接this.element.style.xxx
的做法一定是报错,看来有必要循环下:
F.prototype.hide = function() { var i=0, length = this.element.length; for (; i<length; i+=1) { this.element[i].style.display = "none"; } };
于是乎:
$("img").hide(); <span style="color: green;">// 页面所有图片都隐藏啦!</span>
上面代码的demo地址应该不会被人看到吧……
单纯一个hide
方法还可以应付,再来个show
方法,岂不是还要循环遍历一次,岂不是要烦死~
因此,急需一个遍历包装器元素的方法,姑且叫做each
吧~
于是有:
F.prototype<span style="color: #cd0000;">.each</span> = function(fn) { var i=0, length = this.element.length; for (; i<length; i+=1) { fn.call(this.element[i], i, this.element[i]); } return this; }; F.prototype.hide = function() { this<span style="color: #cd0000;">.each</span>(function() { this.style.display = "none"; }); }; $("img").hide(); <span style="color: green;">// 页面所有图片都隐藏啦!</span>
上面代码的demo地址应该不会被人看到吧……
9. 我不喜欢this.element, 可以去掉吗?
现在包装器对象结构类似这样:
F.prototype = { element: [NodeList], each: function() {}, hide: function() {} }
element
看上去好碍眼,就不能去掉吗?可以啊,宝贝,NodeList
是个类数组结构,我们把它以数值索引形式分配到对象中就好啦!一来去除冗余element
属性,二来让原型对象成为类数组结构,可以有一些特殊的功能。
于是,F.prototype.getNodeList
需要换一个名字了,比方说初始化init
, 于是有:
F.prototype<span style="color: #cd0000;">.init</span> = function(selector, context) { var nodeList = (context || document).querySelectorAll(selector); <span style="color: #cd0000;">this.length</span> = nodeList.length; for (var i=0; i<this.length; i+=1) { <span style="color: #cd0000;">this[i]</span> = nodeList[i]; } return this; };
此时,each
方法中,就没有烦人碍眼的this.element[i]
出现了,而是直接的this[i]
.
F.prototype.each = function(fn) { var i=0, length = <span style="color: #cd0000;">this.length</span>; for (; i<length; i+=1) { fn.call(<span style="color: #cd0000;">this[i]</span>, i, <span style="color: #cd0000;">this[i]</span>); } return this; };
我们也可以直接使用索引访问包装器中的DOM元素。例如:$("img")[0]
就是第一张图片啦!
上面代码的demo地址应该不会被人看到吧……
10. 我是完美主义者,我特不喜欢F名称,可以换掉吗?
F
这个名称从头到尾出现,我好不喜欢的来,我要换成$
, 我就是要换成$
符号……
这个……$
已经用了啊,再用冲突的吧。再说,你又不是狐后,耍无赖也没用啊……
好吧,想想其他办法吧。一步一步来,那我把所有的
F
换成$.fn
.
就有:
上图代码的demo地址应该不会被人看到吧……
显然,运行是OK的。似乎也非常有jQuery的模样了,但是,实际上,跟jQuery比还是有差别的,有个较大的差别。如果是上图代码所示的JS结构,则包装器对象要扩展新方法,每个都需要再写一个原型的。例如,扩展一个attr
方法,则要写成:
$.fn.prototype.attr = function() { <span style="color: green;">// ...</span> };
又看到prototype
了,高级的东西应该要隐藏住,否则会给人难以上手的感觉。那该怎么办呢?御姐不是好惹的。
脑子动一下就知道了,把F.prototype
换成$.fn
不久好了。这样,扩展新方法的时候,直接就是
$.fn.attr = function() { <span style="color: green;">// ...</span> };
至此,就使用上讲,与jQuery非常接近了。 但是,还有几个F
怎么办呢,总不能就像下面这样放着吧:
var $ = function(selector, context) { return new F(selector, context); }; var F = function(selector, context) { return this.init(selector, context); }; $.fn = F.prototype; $.fn.init = function(selector, context) { <span style="color: green;">// ...</span> return this; }; $.fn.each = function(fn) { <span style="color: green;">// ...</span> }; $.fn.hide = function() { <span style="color: green;">// ...</span> };
数学中,我们都学过合并同类项。仔细观察上面的的代码:
$()
返回的是new F()
,而new F()
又是返回的对象的引用。擦,这返回来返回去的,参数又是一样的,我们是不是可以一次性返回,然后再做些手脚,让$.fn.init
返回的this
依然能够正确指向。
于是,一番调整有:
var $ = function(selector, context) { return new $.fn.init(selector, context); }; var F = function() { }; $.fn = F.prototype; $.fn.init = function(selector, context) { <span style="color: green;">// ...</span> return this; }; <span style="color: green;">// ...</span>
上面代码显然是有问题的,new
的是$.fn.init
, $.fn.init
的返回值是this
. 也就是$()
的返回值是$.fn.init
的原型对象,尼玛$.fn.init
的prototype
原型现在就是个光杆司令啊,哟,正好,$.fn
对应的原型方法,除了init没用外,其他hide()
, each()
就是我们需要的。因此,我们需要加上这么一行:
$.fn.init.prototype = $.fn
于是,$()
的返回值从$.fn.init.prototype
一下子变成$.fn
,正好就是我们一开始的扩展方法。
于是乎,大功告成。慢着……
上面明明还有残留的F
呢!
哦,那个啊。
F
是任意函数,$
本身就是函数,因此,直接使用$
替换就可以了:
var $ = function(selector, context) { return new $.fn.init(selector, context); }; <del datetime="2013-07-17T03:12:44+00:00">var F = function() { };</del> // 这个直接删除 $.fn = <span style="color: #cd0000;">$</span>.prototype; $.fn.init = function(selector, context) { <span style="color: green;">// ...</span> return this; }; <span style="color: green;">// ...</span>
上图代码的demo地址应该不会被人看到吧……
实际上,如果你不是非得一个$
行便天下的话,到了上面进阶第9步就足够了。jQuery在第10步的处理是为了彰显其$
用得如此的出神入化,代码完美,令人惊叹!
至此,jQuery大核心已经一步一步走完了,可以看到,所有的这些进阶都是根据需求、实际开发需要来的,慢慢完善,慢慢扩充的!
11. 每个扩展方法都要$.fn.xxxx, 好闹心的来
$.fn.css = function() {} $.fn.attr = function() {} $.fn.data = function() {} <span style="color: green;">// ...</span>
每个扩展前面都有个$.fn
, 好讨厌的感觉,就不能合并吗?
于是,jQuery搞了个extend
方法。
$.fn.extend({ css: function() {}, attr: function() {}, data: function() {}, <span style="color: green;">// ...</span> });
12. $()不仅可以是选择器字符串,还可以是DOM
在init
方法中,判断第一个参数,如果是节点,直接this[0] = this_node
. over!
以下13~?都是完善啊,补充啊,兼容性处理啊什么的,没有价值,到此为止!
网上也有其他一些介绍jQuery原理或机制的文章,可能当事人自己理解,而阅读者本来就不懂,说来说去,越说越绕,可能更不懂了。
jQuery是很优秀,好比身为灵长类的人类。但是,其诞生显然是从简单开始的。因此,要了解人类,可以通过追溯其起源。如果你是上帝,要让你造一个人,你会怎么造,是一口气出来?女娲造人还要捏泥人呢!不妨从单细胞生物开始,随着自然进化,淘汰,自然而然,就会出现人类,上帝他就是这么干的。
jQuery的诞生也大致如此,要想了解jQuery,可以试试踏着本文jQuery的成长足迹,一点一点逐步深入,您就会了解为何jQuery要这么设计,它是如何设计的等。
虽然,内容由浅及深,但是,其中涉及的原型以及new
构造函数的一些特性,对于新人而言,还是有一些理解门槛的,希望我的描述与解释可以让你有一丝豁然开朗,那就再好不过了。
感谢您的阅读至此,欢迎指出文章可能书写不准确的地方,再次感谢!
月底在百姓网有个小分享,演示文档连个肉渣子还没准备呢。因此,未来一周休文。
原创文章,转载请注明来自张鑫旭-鑫空间-鑫生活[http://www.zhangxinxu.com]
本文地址:http://www.zhangxinxu.com/wordpress/?p=3520
(本篇完)
From: https://blogs.oracle.com/mysqlinnodb/entry/introduction_to_transaction_locks_in
Introduction
Transaction locks are an important feature of any transactional storage engine. There are two types of transaction locks – table locks and row locks. Table locks are used to avoid a table being altered or dropped by one transaction when another transaction is using the table. It is also used to prohibit a transaction from accessing a table, when it is being altered. InnoDB supports multiple granularity locking (MGL). So to access rows in a table, intention locks must be taken on the tables.
Row locks are at finer granularity than table level locks, different threads can work on different parts of the table without interfering with each other. This is in contrast with MyISAM where the entire table has to be locked when updating even unrelated rows. Having row locks means that multiple transactions can read and write into a single table. This increases the concurrency level of the storage engine. InnoDB being an advanced transactional storage engine, provides both table and row level transaction locks.
This article will provide information about how transaction locks are implemented in InnoDB storage engine. The lock subsystem of InnoDB provides many services to the overall system, like:
The lock subsystem helps to isolate one transaction from another transaction. This article will provide information about how transaction locks are created, maintained and used in the InnoDB storage engine. All reference to locks means transaction locks, unless specified otherwise.
Internal Data Structures of InnoDB
Before we proceed with scenarios and algorithms, I would like to present the following data structures. We need to be familiar with these data structures to understand how transaction locks work in InnoDB. The data structures of interest are:
The Lock Modes
The locks can be obtained in the following modes. I’ll not discuss about the LOCK_AUTO_INC in this article.
/* Basic lock modes */
enum lock_mode {
LOCK_IS = 0, /* intention shared */
LOCK_IX, /* intention exclusive */
LOCK_S, /* shared */
LOCK_X, /* exclusive */
LOCK_AUTO_INC, /* locks the auto-inc counter of a table
in an exclusive mode */
LOCK_NONE, /* this is used elsewhere to note consistent read */
LOCK_NUM = LOCK_NONE, /* number of lock modes */
LOCK_NONE_UNSET = 255
};
The lock struct or lock object
The structure lock_t represents either a table lock (lock_table_t) or a group of row locks (lock_rec_t) for all the rows belonging to the same page. For different lock modes, different lock structs will be used. For example, if a row in a page is locked in LOCK_X mode, and if another row in the same page is locked in LOCK_S mode, then these two row locks will be held in different lock structs.
This structure is defined as follows:
struct lock_t {
trx_t* trx;
ulint type_mode;
hash_node_t hash;
dict_index_t* index;
union {
lock_table_t tab_lock;/*!< table lock */
lock_rec_t rec_lock;/*!< record lock */
} un_member; /*!< lock details */
};
/** A table lock */
struct lock_table_t {
dict_table_t* table; /*!< database table in dictionary
cache */
UT_LIST_NODE_T(lock_t)
locks; /*!< list of locks on the same
table */
};
/** Record lock for a page */
struct lock_rec_t {
ulint space; /*!< space id */
ulint page_no; /*!< page number */
ulint n_bits; /*!< number of bits in the lock
bitmap; NOTE: the lock bitmap is
placed immediately after the
lock struct */
};
The important point here is the lock bitmap. The lock bitmap is a space efficient way to represent the row locks. This space efficient way of representing the row locks avoids the need for lock escalation and lock data persistence. (Note: For prepared transactions, it would be useful to have lock data persistence, but InnoDB currently do not support lock data persistence.)
The lock bitmap is placed immediately after the lock struct object. If a page can contain a maximum of 100 records, then the lock bitmap would be of size 100 (or more). Each bit in this bitmap will represent a row in the page. The heap_no of the row is used to index into the bitmap. If the 5th bit in the bitmap is enabled, then the row with heap_no 5 is locked.
The Transaction And Lock Relationship
The struct trx_t is used to represent the transaction within InnoDB. The struct trx_lock_t is used to represent all locks associated to a given transaction. Here I have listed down only those members relevant to this article.
struct trx_t {
trx_id_t id; /*!< transaction id */
trx_lock_t lock; /*!< Information about the transaction
locks and state. Protected by
trx->mutex or lock_sys->mutex
or both */
};
struct trx_lock_t {
ib_vector_t* table_locks; /*!< All table locks requested by this
transaction, including AUTOINC locks */
UT_LIST_BASE_NODE_T(lock_t)
trx_locks; /*!< locks requested
by the transaction;
insertions are protected by trx->mutex
and lock_sys->mutex; removals are
protected by lock_sys->mutex */
};
Global hash table of row locks
Before we look at how the row locks internally works, we need to be aware of this global data structure. The lock subsystem of the InnoDB storage engine has a global object lock_sys of type lock_sys_t. The class lock_sys_t is defined as follows:
struct lock_sys_t {
ib_mutex_t mutex;
hash_table_t* rec_hash;
ulint n_lock_max_wait_time;
// more …
};
The lock_sys_t::rec_hash member is the hash table of the record locks. Every page within InnoDB is uniquely identified by the (space_id, page_no) combination, called the page address. The hashing is done based on the page address. So given the page address, we can locate the list of lock_t objects of that page. All lock structs of the given page will be in the same hash bucket. So using this mechanism we can locate the row lock of any row.
The Transaction Subsystem Global Object
The transaction subsystem of InnoDB has one global object trx_sys of type trx_sys_t, which is an important internal data structure. It is defined as follows:
/** The transaction system central memory data structure. */
struct trx_sys_t{
// more …
trx_list_t rw_trx_list; /*!< List of active and committed in
memory read-write transactions, sorted
on trx id, biggest first. Recovered
transactions are always on this list. */
trx_list_t ro_trx_list; /*!< List of active and committed in
memory read-only transactions, sorted
on trx id, biggest first. NOTE:
The order for read-only transactions
is not necessary. We should exploit
this and increase concurrency during
add/remove. */
// more …
};
This global data structure contains the active transaction table (ATT) of InnoDB storage engine. In the following sections, I’ll use this global object to access my transaction object through the debugger to demonstrate the transaction locks.
The table descriptor (dict_table_t)
Each table in InnoDB is uniquely identified by its name in the form of databasename/tablename. For each table, the data dictionary of InnoDB will contain exactly one table descriptor object of type dict_table_t. Given the table name, the table descriptor can be obtained. This table descriptor contains a list of locks on the table. This list can be used to check if the table has already been locked by a transaction.
struct dict_table_t{
// more …
table_id_t id; /*!< id of the table */
char* name; /*!< table name */
UT_LIST_BASE_NODE_T(lock_t)
locks; /*!< list of locks on the table; protected
by lock_sys->mutex */
};
The heap_no of a row
Every row in InnoDB is uniquely identified by space_id, page_no and heap_no of the row. I assume that you know about space_id and page_no. I’ll explain here only about the heap_no of the row. Each row in the page has a heap_no. The heap_no of the infimum record is 0, the heap_no of the supremum record is 1, and the heap_no of the first user record in page is 2.
If we have inserted 10 records in the page, and if there has been no updates on the page, then the heap_no of the user records would be 2, 3, 4, 5 … 10, 11. The heap_no will be in the same order in which the records will be accessed in ascending order.
The heap_no of the row is used to locate the bit in the lock bitmap corresponding to the row. If the row has a heap_no of 10, then the 10th bit in the lock bitmap corresponds to the row. This means that if the heap_no of the row is changed, then the associated lock structs must be adjusted accordingly.
The Schema
To demonstrate the transaction locks, I use the following schema in this article. There is one table t1 which has 3 rows (1, ‘அ’), (2, ‘ஆ’) and (3, ‘இ’). In this article, we will see when and how the transaction locks (table and row) are created and used. We will also cover the internal steps involved in creating table and row locks.
set names utf8;
drop table if exists t1;
create table t1 (f1 int primary key, f2 char(10)) charset=’utf8′;
insert into t1 values (1, ‘அ’), (2, ‘ஆ’), (3, ‘இ’);
Table Level Transaction Locks
The purpose of table level transaction locks or simply table locks is to ensure that no transactions modify the structure of the table when another transaction is accessing or modifying the table or the rows in a table. There are two types of table locks in MySQL – one is the meta-data locking (MDL) provided by the SQL engine and the other is the table level locks within InnoDB storage engine. Here the discussion is only about the table level locks within InnoDB.
On tables, InnoDB normally acquires only intentional shared (LOCK_IS) or intentional exclusive (LOCK_IX) modes. It does not lock the tables in shared mode (LOCK_S) or exclusive mode (LOCK_X) unless explicitly requested via LOCK TABLES command. One exception to this is in the prepare phase of the online alter table command, where the table is locked in shared mode. Please refer to Multiple Granularity Locking to know about intention shared and intention exclusive locks.
Scenario (௧) for LOCK_IS table lock
Here is an example scenario that will take intention shared (LOCK_IS) lock on the table.
mysql> set transaction isolation level serializable;
mysql> start transaction;
mysql> select * from t1;
When the above 3 statements are executed, one table lock would be taken in LOCK_IS mode. In mysql-5.6, there is no way to verify this other than using the debugger. I verified it as follows:
(gdb) set $trx_locklist = trx_sys->rw_trx_list->start->lock->trx_locks
(gdb) p $trx_locklist.start->un_member->tab_lock->table->name
$21 = 0x7fffb0014f70 “test/t1”
(gdb) p lock_get_mode(trx_sys->rw_trx_list->start->lock->trx_locks->start)
$25 = LOCK_IS
(gdb)
You need to access the correct transaction object and the lock object.
Scenario (௨) for LOCK_IX table lock
Here is an extension to the previous scenario. Adding an INSERT statement to the scenario will take an intention exclusive (LOCK_IX) lock on the table.
mysql> set transaction isolation level serializable;
mysql> start transaction;
mysql> select * from t1;
mysql> insert into t1 values (4, ‘ஃ’);
When the above 4 statements are executed, there would be two locks on the table – LOCK_IS and LOCK_IX. The select statement would have taken the table lock in LOCK_IS mode and the INSERT statement would take the table lock in LOCK_IX mode.
This can also be verified using the debugger. I’ll leave it as an exercise for the reader.
What Happens Internally When Acquiring Table Locks
Each table is uniquely identified within the InnoDB storage engine using the table descriptor object of type dict_table_t. In this section we will see the steps taken internally by InnoDB to obtain a table lock. The function to refer to is lock_table() in the source code. Necessary mutexes are taken during these steps to ensure that everything works correctly in a multi-thread environment. This aspect is not discussed here.
You can re-visit the above scenario (௨) and then follow the above steps and verify that the number of lock structs created is 2.
Row Level Transaction Locks
Each row in the InnoDB storage engine needs to be uniquely identified in order to be able to lock it. A row is uniquely identified by the following pieces of information:
There are two types of row locks in InnoDB – the implicit and the explicit. The explicit row locks are the locks that make use of the global row lock hash table and the lock_t structures. The implicit row locks are logically arrived at based on the transaction information in the clustered index or secondary index record. These are explained in the following sections.
Implicit Row Locks
Implicit row locks do not have an associated lock_t object allocated. This is purely calculated based on the ID of the requesting transaction and the transaction ID available in each record. First, let use see how implicit locks are “acquired” (here is comment from lock0lock.cc):
“If a transaction has modified or inserted an index record, then it owns an implicit x-lock on the record. On a secondary index record, a transaction has an implicit x-lock also if it has modified the clustered index record, the max trx id of the page where the secondary index record resides is >= trx id of the transaction (or database recovery is running), and there are no explicit non-gap lock requests on the secondary index record. ”
As we can see, the implicit locks are a logical entity and whether a transaction has implicit locks or not is calculated using certain procedures. This procedure is explained here briefly.
If a transaction wants to acquire a row lock (implicit or explicit), then it needs to determine whether any other transaction has an implicit lock on the row before checking on the explicit lock. As explained above this procedure is different for clustered index and secondary index.
For the clustered index, get the transaction id from the given record. If it is a valid transaction id, then that is the transaction which is holding the implicit exclusive lock on the row. Refer to the function lock_clust_rec_some_has_impl() for details.
For the secondary index, it is more cumbersome to calculate. Here are the steps:
In the case of secondary indexes, we need to make use of the undo logs to determine if any transactions have an implicit exclusive row lock on record. Refer to the function lock_sec_rec_some_has_impl() for details.
Also note that the implicit row locks do not affect the gaps.
Explicit Row Locks
Explicit row locks are represented by the lock_t structures. This section provides some scenarios in which explicit row locks are obtained in different modes. It also briefly discusses the internal procedure to acquire an explicit row lock.
Scenario (௩) for LOCK_S row lock
For transaction isolation level REPEATABLE READ and less stronger levels, InnoDB does not take shared locks on rows. So in the following scenario, we use SERIALIZABLE transaction isolation level for demonstrating shared row locks:
mysql> set transaction isolation level serializable;
mysql> start transaction;
mysql> select * from t1;
This scenario is the same as the Scenario (௧). The verification via debugger alone is different. In the case of row locks, each lock object represents a row lock for all rows of a page (of the same lock mode). So a lock bitmap is used for this purpose which exists at the end of the lock struct object. In the above scenario, we can verify the locks as follows:
The lock_t object allocated for the row locks is
(gdb) set $trx_locklist = trx_sys->rw_trx_list->start->lock->trx_locks
(gdb) set $rowlock = $trx_locklist.start->trx_locks->next
(gdb) p $rowlock
$47 = (ib_lock_t *) 0x7fffb00103f0
Verify the lock mode of the lock object.
(gdb) p lock_get_mode($rowlock)
$48 = LOCK_S
(gdb)
Verify that it is actually a lock struct object allocated for rows (and not a table lock).
(gdb) p *$rowlock
$43 = {trx = 0x7fffb0013f78, trx_locks = {prev = 0x7fffb00103a8, next = 0x0},
type_mode = 34, hash = 0x0, index = 0x7fffb001a6b8, un_member = {tab_lock = {
table = 0xc, locks = {prev = 0x3, next = 0x48}}, rec_lock = {space = 12,
page_no = 3, n_bits = 72}}}
(gdb)
The row lock bit map is at the end of the lock object. Even though the number of bits in the lock bitmap is given as n_bits = 72 (9 bytes) above, I am examining only 4 bytes here.
(gdb) x /4t $rowlock + 1
0x7fffb0010438: 00011110 00000000 00000000 00000000
(gdb)
Note that there are 4 bits enabled. This means that there are 4 row locks obtained. The row in the page, and the bit in the lock bit map are related via the heap_no of the row, as described previously.
Scenario (௪) for LOCK_X row lock
Changing the above scenario slightly as follows, will obtain LOCK_X locks on the rows.
mysql> set transaction isolation level serializable;
mysql> start transaction;
mysql> select * from t1 for update;
Verification of this through the debugger is left as an exercise for the reader.
What Happens Internally When Acquiring Explicit Row Locks
To lock a row, all information necessary to uniquely identify the row – the trx, lock mode, table space id, page_no and heap_no – must be supplied. The entry point for row locking is the lock_rec_lock() function in the source code.
Releasing transaction locks
All the transaction locks acquired by a transaction is released by InnoDB at transaction end (commit or rollback). In the case of REPEATABLE READ isolation level or SERIALIZABLE, it is not possible for the SQL layer to release locks before transaction end. In the case of READ COMMITTED isolation level, it is possible for the SQL layer to release locks before transaction end by making use of ha_innobase::unlock_row() handler interface API call.
To obtain the list of all transaction locks of a transaction, the following member can be used – trx_t::trx_lock_t::trx_locks.
Conclusion
This article provided an overview of the transaction locks in InnoDB. We looked at the data structures used to represent transaction locks. We analysed some scenarios in which the various locks are created. We also looked at the internal steps that happen when a table lock or a row lock is created.
You can download this article in pdf.
From: https://blogs.oracle.com/mysqlinnodb/entry/data_organization_in_innodb
This article will explain how the data is organized in InnoDB storage engine. First we will look at the various files that are created by InnoDB, then we look at the logical data organization like tablespaces, pages, segments and extents. We will explore each of them in some detail and discuss about their relationship with each other. At the end of this article, the reader will have a high level view of the data layout within the InnoDB storage engine.
MySQL will store all data within the data directory. The data directory can be specified using the command line option –data-dir or in the configuration file as datadir. Refer to the Server Command Options for complete details.
By default, when InnoDB is initialized, it creates 3 important files in the data directory – ibdata1, ib_logfile0 and ib_logfile1. The ibdata1 is the data file in which system and user data will be stored. The ib_logfile0 and ib_logfile1 are the redo log files. The location and size of these files are configurable. Refer to Configuring InnoDB for more details.
The data file ibdata1 belongs to the system tablespace with tablespace id (space_id) of 0. The system tablespace can contain more than 1 data file. As of MySQL 5.6, only the system tablespace can contain more than 1 data file. All other tablespaces can contain only one data file. Also, only the system tablespace can contain more than one table, while all other tablespaces can contain only one table.
The data files and the redo log files are represented in the memory by the C structure fil_node_t.
By default, InnoDB contains only one tablespace called the system tablespace whose identifier is 0. More tablespaces can be created indirectly using the innodb_file_per_table configuration parameter. In MySQL 5.6, this configuration parameter is ON by default. When it is ON, each table will be created in its own tablespace in a separate data file.
The relationship between the tablespace and data files is explained in the InnoDB source code comment (storage/innobase/fil/fil0fil.cc) which is quoted here for reference:
“A tablespace consists of a chain of files. The size of the files does not have to be divisible by the database block size, because we may just leave the last incomplete block unused. When a new file is appended to the tablespace, the maximum size of the file is also specified. At the moment, we think that it is best to extend the file to its maximum size already at the creation of the file, because then we can avoid dynamically extending the file when more space is needed for the tablespace.”
The last statement about avoiding dynamic extension is applicable only to the redo log files and not the data files. Data files are dynamically extended, but redo log files are pre-allocated. Also, as already mentioned earlier, only the system tablespace can have more than one data file.
It is also clearly mentioned that even though the tablespace can have multiple files, they are thought of as one single large file concatenated together. So the order of files within the tablespace is important.
A data file is logically divided into equal sized pages. The first page of the first data file is identified with page number of 0, and the next page would be 1 and so on. A page within a tablespace is uniquely identified by the page identifier or page number (page_no). And each tablespace is uniquely identified by the tablespace identifier (space_id). So a page is uniquely identified throughout InnoDB by using the (space_id, page_no) combination. And any location within InnoDB can be uniquely identified by the (space_id, page_no, page_offset) combination, where page_offset is the number of bytes within the given page.
How the pages from different data files relate to one another is explained in another source code comment: “A block’s position in the tablespace is specified with a 32-bit unsigned integer. The files in the chain are thought to be catenated, and the block corresponding to an address n is the nth block in the catenated file (where the first block is named the 0th block, and the incomplete block fragments at the end of files are not taken into account). A tablespace can be extended by appending a new file at the end of the chain.” This means that the first page of all the data files will not have page_no of 0 (zero). Only the first page of the first data file in a tablespace will have the page_no as 0 (zero).
Also in the above comment it is mentioned that the page_no is a 32-bit unsigned integer. This is the size of the page_no when stored on the disk.
Every page has a page header (page_header_t). For more details on this please refer to the Jeremy Cole’s blog “The basics of InnoDB space file layout.”
An extent is 1MB of consecutive pages. The size of one extent is defined as follows (1048576 bytes = 1MB):
#define FSP_EXTENT_SIZE (1048576U / UNIV_PAGE_SIZE)
The macro UNIV_PAGE_SIZE used to be a compile time constant. From mysql-5.6 onwards it is a global variable. The number of pages in an extent depends on the page size used. If the page size is 16K (the default), then an extent would contain 64 pages.
One page can be used for many purposes. The page type will identify the purpose for which the page is being used. The page type of each page will be stored in the page header. The page types are available in the header file storage/innobase/include/fil0fil.h. The following table provides a brief description of the page types.
Page Type | Description |
---|---|
FIL_PAGE_INDEX | The page is a B-tree node |
FIL_PAGE_UNDO_LOG | The page stores undo logs |
FIL_PAGE_INODE | contains an array of fseg_inode_t objects. |
FIL_PAGE_IBUF_FREE_LIST | The page is in the free list of insert buffer or change buffer. |
FIL_PAGE_TYPE_ALLOCATED | Freshly allocated page. |
FIL_PAGE_IBUF_BITMAP | Insert buffer or change buffer bitmap |
FIL_PAGE_TYPE_SYS | System page |
FIL_PAGE_TYPE_TRX_SYS | Transaction system data |
FIL_PAGE_TYPE_FSP_HDR | File space header |
FIL_PAGE_TYPE_XDES | Extent Descriptor Page |
FIL_PAGE_TYPE_BLOB | Uncompressed BLOB page |
FIL_PAGE_TYPE_ZBLOB | First compressed BLOB page |
FIL_PAGE_TYPE_ZBLOB2 | Subsequent compressed BLOB page |
Each page type is used for different purposes. It is beyond the scope of this article, to explore each page type. For now, it is sufficient to know that all pages have a page header (page_header_t) and they store the page type in it, and based on the page type the contents and the layout of the page would be decided.
Each tablespace will have a header of type fsp_header_t. This data structure is stored in the first page of a tablespace.
From the tablespace header, we can access the list of segments available in the tablespace. The total space occupied by the tablespace header is given by the macro FSP_HEADER_SIZE, which is equal to 16*7 = 112 bytes.
As mentioned earlier, InnoDB will always contain one tablespace called the system tablespace with identifier 0. This is a special tablespace and is always kept open as long as the MySQL server is running. The first few pages of the system tablespace is reserved for internal usage. This information can be obtained from the header storage/innobase/include/fsp0types.h. They are listed below with a short description.
Page Number |
The Page Name |
Description |
0 | FSP_XDES_OFFSET | The extent descriptor page. |
1 | FSP_IBUF_BITMAP_OFFSET | The insert buffer bitmap page. |
2 | FSP_FIRST_INODE_PAGE_NO | The first inode page number. |
3 | FSP_IBUF_HEADER_PAGE_NO | Insert buffer header page in system tablespace. |
4 | FSP_IBUF_TREE_ROOT_PAGE_NO | Insert buffer B-tree root page in system tablespace. |
5 | FSP_TRX_SYS_PAGE_NO | Transaction system header in system tablespace. |
6 | FSP_FIRST_RSEG_PAGE_NO | First rollback segment page, in system tablespace. |
7 | FSP_DICT_HDR_PAGE_NO | Data dictionary header page in system tablespace. |
As can be noted from above, the first 3 pages will be there in any tablespace. But the last 5 pages are reserved only in the case of system tablespace. In the case of other tablespaces only 3 pages are reserved.
When the option innodb_file_per_table is enabled, then for each table a separate tablespace with one data file would be created. The source code comment in the function dict_build_table_def_step() states the following:
/* We create a new single-table tablespace for the table. We initially let it be 4 pages: - page 0 is the fsp header and an extent descriptor page, - page 1 is an ibuf bitmap page, - page 2 is the first inode page, - page 3 will contain the root of the clustered index of the table we create here. */
A tablespace can contain many file segments. File segments (or just segments) is a logical entity. Each segment has a segment header (fseg_header_t), which points to the inode (fseg_inode_t) describing the file segment. The file segment header contains the following information:
Note: It would have been really more readable (at source code level) if fseg_header_t and fseg_inode_t had proper C-style structures defined for them.
The fseg_inode_t object contains the following information:
When a segment wants to grow, it will get free extents or pages from the tablespace to which it belongs.
In InnoDB, when a table is created, a clustered index (B-tree) is created internally. This B-tree contains two file segments, one for the non-leaf pages and the other for the leaf pages. From the source code documentation:
“In the root node of a B-tree there are two file segment headers. The leaf pages of a tree are allocated from one file segment, to make them consecutive on disk if possible. From the other file segment we allocate pages for the non-leaf levels of the tree.”
For a given table, the root page of a B-tree will be obtained from the data dictionary. So in InnoDB, each table exists within a tablespace, and contains one B-tree (the clustered index), which contains 2 file segments. Each file segment can contain many extents, and each extent contains 1MB of consecutive pages.
This article discussed the details about the data organization within InnoDB. We first looked at the files created by InnoDB, and then discussed about the various logical entities like tablespaces, pages, page types, extents, segments and tables. We also looked at the relationship between each one of them.