1. 绑定变量作为一种优化查询处理的方法,在性能上有利有弊,是一把双刃剑。它的优势在于可以共享库缓存中的父游标,从而避免了硬解析及相关的开销;劣势在于因绑定变量扫视增加了查询优化器选择(非常)低效执行计划的风险,即使支持自适应游标共享,也引入了游标感知判断和谓词选择率估算的代价,而且在生成高效的执行计划前至少有一次是无效率的。因此,是否使用绑定变量,需要衡量实际字面值与处理数据量带来的解析执行的收益与损害,当损害大于收益时就不应该使用,反之当处理较少数据硬解析耗时比执行多时,就可以使用了
2. 存储快照一般有三种层次:物理卷、文件系统和应用程序
◆ 物理卷快照基于卷扇区映射表实现,宜采用CoFW法,因为它不必每次写io都去遍历映射表,比RoFW快
◆ 文件系统快照基于inode树即元数据复制实现,每当写io时更新快照或源inode的指向,必要时向上回溯至根inode。有的文件系统比如NetApp公司的WAFL则更优,只须复制根inode,因为每次写io时它会变但其下所有的inode不会变
◆ 应用程序快照最典型的就是数据库,原理本质与上述两种一样,基于页改变位图,当page首次(相对于快照创建时刻)改变时拷贝到快照文件(一种稀疏文件),另外当撤消未提交事务或回滚事务时也会发生拷贝(此时快照慢慢不再稀疏),这是为了保证快照的可用一致性
3.
数据块的加锁有单机和分布式两种情景,前者是为了同步单实例事务的并发,后者是为了协调分布式事务的同步,并与缓存一致性协议紧密联系。undo,redo,undo/redo三种日志对数据脏块与提交日志记录落盘的顺序要求各不同,因此恢复方式不同。脱服务器备份架构比较好,具有不占用应用服务器资源的优势,而微软的vss可传输卷影拷贝提供了这一支持,足见其技术的先进前瞻性
4. Oracle的
实例恢复完全靠在线重做日志,介质恢复必须靠归档重做日志,以及在线重做日志。然而在线重做日志是有限数量的,那么Oracle是怎样保证宕机经实例恢复后不丢数据?答案是检查点。检查点是数据库中一个很重要的机制,被重做日志切换触发,由DBWn执行刷新脏块,并清除老的无用的在线重做日志,以允许被覆盖
5. Linux内核的swap高速缓存和其它的缓存(比如page缓存)不太一样,因为它存在的主要原因不是为了减少磁盘IO提高性能,而是解决换入换出共享匿名页同步即并发swap的问题。那么它是唯一的方法吗?不一定,可以遍历所有的anon_vma链表,查找匿名页对应的页框是否已建立,但该方法没有swap缓存快。当然,在换入操作很多的情景,swap缓存确实能提高系统性能
6. Linux内存回收的核心是LRU链表,Oracle的buffer cache也有个LRU,这两种LRU的共同点是引用计数(标志)和非活跃链表,引用计数会影响一个对象是否移到非活跃链表,非活跃链表用于回收或覆盖这个对象。对于Linux这个对象是页框,移到非活跃链表取决于swap tendency;而Oracle则是数据块buffer及其TCH
7. Linux内核中的反向映射让我想起了Oracle中的反向键索引,它们的共同点都是为了高性能,前者是为了快速定位引用同一页框的所有页表项,从而方便共享内存的回收;后者是为了减少右侧索引叶块的竞争,从而降低缓冲区忙等待、提高并发量
8. mvcc与read uncommitted(简称RU)隔离级别的关系究竟如何?这取决于现代数据库的实现。对于Oracle,RU和RC的读实现都基于mvcc实现,换句话说Oracle其实没有脏读;对于MySQL innodb引擎,mvcc不适用于RU而只适用于RC/RR级别,因为RC/RR必须读取修改已提交的数据,但基准点不同,前者查询开始时、后者事务开始时,而RU则可读取未提交的数据,当然用mvcc模拟实现RU应该也可以,只需要读取当前新版本而非旧版本
9. 借助内核page cache的数据库或者存储引擎,一定程度上讲,是粗暴懒惰的表现,这会导致系统负载比较重的情况下,io性能很差。所以为高性能,必须得处理好direct io,设计self cache,这样一来,就避免了浪费在原先内核页缓存的页框,避免处理内核页缓存和预读的多余指令而提高了系统调用read和write的效率,同时减少了一次数据拷贝
10. SQL半连接的本质是在内连接的基础上对内表去重,即使内表有符合多个连接条件的元组,也只匹配一条,从而减少了连接返回的结果集。一般地,简单的in、exists和any子句,都采用半连接实现,但若内表本身保证了唯一性,则半连接可消除转为内连接实现,或者内表数据量很小且外表存在索引,那么也会消除半连接,生成由内表驱动外表,外表走索引的执行计划。由此一例看出,SQL优化器偏爱内连接,因为内连接带来了驱动表选择和谓词下推的灵活,便于产生更优的执行计划
11. 从Oracle数据库内核角度讲,游标代表SQL语句的句柄,包含了依赖对象及执行计划等信息,它相当于linux的文件描述符和windows的句柄。打开或缓存的游标是指对应SQL语句所占的内存(父游标句柄、父堆0和子游标句柄的chunk)被加上kgl lock和pin锁,意味着第三次后解析同样的SQL不必再从library cache hash chain中加锁查找而直接从PGA的子堆6地址中获取并调用执行计划,如此优化提高了并发度加快了查询,这正是软软解析;软软解析前必须软解析2次,目的是将library cache的执行计划在PGA中做一份链接,软解析前必须硬解析,目的是将执行计划放在library cache中。然而,如果共享池空闲内存不足,或者依赖对象发生DDL操作导致执行计划失效,那么执行计划所占chunk可以被覆盖释放,这样一来,软(软)解析时就需要重新生成执行计划了
12. Oracle的内存管理粗略地类似于Linux内核,所不同的是内存分配单元,前者叫granule通常大小4M~16M,后者叫page通常4K;数据块缓冲的分配类似伙伴算法,共享池(主要用于sql缓存)的chunk分配类似slab算法,共享池中的保留池类似基于slab的内存池
13. Oracle数据库究竟是怎样构建表数据块的读一致性版本?这是个比较复杂、细致和有趣的问题,核心流程如下
◆ 克隆数据块,若不存在则先从磁盘读,下面几步以克隆块为目标
◆ 根据ITL中的flag及lck,对所有已提交的事务做清除操作,即延迟块清除。延迟块清除为了获取足够精确的提交SCN填充到ITL,分2种情况,若事务表槽没被覆盖,则直接用其提交SCN;否则先从事务控制区获取SCN,并判断对于上界提交是否足够精确,若不够则需要回滚事务表一直找到合适的SCN或报错ORA-01555
◆ 根据ITL中的uba,反向更改所有未提交的事务,也就是应用事务的undo记录
◆ 根据ITL中的SCN,不断反向更改大于目标SCN的已提交事务,直至遇见合适的已提交事务。这里也是应用undo记录,但不同的是,除了应用行数据,还会从事务的第一个undo记录找到先前即前一个已提交事务的ITL项拷贝回当前块的对应ITL项
14. Oracle的多版本控制机制,为dml不仅提供了一致且正确的结果,还提高了并发性,可谓鱼和熊掌兼得。那么它的缺点是什么?可能会导致热表的IO增高,因为读一致性需要不断回滚多个事务对数据块的修改,直到查询开始时的数据。事务隔离级别read committed与read uncommitted的相同是不会脏读,区别是前者会不可重复读或幻读
15. Sql*plus的ARRAYSIZE对查询数据性能有重要的影响,这个值过大过小都不好,而是要接近一个数据块所拥有的行数,如此仅一次逻辑IO就拿到了一批行。那么设置合适的ARRAYSIZE就一定能提高性能吗?不一定,还要看所查询的表使用了什么索引列及表数据在磁盘上的物理布局,若数据分散即聚簇因子低,则优化器会选用全表而非索引区间扫描,去执行这个查询
16. IOT表如果为非主键列再建索引,那么就成二级索引。这时候查询数据,需要两次扫描,一是扫描二级索引得到IOT中的位置,二是扫描IOT本身匹配那个位置,之所以这样是因为行记录在IOT中的位置会变。而堆组织表,仅需一次扫描索引结构,得到rowid,再直接读磁盘获取行记录。因此IOT上再建二级索引,并非明智的选择
17. 相容性矩阵是封锁调度的核心结构; 任意一个无环优先图的封锁调度都是冲突可串行化的; 基于树协议的无环优先图的封锁调度,其整个事务集合的任意一个拓扑顺序都是等价可串行化的
18. 总结解决数据库丢失更新问题的方案
◆ 对于表不会被悲观锁锁定的情景:使用基于select+update的乐观锁方法,查询保存前映像,以便定位更新。前映像列可为全列,或新增一个时间戳列作为版本列
◆ 对于表可能会被悲观锁锁定的情景:使用select…for update nowait+update的悲观锁方法,可以以全列的hash(虚拟列)来定位更新
19. 如果能够在备库上打开闪回,那么就可以做到既让生产系统没有承担闪回的开销,又能快速地为错误或故障恢复到以前某个时刻。一举两得比较完美,重做日志的创新使用真是太棒了
20. Oracle的索引聚簇表是个创新,它能将多个不同表的行按照索引列存储在同一块中,属于物理上的join,这样一来既可减少data buffer缓存的块数而提高效率,又可提高多个相关表连接查询的性能,比如通过外键约束的父子表。最典型的应用就是数据字典,数据字典对于查询优化的成本估算很重要,由此可见oracle的设计之明智,mysql的innodb只有索引组织表,sql server有堆表和索引组织表,但它们都没有索引聚簇表
21. 分布式事务处理是工程难题。Oracle的serializable串行隔离级别以乐观锁实现,所以并发度与非串行相当,需要注意的是:串行并不是说一个事务提交了才能处理下一个,而是多个事务间没有冲突表现地像只有一个事务在运行,否则Oracle的serializable级别就不存在抛出ORA-08177错误了
22. 理清read uncommitted事务隔离级别的锁策略:读不加共享锁,写加排它锁直至提交,这里的锁是指lock;块的缓冲区并发操作必须加锁,这里的锁是指latch,若不加,那脏读读到的数据可能是错的。脏读隔离级别允许读修改但未提交的行记录,这意味着读不能被写阻塞,也不能阻塞写,所以不会申请共享锁(显式锁定读除外)
23. 与MySQL不同,Oracle的行锁无需索引列的限制,是真正的行锁,其实现为数据块的属性而非传统的锁管理器,但是它需要在事务commit或rollback时才释放,如果存在慢sql,那么导致的阻塞会比较严重
24. 隔离是实现安全的一种办法,其结果常被称作“沙箱”。从这个意义上讲Oracle很明智,因为它的事务没有也不需要read uncommitted隔离级别,Oracle最低且默认的隔离级别是read committed,因为它有基于undo的多版本控制,天生非阻塞读,根本不会脏读。我想不出read uncommitted有什么好处,除了非阻塞读及可能的高并发,要谨慎脏读是危险不安全的
25. windows内存映射和linux内存映射的实现机制不太一样,前者使用了内存区section的专用数据结构而不像后者重用了页缓存,内存区的映射完全由内存管理器负责包括物理页分配及脏页面写入器,与缓存管理器无关;缓存管理器基于内存管理器维护了文件块数据的视图,并提供了自己的延迟写入器。这两种写入器即回刷,独立并行地工作
posted on 2019-11-06 11:29
春秋十二月 阅读(8030)
评论(0) 编辑 收藏 引用 所属分类:
Database