Monthly Archives: August 2014

一个慢sql可能会成为害群之马

一条sql执行慢,不仅相关业务受影响,其他业务也可能影响。

数据库连接是有限的,一条慢sql执行慢,就意味着它会在较长时间内占用数据库连接; 如果并发执行慢sql, 很容易占用大量的数据库连接,其他的快sql获取不到连接,执行不了,快sql对应的sql也就受影响了,最终导致整个系统的可用性都出问题。

除了数据库连接,慢SQL(或者大规模数据处理如批量数据清理)还会占用数据库的其他资源。
比如过久占用锁、占用过多CPU/内存等,这些都有可能导致其他sql不能立即执行。

解决方案有:

1. 甄别、改进慢sql. 对于高并发的系统,sql rt应该维持在5ms以内

2. 数据库连接池互相隔离,比如让主干业务独享数据库连接池,其他业务若出现慢sql也不影响主业务。

3. 设置sql执行超时,一段时间内没有执行完强制释放数据库连接。

redo log, undo log中的”do” 都是idempotent的

redo log, undo log中的"do" 都是idempotent的.

所谓的redo, 就是把数据改成log entry所记录的新值;不管故障发生时数据是不是已经为新值,在恢复时把数据设成新值总是无害的,因为“设为新值”这个操作是idempotent的。

undo也一样,即把数据改成旧值。

脑子里要建立这个意识,redo/undo其实都是赋值操作,而不能是“增1”、“减1”这种non-idempotent操作。

数据日志中的checkpoint

根据redo log或undo log进行数据恢复时,是不是要扫描整个数据文件? 那未免太重了。

如果我们可以确信在log的某一点之前,数据都是完整的,那么我们就只需要处理这一点之后的log.

这个点就叫做"checkpoint".  在log中定期地生成checkpoint, 进行数据恢复时再找到最后一个checkpoint, 即可像上面所说的那样节省工作量。

以undo logging为例。生成checkpoint的简单办法是:

引用

1. 禁止启动新的transaction

2. 待所有transaction已经commit或abort

3. 将log flush至disk

4. 在log中生成一个<checkpoint>标识并flush至disk

5. 允许新的transaction

当然这种办法过于粗暴,由于禁止新transaction, 它会导致DB一段时间不可用; 有其他的机制如nonquiscent checkpointing, 可以不影响可用性。 具体就不说了。

redo log + undo log一起上

undo logging
redo logging各有优缺点,一个需要频繁flush to disk, 一个需要较大的buffer.

如何同时避免这两种缺点?让buffer manager按最佳性能策略决定何时flush, 而不必考虑log的问题。

办法就是redo log + undo log一起上. 对于每个数据改动,记录 <Transaction T, 数据X, 旧值,新值>.

唯一的约束是,在将数据改动flush至disk之前,保证相应的log entry已经写至disk.

数据恢复时,把commited transaction全部redo一遍,再把incomplete transaction全部undo一遍。

通过archiving恢复数据的一般策略

一般来说,会做一个full dump, 然后周期性地进行incremental dump. incremental dump只记录那些改变过的数据。

数据恢复时,将full dump和若干incremental dump依次写回到数据库中。

问题是,在dump过程中数据仍会被改动,这些改动可能不会被记到dump中。要解决这个问题,可以在dump过程中记录undo log或redo log,恢复数据时,搞完dump后,再根据log做数据恢复。

这个log不能跟源数据放在同一个disk里。既然要用archiving机制做数据恢复,就意味着DB已经出现media failure, 如果log跟DB数据放在一起,那log很可能也已经丢了。

根据redo log恢复数据

redo logging机制的基本原理是: 进行数据恢复时,对Incompleted Transaction不做处理;而对Committed Transaction,把记录了的数据改动再重现做一遍。

记录log

引用

Start Transaction T



记录:X被修改后的值是A

记录:Y被修改后的值是B

记录:X被修改后的值是C



Commit Transaction T

根据log恢复

1. 从上往下扫log文件

2. 遇到Commit Transaction时,依次…, 将X的值改回为A, Y的值的改会为B,  X的值改为C…

可靠性如何保证?

Redo logging要求一个transaction的所有log entry都已经写入disk了,才能把数据改动写入disk. 进行数据恢复时,根据这个规则, 对于Incomplete Transaction我们可以断定数据改动还没写入disk, 所以可以直接忽略; 而对于Commited Transaction, 数据改动可能已经写入disk,也可能未写入, 这时只须根据log把数据改动做一遍,可能白做,但绝对无害。

Redo Logging的缺点

在把一个transaction的所有log entry flush to disk之前,还不能flush数据改动; 也就是说,要有足够大的buffer, 容纳这些数据改动,直到transaction结束. 所以,redo logging可能比较占用内存。

根据undo log恢复数据

undo logging机制的基本原理是: 进行数据恢复时,对于Committed Transaction不做处理,对于Incomplete Transaction,则将相关数据写回成Transaction开始前的旧值。

记录log

引用

Start Transaction T



记录:X被修改前的值是A

记录:Y被修改前的值是B

记录:X被修改前的值是C



Commit Transaction T

根据log恢复

1. 从下往上扫log文件

2. 遇到有Start无Commit的Transaction时,依次将…, X的值改回为C, Y的值改回为B, X的值的改回为A.

可靠性如何保证?

1. Undo logging要求所有数据改动都已经写入disk了,才能将Commit Transaction这种log entry写入disk,这就保证commited transaction相关的数据改动已经真的持久化了。

2. Undo Logging还要求先在log文件中记录数据的旧值,再将数据改动持久化。当发生断电时,数据改动可能已经持久化,也可能还没有; 进行数据恢复时不必管有没有,一律把数据恢复成旧值即可,有可能是多余的,也有可能不是,总之对数据一致性是无害的。

Undo Logging的缺点

必须先将数据改动写入disk, 才能标识一个Transaction的结束; 也就是说,要完成一个Transaction, 必须将数据改动写入disk, 不能再在buffer里放着。缺点就是,数据改动可能没有累积到一定的量,就要从buffer中flush中去,所以I/O读写可能会比较频繁。

数据库读写的三个地址空间

数据库的读写过程会经历三个地址空间。

1. disk

2. buffer

3. 当前查询或事务自己的地址空间。

所有输入数据都要先流经buffer才能到达disk。反之亦然:所有输出数据都要先从disk进入buffer,才会到达客户端。 

也就是说,可以 3=>2=>1或1=>2=>3, 不能直接1=>3或3=>1.

不仅数据读写如此,redo log和undo log的写入也要先经buffer,才能进入disk.  

有个专门的buffer manager来管理buffer, 比如在什么时机将数据flush至disk, 就是由它来决定的。

数据库的两种数据故障

数据库的数据故障可以分为两类:

1. system failure. 比如断电导致内存中的数据未及写入硬盘,造成数据的不一致。这类故障可以通过数据日志来恢复(redo log或undo log)

2. media failure.  数据硬盘坏了,日志也放在硬盘里,数据和日志都丢失了。这类问题一般通过数据备份(archiving)来解决。即把数据dump到其他硬盘或磁带中,当故障发生时,恢复数据。还有一种做法是把数据实时同步到备库,当主库出现故障时将系统切换到备库。