简介
本文一些内容可以结合 《mysql技术内幕》笔记1来看
ACID
本段主要来自知乎上“左轻侯”的回答,ACID作为一条线,将数据库的一大部分知识点串起来了。
几个特性之间的关系
在事务处理的ACID属性中,一致性是最基本的属性,其它的三个属性都为了保证一致性而存在的。
所谓一致性,指的是数据处于一种有意义的状态,这种状态是语义上的,而不是语法上的,比如常见的转账的例子。
从转账的例子可以看到,一致性的前提是原子性,但原子性并不能完全保证一致性。在多个事务并行进行的情况下,即使保证了每一个事务的原子性,仍然可能导致数据不一致的结果。例如,事务1将100元转给A,先读取账号A的值,然后在这个值上加上100.但是在这两个操作之间,另一个事务2修改了账号A的值,为它增加了100元,那么最后的结果应该是A增加了200元。但事实上,事务1最终完成后,账号A只增加了100元,因为事务2的修改结果被事务1覆盖掉了。说白了,还是并发读写问题
为了保证并发情况下的一致性,引入了隔离性,即保证每一个事务能够看到的数据总是一致的,用术语来说,就是多个事务并发执行后的状态和它们串行执行后的状态是等价的。(估计这就是为什么,隔离性的级别都是从xx读的角度来描述,而不涉及到写)
几个特性的实现原理
为了实现原子性,需要通过日志:将所有对数据的更新操作都写入日志,如果一个事务中的一部分操作已经操作,但以后的操作由于断电等原因无法继续,则通过回溯日志,将已经执行成功的操作撤销,从而达到全部操作失败的目的。(原子性要求的“要么全成功,要么全失败”,在实现上其实就是提供“一部分失败则撤销已经成功的操作”的能力)
最常见的场景是,数据库系统崩溃后重启,此时数据库处于不一致的状态,必须先执行一个crash recovery的过程:读取日志进行REDO(重新执行所有已经执行成功,但尚未写入到磁盘的操作,保证持久性),再对所有崩溃时尚未成功提交的事务进行进行undo(撤销所有执行一部分但尚未提交的操作,保证原子性)。crash recovery结束后,数据库恢复到一致性状态,可以继续被使用。(原来REDO和UNDO是以crash recovery的视角来命名的)
Undo日志记录某数据被修改前的值,可以用来在事务失败时进行rollback;Redo日志记录某数据块被修改后的值,可以用来恢复未写入data file的已成功事务更新的数据。例如某一事务的事务序号为T1,其对数据X进行修改,设X的原值是5,修改后的值为15,那么Undo日志为<T1, X, 5>
,Redo日志为<T1, X, 15>
。
日志的管理和重演是数据库实现中最复杂的部分之一,如果涉及到并行处理和分布式系统(日志的复制和重演是数据库高可用性的基础),会比上述场景还要复杂的多。
隔离性的实现,原则上无非是两种类型的锁:
- 悲观锁,即对当前事务所涉及的对象全部加锁,操作完成后释放给其它事务使用。为了尽可能提高性能,发明了各种粒度(数据库级、表级、行级)/各种性质(共享、排他等)。为了解决死锁问题,又发明了两阶段锁协议、死锁检测等一系列技术。
- 乐观锁,即不同的事务可以同时看到同一对象的不同历史版本。如果有两个事务同时修改了同一数据行,那么在较晚的事务提交时进行冲突检测。历史版本可以保存在undo日志中,也可以全保存在内存中。
事务和一致性的关系
事务是一个操作序列,系统需要提供一定的机制,支持这个操作序列的执行。从这个角度看,这跟操作系统需要支持进程运行类似。
但事务有一致性的要求,而一致性本质是和语义相关的,这就意味着数据库系统要做更多的工作来支持。
数据的组织
假定数据库存放数据的文件称为data file,数据库的内容在内存里是有缓存的,称为db buffer。某次操作,我们取了数据库某表格中的数据,这个数据会在内存中缓存一些时间。对这个数据的修改在开始时候也只是修改在内存中的内容。当db buffer已满或者遇到其他的情况,这些数据会写入data file。(为了维护这个db buffer和db file的一致性,引入了checkpoint)
db buffer的存在不仅提高了性能,系统也有机会对db buffer进行一定整理后集中写入,毕竟db数据随机写入的开销比较大。log file一般是追加内容,可以认为是顺序写,
How do you build a database
摘自How do you build a database? ,为防止链接失效,贴上原文。
Its a great question, and deserves a long answer. Most database servers are built in C, and store data using B-tree type constructs. In the old days there was a product called C-Isam (c library for an indexed sequential access method) which is a low level library to help C programmers write data in B-tree format. So you need to know about btrees and understand what these are. BTree 很重要
Most databases store data separate to indexes. Lets assume a record (or row) is 800 bytes long and you write 5 rows of data to a file. If the row contains columns such as first name, last name, address etc. and you want to search for a specific record by last name, you can open the file and sequentially search through each record but this is very slow. Instead you open an index file which just contains the lastname and the position of the record in the data file. Then when you have the position you open the data file, lseek to that position and read the data. Because index data is very small it is much quicker to search through index files. Also as the index files are stored in btrees in it very quick to effectively do a quicksearch (divide and conquer) to find the record you are looking for. 一个表单单数据文件是不够的,需要一/多个索引文件。
So you understand for one “table” you will have a data file with the data and one (or many) index files. The first index file could be for lastname, the next could be to search by SS number etc. When the user defines their query to get some data, they decide which index file to search through. If you can find any info on C-ISAM (there used to be an open source version (or cheap commercial) called D-ISAM) you will understand this concept quite well.
Once you have stored data and have index files, using an ISAM type approach allows you to GET a record based on a value, or PUT a new record. However modern database servers all support SQL, so you need an SQL parser that translates the SQL statement into a sequence of related GETs. SQL may join 2 tables so an optimizer(优化器最初是为了加快join表的速度么?) is also needed to decide which table to read first (normally based on number of rows in each table and indexes available) and how to relate it to the next table. SQL can INSERT data so you need to parse that into PUT statements but it can also combine multiple INSERTS into transactions so you need a transaction manager to control this, and you will need transaction logs to store wip/completed transactions.
It is possible you will need some backup/restore commands to backup your data files and index files and maybe also your transaction log files, and if you really want to go for it you could write some replication tools to read your transaction log and replicate the transactions to a backup database on a different server. Note if you want your client programs (for example an SQL UI like phpmyadmin) to reside on separate machine than your database server you will need to write a connection manager that sends the SQL requests over TCP/IP to your server, then authenticate it using some credentials, parse the request, run your GETS and send back the data to the client. So these database servers can be a lot of work, especially for one person. But you can create simple versions of these tools one at a time. Start with how to store data and indexes, and how to retrieve data using an ISAM type interface. There are books out there - look for older books on mysql and msql, look for anything on google re btrees and isam, look for open source C libraries that already do isam. Get a good understanding on file IO on a linux machine using C. Many commercial databases now dont even use the filesystem for their data files because of cacheing issues - they write directly to raw disk. You want to just write to files initially. I hope this helps a little bit.
概要内容:
- 知道BTree 很重要
- 一个表单单数据文件是不够的,需要一/多个索引文件,数据文件和索引文件分开存储
- 有了数据文件和索引文件,你就可以读写数据了,但你需要SQL parser 将sql 翻译成读写操作,需要optimizer加快join表的速度,需要Transaction manager 管理事务
- 备份儿/恢复数据文件、索引文件、Transaction log文件。如果支持客户端程序的话,还需要一个Connection manager