## 前言
在前面几期月报我们介绍了undo log、redo log以及InnoDB如何崩溃恢复来实现数据ACID的相关知识。本期我们介绍另外一种重要的数据变更日志,也就是InnoDB change buffer。 Change buffer的主要目的是将对二级索引的数据操作缓存下来,以此减少二级索引的随机IO,并达到操作合并的效果。
在MySQL5.5之前的版本中,由于只支持缓存insert操作,所以最初叫做insert buffer,只是后来的版本中支持了更多的操作类型缓存,才改叫change buffer,这也是为什么代码中有大量的ibuf前缀开头的函数或变量。为了表达方面,本文也将change buffer缩写为ibuf。
由于历史上ibuf的数据格式曾发生过多次变化,本文讨论的相关内容基于如下设定:
版本为5.5及之后的版本,不涉及旧版本的逻辑,innodb_change_buffering 设置为ALL,表示缓存所有操作。
## ibuf btree
change buffer的物理上是一颗普通的btree,存储在ibdata系统表空间中,根页为ibdata的第4个page(FSP_IBUF_TREE_ROOT_PAGE_NO)。
一条ibuf 记录大概包含如下列:
![ibuf 记录](https://box.kancloud.cn/2015-09-24_56039a391da52.png "change buffer 记录结构")
ibuf btree通过三列(space id, page no , counter)作为主键来唯一决定一条记录,其中counter是一个递增值,目的是为了维持不同操作的有序性,例如可以通过counter来保证在merge时执行如下序列时的循序和用户操作顺序是一致的:INSERT x, DELETE-MARK x, INSERT x。
在插入ibuf记录前我们是不知道counter的值的,因此总是先将对应tuple的counter设置为0xFFFF,然后将cursor以模式PAGE_CUR_LE定位到小于等于(space id, page no, 0xFFFF)的位置,新记录的counter为当前位置记录counter值加1。
ibuf btree最大默认为buffer pool size的25%,当超过25%时,可能触发用户线程同步缩减ibuf btree。为何要将ibuf btree的大小和buffer pool大小相关联呢 ? 一个比较重要的原因是防止ibuf本身占用过多的buffer pool资源。
## ibuf bitmap
由于ibuf 缓存的操作都是针对某个具体page的,因此在缓存操作时必须保证该操作不会导致空page 或索引分裂。
针对第一种情况,即避免空page,主要是对purge线程而言,因为只有purge线程才会去真正的删除二级索引上的物理记录。在准备插入类型为IBUF_OP_DELETE的操作缓存时,会预估在apply完该page上所有的ibuf entry后还剩下多少记录(`ibuf_get_volume_buffered`),如果只剩下一条记录,则拒绝本次purge操作缓存,改走正常的读入物理页逻辑。
针对第二种情况,InnoDB通过一种特殊的page来维护每个数据页的空闲空间大小,也就是ibuf bitmap page,该page存在于每个ibd文件中,具有固定的page no,其文件结构如下图所示:
![ibuf bitmap](https://box.kancloud.cn/2015-09-24_56039a397fdf4.png "change buffeer bitmap page")
ibuf bitmap使用4个bit来描述一个page:
1. IBUF_BITMAP_FREE:使用2个bit来描述空闲空间大小,以16KB的page size为例,能表示的空闲空间范围为0(0 bytes)、1(512 bytes)、2(1024 bytes)、3(2048 bytes)。很显然,能够缓存的二级索引记录最大不可能超过2048字节。
由于只有INSERT操作才可能导致page记录满,因此只需要对IBUF_OP_INSERT类型的操作进行判断:
ibuf_insert_low:
~~~
if (op == IBUF_OP_INSERT) {
ulint bits = ibuf_bitmap_page_get_bits(
bitmap_page, page_no, zip_size, IBUF_BITMAP_FREE,
&bitmap_mtr);
if (buffered + entry_size + page_dir_calc_reserved_space(1)
> ibuf_index_page_calc_free_from_bits(zip_size, bits)) {
/* Release the bitmap page latch early. */
ibuf_mtr_commit(&bitmap_mtr);
/* It may not fit */
do_merge = TRUE;
ibuf_get_merge_page_nos(FALSE,
btr_pcur_get_rec(&pcur), &mtr,
space_ids, space_versions,
page_nos, &n_stored);
goto fail_exit;
}
}
~~~
其中`ibuf_bitmap_page_get_bits`函数根据space id 和page no 获取对应的bitmap page,找到空闲空间描述信息;如果本次插入操作可能超出限制,则从当前cursor位置附近开始,触发一次异步的ibuf merge,目的是尽量将当前page的缓存操作做一次合并。
在正常的对物理页的DML过程中,如果page内空间发生了变化,总是需要去更新对应的IBUF_BITMAP_FREE值。参考函数:`btr_compress`、`btr_cur_optimistic_insert`。
2. IBUF_BITMAP_BUFFERED:用于表示该page是否有操作缓存,在`ibuf_insert_low`函数中,准备插入ibuf btree前设置成true。二级索引物理页读入内存时会根据该标记位判断是否需要进行ibuf merge操作。
3. IBUF_BITMAP_IBUF:表示该数据页是否是ibuf btree的一部分,该标记位主要用于异步AIO读操作。InnoDB专门为change buffer模块分配了一个后台AIO线程,如果page属于change buffer的b树,则使用该线程做异步读,参考函数:`ibuf_page_low`
## 操作类型
InnoDB change buffer可以对三种类型的操作进行缓存:INSERT、DELETE-MARK 、DELETE操作,前两种对应用户线程操作,第三种则由purge操作触发。
用户可以通过参数innodb_change_buffering来控制缓存何种操作:
~~~
/** Allowed values of innodb_change_buffering */
static const char* innobase_change_buffering_values[IBUF_USE_COUNT] = {
"none", /* IBUF_USE_NONE */
"inserts", /* IBUF_USE_INSERT */
"deletes", /* IBUF_USE_DELETE_MARK */
"changes", /* IBUF_USE_INSERT_DELETE_MARK */
"purges", /* IBUF_USE_DELETE */
"all" /* IBUF_USE_ALL */
};
~~~
innodb_change_buffering默认值为all,表示缓存所有操作。注意由于在二级索引上的更新操作总是先delete-mark,再insert新记录,因此update会产生两条ibuf entry。
## 缓存条件
只有满足一定条件时,操作才会被缓存,所有对ibuf操作的判断,都从`btr_cur_search_to_nth_level`入口,该函数用于定位到btree上满足条件的记录,大概的判断条件如下:
1. 用户设置了选项innodb_change_buffering;
2. 只有叶子节点才会去考虑是否使用ibuf;
3. 对于聚集索引,不可以缓存操作;
4. 对于唯一二级索引(unique key),由于索引记录具有唯一性,因此无法缓存插入操作,但可以缓存删除操作;
5. 表上没有flush 操作,例如执行flush table for export时,不允许对表进行 ibuf 缓存 (通过`dict_table_t::quiesce` 进行标识)
参考函数:`ibuf_should_try`:
当满足ibuf缓存条件时,会使用两种模式去尝试获取数据页:
~~~
BUF_GET_IF_IN_POOL: 如果数据页在内存中,则获取page并返回,否则返回NULL;
BUF_GET_IF_IN_POOL_OR_WATCH:如果数据页在内存中,则获取page并返回,否则为请求的page设置一个`sentinel`(buf_pool_watch_set),相当于标记这个page,表示这个page上的记录正在被purge。(下一小节介绍)
~~~
前者是前台用户线程触发,后者为purge线程在物理清除无效数据时触发,如果数据已经在内存中了,则不进行缓存。随后进入函数ibuf_insert,经过一系列的检查后(不可产生空page 和索引分裂、未超出最大ibuf size限制)执行操作缓存。
## purge操作缓存
对于purge操作,当page不存在于内存时设置的sentinel是什么鬼?它是如何设置的,什么时候会被清理掉,这几个问题涉及到purge操作的缓存流程:
1. 如何设置sentinel
当purge线程尝试读入page时,若数据页不在buffer pool中,则调用函数`buf_pool_watch_set`,分为两步:
* Step1: 首先检查page hash,如果存在于page hash中:1)若未被设置成sentinel (别的线程将数据页读入内存时会清理掉对应标记),返回数据页;2)否则返回NULL;
* Step2: 若page hash中不存在,则从`buf_pool_t::watch`数组中找到一个空闲的(状态为BUF_BLOCK_POOL_WATCH)page控制结构体对象buf_page_t,将其状态设置为BUF_BLOCK_ZIP_PAGE,初始化相关变量,并插入到page hash中。`buf_pool_t::watch`数组的大小为purge线程的个数,这意味着即使所有purge线程同时访问同一个buffer pool instance,总会拥有一个空闲的watch数组对象。
2. 判断是否可以缓存purge操作
当设置sentinel并返回后,在决定缓存purge之前,需要去判断是否有别的线程对同一条记录缓存了新的操作,举个简单的例子:
* Step 1: delete-mark X (sec index) //session 1
* Step 2: insert X (clust index) //session 1
* Step 3: delete X(sec index) //purge thread
* Step 4: insert X (sec index) //session 1
如果二级索引页在内存中,那么Step 3 和Step4必然是有序的,因为需要获取block锁才能进行数据变更操作。但数据页不在内存时,就需要确保Step 4在Step 3之后执行。因此在缓存purge操作之前,需要根据当前要清理的记录,找到对应的聚集索引记录,并检查相比当前purge线程的readview是否有新版本的聚集索引记录(即有新的插入操作发生)。
如果检查到有新的插入,则本次purge操作直接放弃。因为当符合一定条件时,Step 4的操作可以直接把Step1产生的记录删除标记清除掉,重用物理空间。
参考函数:`row_purge_poss_sec`:
但是注意上述检查流程结束时,会在函数`row_purge_poss_sec`中将mtr提交掉,对应的聚集索引页的Latch会被释放掉,这意味着可能出现如下序列:
* Step 1: delete-mark X;
* Step 2: delete X,purge线程为其设置watch,并完成在函数`row_purge_poss_sec`中的检查,准备插入ibuf
* Step 3: insert X,索引页不在内存,准备插入ibuf
在函数`ibuf_insert`中,针对IBUF_OP_INSERT和IBUF_OP_DELETE_MARK操作,会去检查是否对应的二级索引页被设置成sentinel(`buf_page_get_also_watch`),如果是的话,表明当前有一个pending的purge操作,目前的处理逻辑是放弃insert和delete-mark的缓存操作,转而读取物理页。
综上,如果purge操作先进入`ibuf_insert`,则对应二级索引页的watch必然被设置,insert操作将放弃缓存,转而尝试读入索引页;如果insert先进入`ibuf_insert`,则purge操作的缓存放弃。
即使Purge线程完成一系列检查,进入缓存阶段,这时候用户线程依旧可能会去读入物理页;有没有可能导致purge操作丢失呢 ?答案是否定的!因为purge线程在缓存操作时先将cursor定位到ibuf btree上,对应的ibuf page已将加上latch;而用户线程如果读入物理页,为了merge ibuf entry,也需要请求page latch;当purge线程在拿到latch后,会再检查一次看看物理页是否已读入内存(`buf_pool_watch_occurred`),如果是的话,则放弃本次缓存。
3. 何时清理sentinel
有两种情况会清理sentinel:
* 第一种情况是purge操作完成缓存后(或者判断无法进行purge缓存)进行清理;
* 第二种情况是从磁盘读入文件块的时候,会调用`buf_page_init_for_read->buf_page_init`初始化一个page对象。这时候会做一个判断,如果将被读入的page被设置为sentinel(在watch数组中被设置),则调用`buf_pool_watch_remove`将其从page hash中移除,对应`bp->watch`的数据元素被重置成空闲状态。
## ibuf merge
有以下几种场景会触发ibuf merge操作:
1. 用户线程选择二级索引进行数据查询,这时候必须要读入二级索引页,相应的ibuf entry需要merge到Page中。
2. 当尝试缓存插入操作时,如果预估page的空间不足,可能导致索引分裂,则定位到尝试缓存的page no在ibuf btree中的位置,最多merge 8个(IBUF_MERGE_AREA) page,merge方式为异步,即发起异步读索引页请求。
参考函数:`ibuf_insert_low —> ibuf_get_merge_page_nos_func`
3. 若当前ibuf tree size 超过ibuf->max_size + 10(`IBUF_CONTRACT_DO_NOT_INSERT`)时,执行一次同步的ibuf merge(`ibuf_contract`),merge的page no为随机定位的cursor,最多一次merge 8个page,同时放弃本次缓存。
其中`ibuf->max_size`默认为25% * buffer pool size,百分比由参数`innodb_change_buffer_max_size`控制,可动态调整。
参考函数:`ibuf_insert_low —> ibuf_contract`
4. 若本次插入ibuf操作可能产生ibuf btree索引分裂(BTR_MODIFY_TREE)时:
* 当前ibuf->size max_size, 不做处理;
* 当前ibuf->size >= ibuf->max_size + 5 (IBUF_CONTRACT_ON_INSERT_SYNC)时,执行一次同步ibuf merge,位置随机;
* 当前Ibuf->size介于ibuf->max_size 和ibuf->max_size +5 之间时。执行一次异步ibuf merge,位置随机。
参考函数:`ibuf_insert_low —> ibuf_contract_after_insert`
5. 后台master线程发起merge
master线程有三种工作状态:
IDLE:实例处于空闲状态,以100%的io capacity来作merge操作:
~~~
n_pages = PCT_IO(100);
~~~
相当于一次merge的page数等于innodb_io_capacity
参考函数:`srv_master_do_idle_tasks`
ACTIVE:实例处于活跃状态,这时候会以如下算法计算需要merge的page数:
~~~
/* By default we do a batch of 5% of the io_capacity */
n_pages = PCT_IO(5);
mutex_enter(&ibuf_mutex);
/* If the ibuf->size is more than half the max_size
then we make more agreesive contraction.
+1 is to avoid division by zero. */
if (ibuf->size > ibuf->max_size / 2) {
ulint diff = ibuf->size - ibuf->max_size / 2;
n_pages += PCT_IO((diff * 100)
/ (ibuf->max_size + 1));
}
mutex_exit(&ibuf_mutex);
~~~
可见在系统active时,会以比较温和的方式去做merge,如果当前ibuf btree size超过最大值的一半,则尝试多做一些merge操作。
参考函数: `srv_master_do_active_tasks`
SHUTDOWN:当执行slow shutdown时,会强制做一次全部的ibuf merge
参考函数:`srv_master_do_shutdown_tasks`
6. 对某个表执行flush table 操作时,会触发对该表的强制ibuf merge,例如执行:
~~~
flush table tbname for export;
flush table tbname with read lock;
~~~
实际上强制ibuf merge主要是为flush for export准备的,当执行该命令后,为了保证能安全的将ibd拷贝到其他实例上, 需要对该表应用全部的ibuf 缓存。
参考函数:`row_quiesce_table_start`
## “著名” bug
在change buffer的应用史上,最著名的bug要属 [bug#61104](https://bugs.mysql.com/bug.php?id=61104),其现象为当实例意外crash后,无法从崩溃中恢复,错误日志中报如下断言:
~~~
InnoDB: Failing assertion: page_get_n_recs(page) > 1
~~~
最初官方花了很长的时间都没有找到这个问题的root cause,只能加了一些代码逻辑避免不断crash重启,让用户有机会登录实例,重建二级索引。
后来Percona的开发人员Alexey Kopytov在[bug#66819](https://bugs.mysql.com/bug.php?id=66819) 提出了该问题的根本原因,指出ibuf entry的删除和merge 并不是一个原子的操作(即处于两个mtr事务中),当merge ibuf的mtr提交后crash,就可能在重启时重复做ibuf merge。如果上次执行DELETE操作导致对应索引页上记录数只剩下一条,第二次apply时认为本次操作会产生空页,从而导致断言错误。
官方很快根据Alexey的意见做了修复,修复方式也比较简单:
1. 在第一个mtr里,merge ibuf entry 到二级索引页,并标记删除ibuf entry,提交mtr;
2. 在第二个mtr里,执行真正的悲观删除ibuf entry;
3. 在执行merge操作前,对于被delete mark的ibuf entry,不做merge操作。
具体的参考函数:`ibuf_merge_or_delete_for_page` 和 `ibuf_delete_rec`。
比较乌龙的是,我们发现第一次修复并没有处理purge线程产生的delete缓存;我们将该发现公布到社区,很快得到了响应,并由上游快速fix掉了,因此完整的补丁分布在两个版本中:
[官方第一次fix(MySQL5.5.29)](http://bazaar.launchpad.net/~mysql/mysql-server/5.5/revision/3979)
[官方第二次fix(MySQL5.5.31)](http://bazaar.launchpad.net/~mysql/mysql-server/5.5/revision/4177)
- 数据库内核月报目录
- 数据库内核月报 - 2016/09
- MySQL · 社区贡献 · AliSQL那些事儿
- PetaData · 架构体系 · PetaData第二代低成本存储体系
- MySQL · 社区动态 · MariaDB 10.2 前瞻
- MySQL · 特性分析 · 执行计划缓存设计与实现
- PgSQL · 最佳实践 · pg_rman源码浅析与使用
- MySQL · 捉虫状态 · bug分析两例
- PgSQL · 源码分析 · PG优化器浅析
- MongoDB · 特性分析· Sharding原理与应用
- PgSQL · 源码分析 · PG中的无锁算法和原子操作应用一则
- SQLServer · 最佳实践 · TEMPDB的设计
- 数据库内核月报 - 2016/08
- MySQL · 特性分析 ·MySQL 5.7新特性系列四
- PgSQL · PostgreSQL 逻辑流复制技术的秘密
- MySQL · 特性分析 · MyRocks简介
- GPDB · 特性分析· Greenplum 备份架构
- SQLServer · 最佳实践 · RDS for SQLServer 2012权限限制提升与改善
- TokuDB · 引擎特性 · REPLACE 语句优化
- MySQL · 专家投稿 · InnoDB物理行中null值的存储的推断与验证
- PgSQL · 实战经验 · 旋转门压缩算法在PostgreSQL中的实现
- MySQL · 源码分析 · Query Cache并发处理
- PgSQL · 源码分析· pg_dump分析
- 数据库内核月报 - 2016/07
- MySQL · 特性分析 ·MySQL 5.7新特性系列三
- MySQL · 特性分析 · 5.7 代价模型浅析
- PgSQL · 实战经验 · 分组TOP性能提升44倍
- MySQL · 源码分析 · 网络通信模块浅析
- MongoDB · 特性分析 · 索引原理
- SQLServer · 特性分析 · XML与JSON应用比较
- MySQL · 最佳实战 · 审计日志实用案例分析
- MySQL · 性能优化 · 条件下推到物化表
- MySQL · 源码分析 · Query Cache内部剖析
- MySQL · 捉虫动态 · 备库1206错误问题说明
- 数据库内核月报 - 2016/06
- MySQL · 特性分析 · innodb 锁分裂继承与迁移
- MySQL · 特性分析 ·MySQL 5.7新特性系列二
- PgSQL · 实战经验 · 如何预测Freeze IO风暴
- GPDB · 特性分析· Filespace和Tablespace
- MariaDB · 新特性 · 窗口函数
- MySQL · TokuDB · checkpoint过程
- MySQL · 特性分析 · 内部临时表
- MySQL · 最佳实践 · 空间优化
- SQLServer · 最佳实践 · 数据库实现大容量插入的几种方式
- 数据库内核月报 - 2016/05
- MySQL · 引擎特性 · 基于InnoDB的物理复制实现
- MySQL · 特性分析 · MySQL 5.7新特性系列一
- PostgreSQL · 特性分析 · 逻辑结构和权限体系
- MySQL · 特性分析 · innodb buffer pool相关特性
- PG&GP · 特性分析 · 外部数据导入接口实现分析
- SQLServer · 最佳实践 · 透明数据加密在SQLServer的应用
- MySQL · TokuDB · 日志子系统和崩溃恢复过程
- MongoDB · 特性分析 · Sharded cluster架构原理
- PostgreSQL · 特性分析 · 统计信息计算方法
- MySQL · 捉虫动态 · left-join多表导致crash
- 数据库内核月报 - 2016/04
- MySQL · 参数故事 · innodb_additional_mem_pool_size
- GPDB · 特性分析 · Segment事务一致性与异常处理
- GPDB · 特性分析 · Segment 修复指南
- MySQL · 捉虫动态 · 并行复制外键约束问题二
- PgSQL · 性能优化 · 如何潇洒的处理每天上百TB的数据增量
- Memcached · 最佳实践 · 热点 Key 问题解决方案
- MongoDB · 最佳实践 · 短连接Auth性能优化
- MySQL · 最佳实践 · RDS 只读实例延迟分析
- MySQL · TokuDB · TokuDB索引结构--Fractal Tree
- MySQL · TokuDB · Savepoint漫谈
- 数据库内核月报 - 2016/03
- MySQL · TokuDB · 事务子系统和 MVCC 实现
- MongoDB · 特性分析 · MMAPv1 存储引擎原理
- PgSQL · 源码分析 · 优化器逻辑推理
- SQLServer · BUG分析 · Agent 链接泄露分析
- Redis · 特性分析 · AOF Rewrite 分析
- MySQL · BUG分析 · Rename table 死锁分析
- MySQL · 物理备份 · Percona XtraBackup 备份原理
- GPDB · 特性分析· GreenPlum FTS 机制
- MySQL · 答疑解惑 · 备库Seconds_Behind_Master计算
- MySQL · 答疑解惑 · MySQL 锁问题最佳实践
- 数据库内核月报 - 2016/02
- MySQL · 引擎特性 · InnoDB 文件系统之文件物理结构
- MySQL · 引擎特性 · InnoDB 文件系统之IO系统和内存管理
- MySQL · 特性分析 · InnoDB transaction history
- PgSQL · 会议见闻 · PgConf.Russia 2016 大会总结
- PgSQL · 答疑解惑 · PostgreSQL 9.6 并行查询实现分析
- MySQL · TokuDB · TokuDB之黑科技工具
- PgSQL · 性能优化 · PostgreSQL TPC-C极限优化玩法
- MariaDB · 版本特性 · MariaDB 的 GTID 介绍
- MySQL · 特性分析 · 线程池
- MySQL · 答疑解惑 · mysqldump tips 两则
- 数据库内核月报 - 2016/01
- MySQL · 引擎特性 · InnoDB 事务锁系统简介
- GPDB · 特性分析· GreenPlum Primary/Mirror 同步机制
- MySQL · 专家投稿 · MySQL5.7 的 JSON 实现
- MySQL · 特性分析 · 优化器 MRR & BKA
- MySQL · 答疑解惑 · 物理备份死锁分析
- MySQL · TokuDB · Cachetable 的工作线程和线程池
- MySQL · 特性分析 · drop table的优化
- MySQL · 答疑解惑 · GTID不一致分析
- PgSQL · 特性分析 · Plan Hint
- MariaDB · 社区动态 · MariaDB on Power8 (下)
- 数据库内核月报 - 2015/12
- MySQL · 引擎特性 · InnoDB 事务子系统介绍
- PgSQL · 特性介绍 · 全文搜索介绍
- MongoDB · 捉虫动态 · Kill Hang问题排查记录
- MySQL · 参数优化 ·RDS MySQL参数调优最佳实践
- PgSQL · 特性分析 · 备库激活过程分析
- MySQL · TokuDB · 让Hot Backup更完美
- PgSQL · 答疑解惑 · 表膨胀
- MySQL · 特性分析 · Index Condition Pushdown (ICP)
- MariaDB · 社区动态 · MariaDB on Power8
- MySQL · 特性分析 · 企业版特性一览
- 数据库内核月报 - 2015/11
- MySQL · 社区见闻 · OOW 2015 总结 MySQL 篇
- MySQL · 特性分析 · Statement Digest
- PgSQL · 答疑解惑 · PostgreSQL 用户组权限管理
- MySQL · 特性分析 · MDL 实现分析
- PgSQL · 特性分析 · full page write 机制
- MySQL · 捉虫动态 · MySQL 外键异常分析
- MySQL · 答疑解惑 · MySQL 优化器 range 的代价计算
- MySQL · 捉虫动态 · ORDER/GROUP BY 导致 mysqld crash
- MySQL · TokuDB · TokuDB 中的行锁
- MySQL · 捉虫动态 · order by limit 造成优化器选择索引错误
- 数据库内核月报 - 2015/10
- MySQL · 引擎特性 · InnoDB 全文索引简介
- MySQL · 特性分析 · 跟踪Metadata lock
- MySQL · 答疑解惑 · 索引过滤性太差引起CPU飙高分析
- PgSQL · 特性分析 · PG主备流复制机制
- MySQL · 捉虫动态 · start slave crash 诊断分析
- MySQL · 捉虫动态 · 删除索引导致表无法打开
- PgSQL · 特性分析 · PostgreSQL Aurora方案与DEMO
- TokuDB · 捉虫动态 · CREATE DATABASE 导致crash问题
- PgSQL · 特性分析 · pg_receivexlog工具解析
- MySQL · 特性分析 · MySQL权限存储与管理
- 数据库内核月报 - 2015/09
- MySQL · 引擎特性 · InnoDB Adaptive hash index介绍
- PgSQL · 特性分析 · clog异步提交一致性、原子操作与fsync
- MySQL · 捉虫动态 · BUG 几例
- PgSQL · 答疑解惑 · 诡异的函数返回值
- MySQL · 捉虫动态 · 建表过程中crash造成重建表失败
- PgSQL · 特性分析 · 谈谈checkpoint的调度
- MySQL · 特性分析 · 5.6 并行复制恢复实现
- MySQL · 备库优化 · relay fetch 备库优化
- MySQL · 特性分析 · 5.6并行复制事件分发机制
- MySQL · TokuDB · 文件目录谈
- 数据库内核月报 - 2015/08
- MySQL · 社区动态 · InnoDB Page Compression
- PgSQL · 答疑解惑 · RDS中的PostgreSQL备库延迟原因分析
- MySQL · 社区动态 · MySQL5.6.26 Release Note解读
- PgSQL · 捉虫动态 · 执行大SQL语句提示无效的内存申请大小
- MySQL · 社区动态 · MariaDB InnoDB表空间碎片整理
- PgSQL · 答疑解惑 · 归档进程cp命令的core文件追查
- MySQL · 答疑解惑 · open file limits
- MySQL · TokuDB · 疯狂的 filenum++
- MySQL · 功能分析 · 5.6 并行复制实现分析
- MySQL · 功能分析 · MySQL表定义缓存
- 数据库内核月报 - 2015/07
- MySQL · 引擎特性 · Innodb change buffer介绍
- MySQL · TokuDB · TokuDB Checkpoint机制
- PgSQL · 特性分析 · 时间线解析
- PgSQL · 功能分析 · PostGIS 在 O2O应用中的优势
- MySQL · 引擎特性 · InnoDB index lock前世今生
- MySQL · 社区动态 · MySQL内存分配支持NUMA
- MySQL · 答疑解惑 · 外键删除bug分析
- MySQL · 引擎特性 · MySQL logical read-ahead
- MySQL · 功能介绍 · binlog拉取速度的控制
- MySQL · 答疑解惑 · 浮点型的显示问题
- 数据库内核月报 - 2015/06
- MySQL · 引擎特性 · InnoDB 崩溃恢复过程
- MySQL · 捉虫动态 · 唯一键约束失效
- MySQL · 捉虫动态 · ALTER IGNORE TABLE导致主备不一致
- MySQL · 答疑解惑 · MySQL Sort 分页
- MySQL · 答疑解惑 · binlog event 中的 error code
- PgSQL · 功能分析 · Listen/Notify 功能
- MySQL · 捉虫动态 · 任性的 normal shutdown
- PgSQL · 追根究底 · WAL日志空间的意外增长
- MySQL · 社区动态 · MariaDB Role 体系
- MySQL · TokuDB · TokuDB数据文件大小计算
- 数据库内核月报 - 2015/05
- MySQL · 引擎特性 · InnoDB redo log漫游
- MySQL · 专家投稿 · MySQL数据库SYS CPU高的可能性分析
- MySQL · 捉虫动态 · 5.6 与 5.5 InnoDB 不兼容导致 crash
- MySQL · 答疑解惑 · InnoDB 预读 VS Oracle 多块读
- PgSQL · 社区动态 · 9.5 新功能BRIN索引
- MySQL · 捉虫动态 · MySQL DDL BUG
- MySQL · 答疑解惑 · set names 都做了什么
- MySQL · 捉虫动态 · 临时表操作导致主备不一致
- TokuDB · 引擎特性 · zstd压缩算法
- MySQL · 答疑解惑 · binlog 位点刷新策略
- 数据库内核月报 - 2015/04
- MySQL · 引擎特性 · InnoDB undo log 漫游
- TokuDB · 产品新闻 · RDS TokuDB小手册
- PgSQL · 社区动态 · 说一说PgSQL 9.4.1中的那些安全补丁
- MySQL · 捉虫动态 · 连接断开导致XA事务丢失
- MySQL · 捉虫动态 · GTID下slave_net_timeout值太小问题
- MySQL · 捉虫动态 · Relay log 中 GTID group 完整性检测
- MySQL · 答疑释惑 · UPDATE交换列单表和多表的区别
- MySQL · 捉虫动态 · 删被引用索引导致crash
- MySQL · 答疑释惑 · GTID下auto_position=0时数据不一致
- 数据库内核月报 - 2015/03
- MySQL · 答疑释惑· 并发Replace into导致的死锁分析
- MySQL · 性能优化· 5.7.6 InnoDB page flush 优化
- MySQL · 捉虫动态· pid file丢失问题分析
- MySQL · 答疑释惑· using filesort VS using temporary
- MySQL · 优化限制· MySQL index_condition_pushdown
- MySQL · 捉虫动态·DROP DATABASE外键约束的GTID BUG
- MySQL · 答疑释惑· lower_case_table_names 使用问题
- PgSQL · 特性分析· Logical Decoding探索
- PgSQL · 特性分析· jsonb类型解析
- TokuDB ·引擎机制· TokuDB线程池
- 数据库内核月报 - 2015/02
- MySQL · 性能优化· InnoDB buffer pool flush策略漫谈
- MySQL · 社区动态· 5.6.23 InnoDB相关Bugfix
- PgSQL · 特性分析· Replication Slot
- PgSQL · 特性分析· pg_prewarm
- MySQL · 答疑释惑· InnoDB丢失自增值
- MySQL · 答疑释惑· 5.5 和 5.6 时间类型兼容问题
- MySQL · 捉虫动态· 变量修改导致binlog错误
- MariaDB · 特性分析· 表/表空间加密
- MariaDB · 特性分析· Per-query variables
- TokuDB · 特性分析· 日志详解
- 数据库内核月报 - 2015/01
- MySQL · 性能优化· Group Commit优化
- MySQL · 新增特性· DDL fast fail
- MySQL · 性能优化· 启用GTID场景的性能问题及优化
- MySQL · 捉虫动态· InnoDB自增列重复值问题
- MySQL · 优化改进· 复制性能改进过程
- MySQL · 谈古论今· key分区算法演变分析
- MySQL · 捉虫动态· mysql client crash一例
- MySQL · 捉虫动态· 设置 gtid_purged 破坏AUTO_POSITION复制协议
- MySQL · 捉虫动态· replicate filter 和 GTID 一起使用的问题
- TokuDB·特性分析· Optimize Table
- 数据库内核月报 - 2014/12
- MySQL· 性能优化·5.7 Innodb事务系统
- MySQL· 踩过的坑·5.6 GTID 和存储引擎那会事
- MySQL· 性能优化·thread pool 原理分析
- MySQL· 性能优化·并行复制外建约束问题
- MySQL· 答疑释惑·binlog event有序性
- MySQL· 答疑释惑·server_id为0的Rotate
- MySQL· 性能优化·Bulk Load for CREATE INDEX
- MySQL· 捉虫动态·Opened tables block read only
- MySQL· 优化改进· GTID启动优化
- TokuDB· Binary Log Group Commit with TokuDB
- 数据库内核月报 - 2014/11
- MySQL· 捉虫动态·OPTIMIZE 不存在的表
- MySQL· 捉虫动态·SIGHUP 导致 binlog 写错
- MySQL· 5.7改进·Recovery改进
- MySQL· 5.7特性·高可用支持
- MySQL· 5.7优化·Metadata Lock子系统的优化
- MySQL· 5.7特性·在线Truncate undo log 表空间
- MySQL· 性能优化·hash_scan 算法的实现解析
- TokuDB· 版本优化· 7.5.0
- TokuDB· 引擎特性· FAST UPDATES
- MariaDB· 性能优化·filesort with small LIMIT optimization
- 数据库内核月报 - 2014/10
- MySQL· 5.7重构·Optimizer Cost Model
- MySQL· 系统限制·text字段数
- MySQL· 捉虫动态·binlog重放失败
- MySQL· 捉虫动态·从库OOM
- MySQL· 捉虫动态·崩溃恢复失败
- MySQL· 功能改进·InnoDB Warmup特性
- MySQL· 文件结构·告别frm文件
- MariaDB· 新鲜特性·ANALYZE statement 语法
- TokuDB· 主备复制·Read Free Replication
- TokuDB· 引擎特性·压缩
- 数据库内核月报 - 2014/09
- MySQL· 捉虫动态·GTID 和 DELAYED
- MySQL· 限制改进·GTID和升级
- MySQL· 捉虫动态·GTID 和 binlog_checksum
- MySQL· 引擎差异·create_time in status
- MySQL· 参数故事·thread_concurrency
- MySQL· 捉虫动态·auto_increment
- MariaDB· 性能优化·Extended Keys
- MariaDB·主备复制·CREATE OR REPLACE
- TokuDB· 参数故事·数据安全和性能
- TokuDB· HA方案·TokuDB热备
- 数据库内核月报 - 2014/08
- MySQL· 参数故事·timed_mutexes
- MySQL· 参数故事·innodb_flush_log_at_trx_commit
- MySQL· 捉虫动态·Count(Distinct) ERROR
- MySQL· 捉虫动态·mysqldump BUFFER OVERFLOW
- MySQL· 捉虫动态·long semaphore waits
- MariaDB·分支特性·支持大于16K的InnoDB Page Size
- MariaDB·分支特性·FusionIO特性支持
- TokuDB· 性能优化·Bulk Fetch
- TokuDB· 数据结构·Fractal-Trees与LSM-Trees对比
- TokuDB·社区八卦·TokuDB团队