一年很快又结束了,以前一直没有写总结的习惯,一年过去了,感觉也没有留下什么。于是今年特此写篇总结,记录下这一年的点点滴滴,再思考下明年的期望。
今年的生活比较简单,简单到回想一年,只能想起工作和回家两件事。在帝都工作的人也许都这样,生活节奏太快。今年锻炼也比较少,去年有时还和地总去奥体游泳,今年自从地总找了个女友后,找我除了帮他办事以外,绝对没有第二件事,对他是彻底无语。一直想去奥森公园锻炼的,也没有去过,希望明年可以加强下锻炼。
周围结婚的同学越来越多了,打开朋友圈,不是婚纱照就是密月照。很多同学的婚礼也没来的及参加,感到非常的遗憾。
首先非常荣幸,又在我司平安的渡过了第二个元旦。以前一直不信,工作再累也不会过劳死,来到我司后,发现一切皆有可能。
相比于去年,今年的工作更加忙了。仔细回想,技术上没有太大长进,对我司的推拉扯(推动,拉通,扯皮)文化倒是理解的刻骨铭心。以前开个会说话还吞吞吐吐,现在就算屁事没有,拉一个会也能扯上半小时。
今年对技术方面的书看的不多,主要还是查找资料,和大家交流。如果说去年一年是对分布式数据库产品有个入门的了解 ...
more ...在传统关系型数据库中有一个非常重要的特性就是high availability,简称高可用。简而言之就是如果一台服务器坏了,仍然系统要保证可用性,通常的实现方式有以下几种,
1: 使用一主一备或多备的方式来同步数据;
2: 使用共享磁阵的方式共享数据;
3: 使用分布式文件系统来保证数据的安全;
本篇博客只探讨单机数据库中的高可用,对于分布式数据库中的高可用可以到下次再探讨。
通常单机数据库采用方法1来实现high availability,一台服务器作为主机,另一台作为热备。主机实时将事务日志xlog同步给备机,备机将日志写盘后回复主机写入的位置(例如LSN,一个uint64位顺序递增数值,并假设它永远用不完),主机将事务等待队列中小于该LSN的事务全部提交。
主备复制流程如图1所示:
Note left of Primary: start transaction
Note left of Primary: flush xlog to disk
Primary->Standby: send xlog to Standby
Note right of Standby: flush ...
在传统关系型数据库中有一个功能称为full page write,这个功能是为了避免机械磁盘在写的时候无法保证原子性而设计的,也可以认为是一个古老的功能,但是其思想值得借鉴。以下以PostgreSQL数据库的实现为例子解释这一设计思想。
假设每次写事务日志时都有一个唯一的标记称为LSN,它顺序增长,假设用不完。我们在写每条日志时头部都有LSN标记。
当数据库系统运行时,如果此时系统正在刷写页面(假设page大小为8KB)时系统断电,当该服务器使用的磁盘不带电池时,磁盘刷写数据只能保证512B字节的原子性(因为磁盘的一个扇区是512B) 。 因此此时可能造成一个页面中只有页头的512B刷写到磁盘上,而剩下的数据未更新,此时如果系统启动重新恢复过程中,首先会判断该LSN是否比恢复日志中的LSN大,如果大表示页面数据为未来的,即跳过恢复。然而此时页面数据因为不具有原子性并不是最新,导致数据出现不一致。
解决这个问题的一个方法就是在每次checkpoint之后,如果读取的一个页面是第一次读取,更改该页面时,也就是写更新该页面xlog日志时把整个页面写入xlog中,如果在后面宕机再恢复时无条件将该页面替换磁盘上的页面,因为此时磁盘上的页面可能无法保证页面的原子性。而后面的日志恢复过程中,可能刷坏的页面已经被替换掉,LSN的判断也不可能出现错误,因此可以有效的解决该问题。
该方法的一个缺陷就是可能会造成xlog日志文件很大,在主备同步过程中,网络传输的日志过多,造成性能下降。
然而在现代数据库系统中使用的磁盘通常可能带电池,而可以保证写page的原子性,因此该功能通常可以关闭。并且未来可能会使用更高端的存储介质,比如SSD或NVRAM,性能将是机械磁盘的几个数量级。但是该问题的解决方案还是值得借鉴的,在我们无法保证磁盘上页面数据最新并且完整时,可以使用该full page ...
more ...在上一篇博客中我们讲到目前大众型的关系型数据库都采用了WAL思想,就是将page的更新只在内存中操作,并记录相应的事务日志,提交事务时保证事务日志先提交。
然而我们可以考虑这里面的一个问题,就是装载page的内存容量到底要设多大才合适,工程中服务器的内存是有限的,假设设置为4GB大小,那么这4GB用完了怎么办呢?还有就是我们记录的事务日志(假设称其为xlog)多久才删除呢?
这便涉及到另一个通常使用的思想叫checkpoint,目前大部分的关系型数据库都会使用这一技术。简而言之就是当到一定时候我们就把内存中的数据全部刷盘,然后删除这个时间点之前的所有xlog,因为这个时间点所有的内存数据都刷到磁盘了,之前的这些xlog已经不需要恢复了,下次系统如果出现异常,数据库系统便可以从最近一次checkpoint点来启动恢复,重新redo回内存中的这些操作。
使用这个方法便便可以保证数据库系统可以循环并正常的运行了,通常checkpoint启动可以受xlog个数,时间,业务线程的请求等等控制。并且系统在做checkpoint时会将大量的page fsync到磁盘,因此此时的IO量很大,通常此时性能不好,使用TPCC测试时可以看到tpmc的曲线在这个点出现明显的下降。
解决这个问题的一个方法就是系统中可以再运行一个后台线程,慢慢的整理内存中的page数据,将没有弄脏的page扔掉,慢慢的刷写一些page,可以腾出一些内存空间,以防数据库的内存空间因为满了而导致系统性能下降,也可以降低checkpoint启动时造成的系统性能下降的时间窗。
more ...在数据库系统中,通常大家会讨论到WAL,其称为write-ahead log,预写式日志。是目前传统关系型数据库中提升事务提交性能的一种常用方法。
在描述WAL之前,我们先讨论下数据库存储的实现方式。
在计算机系统刚刚萌芽期间,人们希望可以将一些结构化数据(比如一张表数据)存储起来,并且具有永久性。通常的做法就是将这些数据一行一行的写入一个文件,当想使用时便打开一行一行读取,当然写入和打开都是通过程序让计算机去操作。当数据操作不是很频繁时,这样不失为一种好方法。
然而如果数据在频繁的做操作,每次读取磁盘和写入磁盘都是非常耗时的。那么人们便想,CPU操作内存的速度是操作磁盘速度的几十甚至几百倍,能不能将数据按页面划分,将这些页面读取到内存中,每次数据库系统在更新时都直接更新内存中的数据页面,而不用每次去读写磁盘。
然而当系统一直运行正常的时候,性能固然是很好,但是一旦系统出现异常宕机时,内存中的数据便永久丢失,这在数据库系统中是绝对不允许的。解决这个问题的方法就是每次在提交事务之前记录事务日志,并且将事务日志刷到磁盘上后再提交事务。这样便保证了即使系统宕机,再次启动的时候数据库也可以从事务日志中恢复内存中的数据,这便是WAL,这些日志通常也被称为redo日志。
可能大家会有个疑问,同样都是要fsync到磁盘,写wal与写page有什么区别呢?这便涉及机械磁盘中一个随机IO与顺序IO的问题。写wal时是所有事务日志都写到一个文件中,最后fsync到磁盘就可以。而写page时会涉及到不同表和不同page的文件,因此需要随机的写磁盘上不同位置,这样在机械磁盘的实现上是相对较慢的。
使用写事务日志来代替频繁的读写磁盘,可以提升数据库的并发性能,因此wal也是关系型数据库乃至其它新型分布式系统中一种常用的技术。
more ...在数据库系统中,如果每个并发操作都是原子的,就好比如果都是
set a = 3;
这类的简单操作,那么数据库系统中不需要锁或隔离级别来控制并发,因为它本身就是原子的。
但是数据库中有一个重要的个功能叫事务,就是可以将多个操作定义为一个事务块,这个事务块要么都成功,要么都失败。这样就加大了数据库在并发时的复杂性。
解决这个问题的一种方法就是每个单元都加互斥锁,让每个事务串行的进行下去,这就是串行化隔离级别。
这种方法明显性能不好,无法保证数据库系统的大并发功能。我们可以思考如果把互斥锁改为读写锁,这样在读上可以并发,这是一个性能的提升。使用读写锁时,有两种情况:第一种是读读并发,读读并发可能会造成幻读的情况,这就是可重复读隔离级别。第二种是不仅可以读读并发,还可以读写并发,就是读锁可被升级为写锁,读写并发时不满足可重复读,这就是读已提交隔离级别。
然后还有一种方法就是只在写时加锁,这样可以实现读读并发,读写并发,写读并发,写读并发过程中会造成脏读,这类隔离级别通常称为读未提交。
在SQL92标准中定义了这四种隔离级别,即为读未提交,读已提交,可重复读,串行化。
需要特别注意,这四种隔离级别只是SQL92标准中定义的四个概念。当时数据库的隔离级别实现方式主要是通过锁。并且这个意义上的锁应该是行级锁,这样才有了幻读,可重复读,脏读这些概念 ...
more ...事务处理是传统关系型数据库中至关重要的特性之一,通俗的讲就是当数据库在一个事务内的一组SQL要么全部执行成功要么全部执行失败,不能出现一个事务内有的SQL执行成功,有的执行SQL执行失败。这样才可以保证事务的一致性,典型的例子就是通常大家所有的银行转账系统。
与事务相关的通常有以下几个概念: ACID , CAP , 事物隔离级别下面将先介绍ACID与CAP定义,后面将介绍事务的隔离级别。
A:原子性(Atomicity)
整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
C:一致性(Consistency)
在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。
I:隔离性(Isolation)
隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。
D:持久性(Durability)
在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
more ...C ...
记得刚开github帐号的时候是研二的时候,觉得好奇就注册了个帐号,但是后来对这个帐号也没有发布具体的开源项目。
在学校的时候一直忙于实验室的项目,也没有时间写点自己喜欢的东西。后来暑假的时候有空就把自己平时使用C++写的一个网页抓取程序传入到github了,虽然很简单,但是总算填补了github上没有具体贡献的空洞了。
后来到了研三终于有点空了,当时也是看到搭建的github上的博客,觉得好奇就自己使用Pelican模板建立了一个,刚建立后还是很开心的,终于有了自己的博客了。
本以为以后可以好好维护下的,谁知后来忙于找工作,毕业论文,学车,毕业,学生时代就这么匆匆的过去了。
自14年3月份参加工作后,觉得自己慢慢的变懒了,相比学生时代的好学精神的确差了很多。
工作这段时间对分布式数据库的思考比较多,如分布式事务,checkpoint,undo/redo,备份恢复,高可用,负载均衡等。传统数据库的基本理论还是挺高深的,所以一直想抽点时间出来总结一下,但是一直没有空,这次决定不管多忙都写的心得吧,希望一个星期可以总结一次。
more ...以前一直想建立个自己的博客网站,但一直太忙,刚好最近两天有空,就折腾了一番。
使用了github的二级域名和Pelican的博客框架,感觉还是挺好玩的,唯一遗憾的就是在本地安装markdown编辑软件的时候迟迟没有成功,郁闷了好久,后来索性就直接用在线编辑器stackedit了,也免去了以后升级的麻烦,现在看看还挺好。
第一篇博客,也不写那么多了,希望以后能有空一直写下去,作为一种爱好吧。
more ...