oracle数据库应用开发经验
l 日志表应该以时间做分区,方便清理
一般应用都会有一些表用来记录用户操作日志,数据变更记录,交易流水等日志型的库表。这些表最好按时间字段做分区,这样在迁移或者清理历史记录时会比较方便,借助oracle的分区交换清理特性,效率比delete高很多。
l 频繁访问的sequece应该增加cache
oracle在创建序列可以指定cache参数,如果打开这个参数,oracle就可以预先生成一些sequece,这样应用获取sequece相互争用数据块的概率就会减少,加快获取sequece速度。
l 队列表也应该做分区,减少出现高水位问题
有时我们会使用数据库表存放待处理的信息,处理完后把记录删除,像是消息队列一样。这种我们称之为队列表。这种表经常会出现高水位的问题,即某一瞬间突然涌入了很多数据,等系统把表里面记录处理完,删除后整个表访问速度还是很慢(因为高水位被上移后没恢复)。这时如果库表有分区,则不容易出现这种问题。
l 减少外键使用
在设计库表时我们一般要使用外键以辅助表示不同库表数据的关联,但在实际部署时最好不要把外键加上。一个原因是外键会影响数据插入删除效率,更重要的原因是加了外键的库表在数据清理,修复时会带来许多麻烦。
l 减少存储过程
有些程序员喜欢使用存储过程封装业务逻辑,虽然这样处理数据速度快,但把压力都留给了数据库服务器。而数据库服务器资源往往是比较有限的,而且比较难扩展。而应用服务器资源相对会丰富一些,也好扩展。所以建议尽量少使用存储过程,即使用也不要放太多业务逻辑。
l 使用绑定变量
尽可能使用绑定变量代替拼sql,这样一是减少sql注入风险,另外一个是让数据库可以复用执行计划(sql文本相同的才有可能复用),减少数据库生成执行计划的消耗。
l 使用并行
oracle提供并行技术,可以把一个sql涉及的数据集拆分成多份,交由不同进程处理,以加快数据处理速度。对于olap系统,可以考虑使用此技巧提高sql运行速度。
l 使用hint避免数据量变化过大的表
有时候我们的应用会出现一些数据变化比较大的表,有时表里面只有几十条数据,有时可能有几万,几十万条。对于这种表的访问最好使用hint强制数据库在任何情况都使用索引访问,因为在数据量小时数据库生成的执行计划可能是使用全表扫描,到后面数据发生变化时由于sql没有变,执行计划也没变,这时使用全表扫描效率就会很低。
l 使用tt 共享内存等
当一个会话需要访问一个数据块,而这个数据块正在被另一个用户从磁盘读取到内存中或者这个数据块正在被另一个会话修改时,当前的会话就需要等待,就会产生一个buffer busy waits等待,也伴随着latch争用。如果太多的会话去访问相同的数据块导致长时间的buffer busy waits等待,通常表现形式为cpu使用率很高,但吞吐量很低。造成热快的原因可能是数据库设置导致或者重复执行的sql 频繁访问一些相同的数据块导致。
l 两个大表关联查询尽量走hash join
虽然oracle提供了很多种表关联算法,但是经过实验,对两个数据量大的表连接还是使用hash连接效率最高。
l 尽量少用业务要素作为主键
不使用业务要素作为主键,可以为系统提供很多便利性。一是避免需求变更,原来。二是
l 合理使用纵表和横表设计
所谓横表就是指把一个实体的所有特性存储在一行记录,形成(id,属性1,属性2,。。。,属性n)的库表。
而纵表则是把实体属性分开多条记录存储,设计成(id,属性名称,属性值)这样的库表。
下面是一个横表和纵表的例子:
使用横表好处:
1 比较直观,查询比较方便
2 属性值可以根据属性内容设计,例如年龄用number类型存储,职业用varchar2存储
使用纵表好处:
1 避免单表字段不停扩展,oracle是行存储数据库,记录字段越多,记录扫描时消耗的io就会更多
2 增加属性比较方便
建议:对于频繁使用的属性放横表,对于不频繁使用的属性(例如住址),或者只有少部分记录有的属性(例如博客)放纵表。
l 频繁使用的小表可以考虑设置cache参数
设置了cache后,oracle会尽量让这个表的数据保持在内存,提高访问速度。我碰到过把操作员和菜单信息表加了cache参数,大幅提高登录速度的情况。
l 物化视图
普通视图只是用于简化复杂查询,对于效率提升不大。oracle提供了一种叫物化视图的特殊对象,可以把视图查询的结果集存起来,并且支持在基础数据变化时自动刷新。不过物化视图bug多,使用需要谨慎。
l 使用rac集群的数据库,最好分业务使用不同优先节点
由于oracle访问数据块时要求先把数据装载到内存,如果有某个数据块频繁被不同实例节点访问,会导致rac集群频繁地把数据从一个节点机器传输到另一个节点,这样会很消耗时间。所以建议不同业务优先访问不同rac节点,这样可以减少数据争抢的概率。
l 善用函数索引解决状态字段查询,少用位图索引
使用。位图索引容易造成数据块争用,建议在oltp系统少用。
l 悲观锁和乐观锁
悲观锁思想认为,数据被并发修改的几率比较大,需要在修改之前借助于数据库锁机制,先对数据进行加锁。乐观锁思想认为,数据一般是不会造成冲突的。所以在一般先将数据查出来但不加锁,在修改回数据库时检查数据有没有发生过变化,如果有则认为更新失败。业务场景允许失败重试的情况,建议多考虑使用悲观锁,减少锁资源对数据库的消耗。
l 一致读
oracle的数据块被修改之前会把数据块备份到undo表空间,这样可以保证sql查询过程中,数据被修改不会影响查询结果。而且还可以使用“闪回查询”的技术,指定查询库表某个时间点的数据。
l 使用with as改写复杂的关联查询
这样好处一是简化sql逻辑,二是有必要时还可以使用hint:materialize先把with as的内容实体化,减少重复查询。
l 索引要合理(基数过小的字段不适合建索引)
有些程序员在性别列上面都建了索引,以为查询时至少可以省一半时间,其实是错的。因为对于这种选择性不高的查询,先使用索引查询再回表查会导致很多随机读写,速度反而不如直接全表扫描快。
l 大量数据迁移时加快入库速度的方法:
commit nowait
append
alter table nologging
删除索引
使用交换分区
l 最好对数据库api进行封装,以便在日志里面输出使用的sql
系统做复杂后,新手想完全了解系统业务很困难。如果可以设置在日志里面输出访问数据库使用的sql,可以更方便我们进行系统运维。
更多数据库开发经验见:
《oracle数据库应用性能优化经验(培训讲义)》
《oracle proc编程性能优化经验》