undo日志insert,update,delete (1)—mysql进阶(六十四)
前面说了redo日志为了保证系统宕机的情况下,能够恢复数据,恢复数据是在以checkpoint_lsn为起始位子来恢复,在该值之前的都是已经持久化到磁盘的,可以为了提升效率而放弃,而之后的数据,也可能在checkpoint之后,被后台异步运行的线程刷新到磁盘,这时候如果file header里file_page_lsn值大于checkpoint_lsn值,代表已经持久化,也可以跳过。还有会吧同一个页的space id和page number放入一个hash表,这样避免同一个页反复I/O插入。
事务回滚需求
我们说过事务需要保证原子性, 那么全部完成,要么什么也不做。但偏偏有的时候执行到一半,比如系统宕机,停电,服务器错误等,比如一半之后,程序员可以手动执行rollback回滚。
但执行到一半就结束,可能会修改很多东西,我们需要把数据改回原来的样子,叫回滚(rollback)。这样看起来就如同事务什么都没做。
所以当我们新增一条数据的时候,如果想要回滚,至少要记录他的id,到时候把他删除。
当我们删除一条数据的时候,如果想要回滚,至少要记录他的id,到时候把他新增。
当我们修改一条数据的时候,如果想要回滚,至少要记录他的修改数据和id,到时候吧他修改回来。
innoDB吧这些东西记录在一个日志里,叫undo 日志,这里需要注意的是,select不需要回滚,所以不记录在这些里面。我们先看看事务ID是什么。
事务ID
前面我们说过事务可以开启只读事务,或者开启读写事务:
我们可以通过start transaction read only语句开启一个只读事务,在只读事务里,不可以对普通的表做增删改操作,但可以对临时表增删改。
可以strat transaction read write 语句开启读写事务,或者默认不指定就是开启读写事务。
如果在事务里进行了增删改操作,则innoDB存储引擎会给他分配一个独一无二的事务ID。
对于只读事务,只有他次对临时表增删改才会为这个事务分配一个事务id,否则不分配。(我们前面说过用explain语句会有一个using temporay的提示,表示该语句会用到内部临时表,但这个跟我们自己创建的create temporary table是不一样的,这种临时表会不给他们分配独立的事务id)
对于读写事务,只有他在次对表或者临时表增删改的时候,会给他分配一个事务id。
所以我们有的时候虽然开启了事务,但是并没有增删改,所以也不会给当前事务分配事务id。
事务id怎么生成的
这个事务id本质就是一个数字,他的分配策略和我们前面说的row_id大致相同:
服务器会维护一个全局变量是事务id,当每次需要分配事务id的时候,该变量就+1.
每当这个变量是256的倍数的时候,就会把这个id刷新到页号5称为max trx id的属性处,这个属性占用8个字节存储空间。
当系统下一次重启的时候,会吧max trx id属性加载到内存,将该值加上256后赋值给我们前面提到的全局变量(因为上次关机时该全局变量值可能大于max trx id属性值)。
这样可以保证整个事务id是一个递增数字。
Trx_id隐藏列
前面我们说过innoDB行格式,聚簇索引记录除了保存完整的数据格式,额外数据外,还会有几个隐藏列,比如row_id,trx_id,roll_pointer,其中row_id不是必须的,说过很多次了,只有没有主见或者键,才会创建隐藏的row_id,其中trx_id很好理解,就是事务id。
Undo 日志的格式
为了实现原子性,innoDB存储引擎在增删改的时候,需要把对应的undo日志记下来。一般每对一个undo日志做改动,都对应一个undo日志,但有的时候也可能对应两个undo日志,后面会仔细唠嗑。一个事务可能会进行增删改很多次undo记录,这些undo日志会从0编号开始,这个编号称为undo no,会从第0号undo,号unodo。。。
这些undo日志会记录到fil_page_undo_log的页面中,这些页面也可以从系统表空间分配,也可以从一种专门放undo日志的表空间,也就是所谓的undo table space中分配。我们先创建一个undo_demo表:
mysql> create table undo_demo(
-> id int not null,
-> key1 varchar(100),
-> col varchar(100),
-> primary key(id),
-> key idx_key1(key1)
-> );
Query OK, 0 rows affected (0.05 sec)
相关文章