首先,我们要定义下什么是“锁的粒度”:所谓粒度就是作用范围,锁的粒度就是锁的作用范围。数据库中锁的粒度从高到低依次划分为:数据库、表、页、行。
什么是死锁,顺带说一下吧,当多个操作竞争资源,每个操作都无法获得全部所需资源时,系统进入死锁,如无外力作用,系统将无限等待下去,死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
打破任何一个条件就不会发生死锁。
我们先来看一个生活场景:
有两个文具盒,一个放的是笔(圆珠笔、铅笔等等),一个放的是修正工具(橡皮擦、改正带等等).
现在有两个人,他们要画图,可能需要的物品当然有笔和修正工具。
假设我们用全局的独享,就是两个文具盒都只能同时被一个人拥有,一个人来拿笔,连带修正工具一起归他,这样绝对不会发生图画不下去的问题,因为所要的工具都会一次性给同一个人。但是问题也很严重,资源严重浪费,因为笔和橡皮擦肯定不会同时使用,也不会同时用2支笔。
假设我们用的是文具盒的独享,就是同时一个文具盒只能被一个人拥有,一个人要笔,整个放笔的文具盒都给他,一个人要修正图画,则整个放修正工具的文具盒都给他。这可能出现的问题就是,一个人想,我要画图,橡皮擦肯定要,橡皮擦的文具盒正好在,就先把这个文具盒拿下,等笔盒回来了再继续画。另一个人拿了笔盒,画了一会要修正,一看,放橡皮擦的文具盒没了,就等文具盒放回来。这样两个人就无限等待了,只要其中一个人看到东西不全就不拿,就不会无限等。
假设我们用的是物品独享,就是一支笔、一个橡皮擦只能被一个人拥有,一个人要画图,就拿一支笔,要改就拿一个橡皮擦或者改正带。除非只有一支笔,否则不会出现无限等待。当然,只有一支笔跟独享一个文具盒没区别。
阅读全文…
1989~2009,成长与求学之路20年,已经走完。
2010~2019,工作拼搏第一个10年,已经开始。
给自己一个规划,一步步的去实现。现在,我以我是个阿里人而自豪,未来,我要阿里以我是阿里人而骄傲。
第一目标:参与至少一个大项目,经历一次从项目立项到正式发布的全部数据库设计过程。现在最缺乏的就是经验,有阿里这样大的平台,就要铆足了劲去做事,积累经验。
第二目标:用1~2年时间编写一个功能全面的数据库辅助系统,包括实时监控、历史记录、趋势分析、故障预警、自动备份等功能。DBA工作中有很多工作都是可以自动化的,却普遍存在大量的人肉工作,编写一套DBA辅助工具,非常有必要,也方便自己工作。
第三目标:用3~5年时间写一本书——《数据库常用算法分析》,总结数据库的各种常用算法,各种数据库专用的调优书籍和数据库理论书籍都很全的,缺的是从算法角度来看数据库的书,无关具体数据库,只要了解所用数据库所用的算法,调优自然方法清晰。定这么长的时间,因为想学Knuth爷爷,充分检查错误,不想在序言中写“因作者水平有限,难免有错误……”,而是写上“如果您发现一个错误,请告知作者,将奖励您2.56RMB……”,尽力做完美,会把每一步的进展都写在博客,供大家先批评,我再修改,最后整理出版成书,不在于赚稿费,在于给自己一个技术上的总结。
第四目标:经历5~8年时间的磨练成为一位知名的运维领域专家,利用业余时间为一些有社会价值的互联网创业企业提供无偿的数据库架构顾问。
第五目标:用5~10年时间编写一个可自定义选择不同算法的数据库,或基于MySQL,或自己重新写,不在于能在生产环境用的多广,而在于让跟多人理解数据库如何运转,数据库算法对数据库产生何种影响,将各种不同的算法都写入代码,用户自选用哪种,一个设想,尽力去实现。
感谢Fenng的提醒,已删除锁粒度和死锁的关系,专门撰文写了锁的粒度与死锁的关系。
InnoDB与MyISAM不同,它实现的是一个行级锁,而非MyISAM的表锁。锁的粒度越大,则发生死锁的概率越小、锁机制开销越小,但并发能力会越低。如果锁的粒度变细,则发生死锁的概率也会增大,锁机制的开销会更大,但是并发能力能提高。表锁是如何实现的呢,以MyISAM为例,是在每个表的结构中加入一个互斥变量记录锁状态,像:
struct Table {
Row rows[MAXROWS];
pthread_mutex_t lock;//表锁
};
这样做的好处就是锁非常简单,当操作表的时候,直接锁住整个表就行,锁机制的开销非常小。但是问题也很明显,并发量上不去,因为无论多小的操作,都必须锁整个表,这可能带来其他操作的阻塞。
行锁又是如何实现的呢,Oracle是直接在每个行的block上做标记,而InnoDB则是靠索引来做。InnoDB的主键索引跟一般的索引不太一样,Key后面还跟上了整行的数据,互斥变量也是加载主键索引上的,像
struct PK_Idx {
Row row;
pthread_mutex_t lock;//行锁
};
multimap pk_idx;
这样的形式。
这样做的好处是锁的粒度小,只锁住需要的数据不被更改,但是问题也很明显,锁的开销很大,每个主键索引上都要加上一个标记,因为锁的粒度很小,可能两个不同的操作各锁住一部分行等待对方释放形成死锁,不过这个是有办法解决的,把上锁的操作封装成原子操作就行,不过并发量会受些影响。
下面是类似InnoDB的Next-Key locking算法的演示:
编译需要加-lpthread参数,例如g++ inno.cpp -lpthread -o inno
阅读全文…