电玩城打鱼捕鱼-专业24小时上下分

Innodb三大特性之double

六月 6th, 2019  |  电玩城上下分数据库

页断裂(partial write)与doublewrite技术,partialdoublewrite

      mysql double write (二次写)是mysql
innodb存储引擎的一个重要特性,本人这两天翻阅了相关的资料,结合自己已有的知识,说说自己对double
write的理解,供各位看官参考。

页断裂(partial write)

double write技术innodb为解决页断裂(partial
write)问题而生,所谓页断裂是数据库宕机时(OS重启,或主机掉电重启),数据库页面只有部分写入磁盘,导致页面出现不一致的情况。数据库,OS和磁盘读写的基本单位是块,也可以称之为(page
size)block
size。我们知道数据库的块一般为8K,16K;而OS的块则一般为4K;IO块则更小,linux内核要求IO
block size<=OS block size。磁盘IO除了IO block
size,还有一个概念是扇区(IO sector),扇区是磁盘物理操作的基本单位,而IO
块是磁盘操作的逻辑单位,一个IO块对应一个或多个扇区,扇区大小一般为512个字节。所以各个块大小的关系可以梳理如下:

DB block > OS block >= IO block > 磁盘
sector,而且他们之间保持了整数倍的关系。比如我的系统各个块的大小如下,DB以mysql为例,OS以linux为例。

DB block size

[email protected](none)
09:13:02>show variables like ‘innodb_page_size’;

+——————+——-+

| Variable_name    | Value |

+——————+——-+

| innodb_page_size | 16384 |

+——————+——-+

OS block size

[1 Single:CHECK: mysql ~ ]

$ getconf PAGESIZE

4096

IO block size

比如查看sdb1分区的IO block size

[[email protected]]#
blockdev –getbsz /dev/sdb1

4096

sector size

[[email protected]]#
fdisk -l | grep Sector

Sector size (logical/physical): 512 bytes / 512 bytes

从上面的结果可以看到DB page=4*OS page=IO page=8*sector size。

由于任何DB
page的写入,最终都会转为sector的写入,如果在写磁盘的过程中,出现异常重启,就可能会发生一个DB页只写了部分sector到磁盘,进而出现页断裂的情况。

页断裂与数据库一致性

前面我们分析了异常重启一定会导致页断裂,而页断裂就意味着数据库页面不完整,那么数据库页面不完整是否就意味着数据库不一致呢???我们知道,数据库异常重启时,自身有异常恢复机制,我这里不打算展开讲异常恢复机制,因为不同数据库的异常恢复流程不同。主流数据库基本原理类似:第一阶段重做redo日志,恢复数据页和undo页到异常crash时的状态;第二阶段,根据undo页的内容,回滚没有提交事务的修改。通过两个阶段保证了数据库的一致性。对于mysql而言,在第一阶段,若出现页断裂问题,则无法通过重做redo日志恢复,进而导致恢复中断,数据库不一致。这里大家可能会有疑问,数据库的redo不是记录了所有的变更,并且是物理的吗?理论上来说,无论页面是否断裂,从上一个检查点对应的redo位置开始,一直重做redo,页面自然能恢复到正常状态。对吗?讲清楚这个问题,先讲讲重做日志(redo)格式。

重做日志(redo)格式

数据库系统实现日志主要有三种格式,逻辑日志(logical
logging),物理日志(physical logging),物理逻辑日志(physiological
logging),而对于redo日志,则主要采用物理日志和物理逻辑日志两类。逻辑日志,记录一个个逻辑操作,不涉及物理存储位置信息,比如mysql的binlog;物理日志,则是记录一个个具体物理位置的操作,比如在2号表空间,1号文件,48页的233这个offset地方写入了8个字节的数据,通过(group_id,file_id,page_no,offset)4元组,就能唯一确定数据存储在磁盘的物理位置;物理逻辑日志是物理日志和逻辑日志的混合,如果一个数据库操作(DDL,DML,DCL)产生的日志跨越了多个页面,那么会产生多个物理页面的日志,但对于每个物理页面日志,里面记录则是逻辑信息。这里我举一个简单的INSERT操作来说明几种日志形式。

比如innodb表T(c1,c2, key key_c1(c1)),插入记录row1(1,’abc’)

逻辑日志:

<insert OP, T, 1,’abc’>

逻辑物理日志:

因为表T含有索引key_c1,
一次插入操作至少涉及两次B树操作,二次B树必然涉及至少两个物理页面,因此至少有两条日志

<insert OP, page_no_1, log_body>

<insert OP, page_no_2, log_body>

物理日志:

由于一次INSERT操作,物理上来说要修改页头信息(如,页内的记录数要加1),要修改相邻记录里的链表指针,要修改Slot属性等,因此对应逻辑物理日志的每一条日志,都会有N条物理日志产生。

< group_id,file_id,page_no,offset1, value1>

< group_id,file_id,page_no,offset2, value2>

……

< group_id,file_id,page_no,offsetN, valueN>

 

因此对于上述一个INSERT操作,会产生一条逻辑日志,二条逻辑物理日志,2*N条物理日志。从上面简单的分析可以看出,逻辑日志的日志量最小,而物理日志的日志量最大;物理日志是纯物理的;而逻辑物理日志则页间物理,页内逻辑,所谓physical-to-a-page,
logical-within-a-page。

redo格式与数据一致性

回到“发生页断裂后,是否会影响数据库一致性”的问题,发生页断裂后,对于利用纯物理日志实现redo的数据库不受影响,因为每一条redo日志完全不依赖物理页的状态,并且是幂等的(执行一次与N次,结果是一样的)。另外要说明一点,redo日志的页大小一般设计为512个字节,因此redo日志页本身不会发生页断裂。而逻辑物理日志则不行,比如修改页头信息,页内记录数加1,slot信息修改等都依赖于页面处于一个一致状态,否则就无法正确重做redo。而mysql正是采用这种日志类型,所以发生页面断裂时,异常恢复就会出现问题,需要借助于double
write技术来辅助处理。

double write处理页断裂

doublewrite是Innodb表空间内部分配的一片缓冲区,一般double
write包含128个页,对于pagesize为16k的页,总共2MB,doublewrite页与数据页一样有物理存储空间,存在于共享表空间中。Innodb在写出缓冲区中的数据页时采用的是一次写多个页的方式,这样多个页就可以先顺序写入到doublewrite缓冲区,并调用fsync()保证这些数据被写出到磁盘,然后数据页才被写出到它们实际的存储位置并再次调用fsync()。故障恢复时Innodb检查doublewrite缓冲区与数据页原存储位置的内容,若doublewrite页处于页断裂状态,则简单的丢弃;若数据页不一致,则会从doublewrite页还原。由于doublewrite页落盘与数据页落盘在不同的时间点,不会出现doublewrite页和数据页同时发生断裂的情况,因此doublewrite技术可以解决页断裂问题,进而保证了重做日志能顺利进行,数据库能恢复到一致的状态。

oracle如何处理页断裂

oracle与mysql一样,采用的redo日志也是逻辑物理格式,但没有doublewrite技术。我一直就想着oracle这么牛逼的数据库,一定有它自己的方式来应对这种问题。也许这就是所谓的蛮目崇拜,找了N久资料,中文的英文的,包括咨询何登成大神,基本能得出一个结论oracle遇到页断裂问题,一样会挂,重启不来。但是oracle有一个相对简单的策略来恢复数据库到一致状态,备份+归档日志。备份保证了数据页不发生页断裂,加上归档日志增量可以恢复到某个时间点。为什么不做呢?我想一般使用oracle都会使用DataGuard做容灾,主库发生故障时,备库会承担起主库的责任,然后异常主库通过备份+归档日志的方式来恢复,虽然不如doublewrite技术快,但也是能恢复出来的。

其他方式解决页断裂

前面讨论的都是基于一个假设,异常重启后,一定会导致页断裂,其实在底层设施在一定程度上是可以解决这个问题的,比如文件系统层面,采用ZFS文件系统,ZFS通过日志的方式保证了OS页面的完整性,从底层防止了页断裂问题;在磁盘层面,一般RAID卡都会有带电缓存,即使OS异常重启,缓存数据也不会立即丢失,因而也能保证不发生partial
write问题。但是我在想,OS的pagesize比DB的pagesize要小,即使能保证OS
page不发生页断裂,也不能保证DB page
不断裂,个人感觉不能彻底解决。当然了,如果将DB pagesize设置成和OS
pagesize一样大,就没问题了。

 

参考文档

 

      mysql double write (二次写)是mysql
innodb存储引擎的一个重要特性,本人这两天翻阅了相关的资料,结合自己已有的知识,说说自己对double
write的理解,供各位看官参考。

1、doublewrite
buffer(mysql官方的介绍)

DataOutputStream中writeDouble方法疑问

二进制格式,write(b)这个就是你说的writeBytes
byte本身就是二进制了
 

页断裂(partial
write)

InnoDB uses a novel file flush technique called doublewrite. Before
writing pages to the data
files
, InnoDB first writes them to a contiguous area called the
doublewrite buffer. Only after the write and the flush to the
doublewrite buffer have completed, does InnoDB write the pages to their
proper positions in the data file. If there is an operating system,
storage subsystem, or mysqld
process crash in the middle of a page write, InnoDB can later find a
good copy of the page from the doublewrite buffer during crash recovery.

java writeDouble写入文件的字符格式是什,在文件中显示咋回事乱码

double是64位的二进制,你当他文本读自然是乱码

如果你需要写入文本可是的浮点数,应该把double转成String类型,再写入文件
 

write)与doublewrite技术,partialdoublewrite mysql double write
(二次写)是mysql
innodb存储引擎的一个重要特性,本人这两天翻阅了相关的资…

double
write技术innodb为解决页断裂(partial
write)问题而生,所谓页断裂是数据库宕机时(OS重启,或主机掉电重启),数据库页面只有部分写入磁盘,导致页面出现不一致的情况。数据库,OS和磁盘读写的基本单位是块,也可以称之为(page
size)block
size。我们知道数据库的块一般为8K,16K;而OS的块则一般为4K;IO块则更小,linux内核要求IO
block size<=OS block size。磁盘IO除了IO block
size,还有一个概念是扇区(IO sector),扇区是磁盘物理操作的基本单位,而IO
块是磁盘操作的逻辑单位,一个IO块对应一个或多个扇区,扇区大小一般为512个字节。所以各个块大小的关系可以梳理如下:

Although data is always written twice, the doublewrite buffer does not
require twice as much I/O overhead or twice as many I/O operations. Data
is written to the buffer itself as a large sequential chunk, with a
single fsync() call to the operating system.

DB block > OS block >= IO block
> 磁盘
sector,而且他们之间保持了整数倍的关系。比如我的系统各个块的大小如下,DB以mysql为例,OS以linux为例。

To turn off the doublewrite buffer, specify the option
innodb_doublewrite=0.

DB block
size

2、partial page write

root@(none) 09:13:02>show variables
like ‘innodb_page_size’;

 

+——————+——-+

数据库,OS和磁盘读写的基本单位是块,也可以称之为(page size)block size。

| Variable_name    | Value |

数据库的块一般为8K,16K;而OS的块则一般为4K;IO块则更小,linux内核要求IO
block size<=OS block size。

+——————+——-+

磁盘IO除了IO block size,还有一个概念是扇区(IO
sector),扇区是磁盘物理操作的基本单位,而IO
块是磁盘操作的逻辑单位,一个IO块对应一个或多个扇区,扇区大小一般为512个字节。

| innodb_page_size | 16384 |

所以各个块大小的关系可以梳理如下:

+——————+——-+

 

OS block
size

DB block > OS block >= IO block > 磁盘
sector,而且他们之间保持了整数倍的关系。比如我的系统各个块的大小如下,DB以mysql为例,OS以linux为例

[1 Single:CHECK: mysql ~ ]

DB block size

$ getconf PAGESIZE

mysql> show variables like 'innodb_page_size';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| innodb_page_size | 16384 |
+------------------+-------+

4096

OS block

IO block
size

[root@dev ~]# getconf PAGESIZE
4096

比如查看sdb1分区的IO block size

IO block size

[root@chuckkkk]# blockdev –getbsz
/dev/sdb1

[root@dev ~]# blockdev --getbsz /dev/sda1 
1024

4096

sector size 

sector
size

[root@dev ~]# fdisk -l | grep Sector
Sector size (logical/physical): 512 bytes / 512 bytes
标签:, ,

Your Comments

近期评论

    功能


    网站地图xml地图