大数据量下的数据库查询与插入如何优化? (整理)

时间:2022-06-10 11:34:51

数据库经常要做一些查询与插入,但是如果查询和插入的数据量过大的时候就会引发数据库性能问题,降低数据库工作效率。因此性能调优是大家在工作中都能够预见的问题,大到世界五百强的核心系统,小到超市的库存系统,几乎都会有要调优的时候。面对形形色色的系统,林林总总的需求,调优的手段也是丰富多彩。

1.尽量使语句符合查询优化器的规则避免全表扫描而使用索引查询

2.避免频繁创建和删除临时表,以减少系统表资源的消耗。

3.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

4.建立高效的索引

 

SQL语句的Select部分只写必要的列;尽量将In子查询重写为Exists子查询;

去除在谓词列上编写的任何数学运算;尽可能不用Distinct;

由于优化工具处理“或”逻辑可能有问题,所以尽量采用其他方式重写;

确保所处理的表中数据分布和其他统计信息正确,并反映当前状况;尽可能用UNION ALL取代UNION;

尽可能减少DB2的SQL请求;尽量将区间谓词重写为Between谓词;不要只是为了排序而选择某一列;

 

我目前所在的系统就是这么一个有实时插入又需要大数据的查询的一个系统。

采用了如下手段:

1,当天的记录会放在一个独立的表中.主要是针对实时的插入的记录,记录不要太多以免插入的时候维护索引的开销稳定在一个范围内。

2,历史的记录会按天分区的形式保存在历史表中。这个表一天只会批量的插入一次数据(用的是分区交换的方法)。

3,分区的索引对我的业务性能不好,因为要跨天 查询。历史查询最长时间段是一个月的时间,如果按照一个月一个分区的话,一个分区差不多是一个亿的记录,

就算是按月分区的话,再创建分区的本地索引,如果是时间段跨了月份的话估计分区的本地索引性能估计也不行。

4,后来采用一个方案,DB层上面再放了一个缓冲层,就是我最近在测试的Timesten关系型内存数据库,按照时间的老化策略缓冲一个月的数据。具体不展开说了。涉及的内容很多。

 

只是对于这个系统,我总结一下有以下需要注意的地方:

1,对于一个系统来说,如果查询性能反应不好的话,第一个调整的地方是思考业务的需求是否是合理的?

一个查询既要分页获取前面一页或者几页的数据,又要根据条件获取总的记录数,如果符合的记录总数是上亿条的话,感觉就是一个不合理的要求。

2,市场需求调研人员业务水平根本不合格。

3,前台开发人员写的SQL差,根本没有调优的基本概念,术业有专攻啊。

4,如果业务需求合理,SQL的调整无非是从执行计划开始,如果是ORACLE10g,开了cbo,可以用 SQL优化器 (SQL Tuning Advisor :STA)

分析你的sql。

5,最近的nosq和海量分布式的数据库概念很热。公司也在考虑HBASE了。

 

 

分区,

读写分离。

 

细节的优化手段大家都说了很多,我说些几个粗略的方面:

1.使用分区表

2.并行查询

3.定期的数据信息采集

4.可以考虑使用sql hint(生产库上我个人认为还是少指定 HINT,可以考虑用 SQL_PROFILE固定执行计划)

 

1 数据量上亿时我都会使用分区,具体的分区方案看业务需求,如果查询数据是按时间来的且时间跨度小,那么我会按天分

2 读写分离,说实话这个对我来说只是理想方案,实现起来往往有许多困难。磁盘阵列能上则上。

3 SQL语句的优化,实话说很多程序员写出来的SQL真的不忍直视。

4 内存数据库,这个我还没有尝试过,但用过的人说还好。

 

对于大数据量的处理,我通常采用如下方法:

一、对于大数据存储的处理(以下是假设硬件指标合格的情况)

(1)对大表进行分区,根据不同的业务以及数据特征,采用不同的分区方法。比如,销售,可以考虑采用间隔分区技术,多分公司或是多部门,可以考虑采用列表分区或是上述的组合分区等。

(2)如果硬件具备,对于大表进行分离存储,从而减少磁盘争用。

(3)对历史数据进行定期的归档处理。比如,销售的区间分区,可以采用表与分区交换的技术去处理。对于含有复杂的业务的数据表的归档,采用PL/SQL脚本与后台作业定时完成。

二、对于查询的处理(以下是基于成本优化器的情况)

对于查询,优化的方法通常会多一些,但是也是最频繁调整的一块内容。通常,我从如下这几个方面来进行考虑并调优.

(1)对于SQL本身的编写是否合理。比如基础写法与高级写法之间的配合,在满足业务要求的基础上,做到尽量减少表的访问。

(2)索引的创建是否合理,优化器是否选择了较正确的索引。在不同的业务场景下,B*树索引与位图索引的相互配合是否合理等。

(3)监控系统中产生的争用,根据产生的不同的闩或锁,对SQL或是业务处理逻辑进行调整。

(4)如果有必要,根据当前系统的负载与硬件本身的支持,对PGA,SGA进行一个分配,使之更为合理。

(5)如果有必要,在优化器参数进行一个调整。

(6)采用并行处理。

三、对数据插入的处理

这是考虑问题当中最薄弱的一块内容,一般不作必要的优化处理,但有如下一些技巧:

(1)对于大数据量的表与表之间的插入,可以采用并行,直接路径、最小化日志。

(2)如果必要,对表本身参数进行一个修改,如freelist。

(3)如果是同数据源多目标的插入,可以采用多表插入技术。

(4)如果可以,尽量使用INSERT INTO SELECT,CREATE TABLE AS SELECT方法。

 

1.需求阶段的优化改进

   摸清用户的真实需求。发掘隐藏的用户需求。

一般这个很难被真实的发掘出来,因为用户的需求在被调研的时候,往往由于调研者对业务的不深入理解,导致只发掘了用户的表面需求,而没有摸清用户的真实需求。导致后期,开发的时候,每次查询都要查询很多张大表等。

 

2.在数据库设计阶段的改进

     规范数据库的表的设计。

     大表尽量要有明确的数据保留策略。

    尽量不要在大表上建立影响性能的触发器

     大表合理的规划分区。

    合理的建立索引,这个很重要。也是优化之核心。

    数据库参数的合理设置。

    表空间的合理规律。

    通过在设计阶段,把

尽量用少做事,或者是不做事情,就可以达到用户的需求。

 

如果在前期需求调研,设计阶段就做好了工作。那么后面的性能优化问题麻烦就可以少很多。

当然通常由于各种原因,往往前期做不到那么好。

那么找到问题,发现问题,解决问题,通常

1. 通过工具来定位问题,例如选择用10046 TRACE 工具包来实现

2. 找到问题所在以后去理解需求,探索是否能少做事完成需求(选择用索引来替代全表扫描,从而减少访问路径);

3. 去思考需求背后的真正需求

   例如是否可以用UNION ALL取代UNION ,避免不必要的排序引起资源消耗。

4. 去分析资源如何合理应用 (在系统空闲时使用并行技术)。

5.诊断索引方面的问题。

   例如是否由于错误的优化器统计信息导致执行了不正确的执行计划。

 

一种是分库分表

    还有一种就是提前运算好存到数据库中,直接取出。

    根据情况实际设计是最合适的~

 

1 大量数据插入的时候,考虑先删除索引,然后重建索引。这样做的缺点是业务不能同时进行。

2 大量数据的查询优化,根据业务,考虑创建不同的索引。

 

数据库表进行插入、查询操作当数据达到百万甚至千万条级别的时候, 这一切似乎变得相当困难。几经折腾,总算完成了任务。在此做些简单的小结,不足之处,还望高手们帮忙补充补充!

 

1、 避免使用Hibernate框架

Hibernate用起来虽然方便,但对于海量数据的操作显得力不从心。

关于插入:

试过用Hibernate一次性进行5万条左右数据的插入,若ID使用sequence方式生成,Hibernate将分5万次从数据库取得 5万个sequence,构造成相应对象后,再分五万次将数据保存到数据库。花了我十分钟时间。主要的时间不是花在插入上,而是花在5万次从数据库取 sequence上,弄得我相当郁闷。虽然后来把ID生成方式改成increase解决了问题,但还是对那十分钟的等待心有余悸。

关于查询:

Hibernate对数据库查询的主要思想还是面向对象的,这将使许多我们不需要查询的数据占用了大量的系统资源(包括数据库资源和本地资 源)。由于对Hibernate的偏爱,本着不抛弃、不放弃的作风,做了包括配SQL,改进SQL等等的相当多的尝试,可都以失败告终,不得不忍痛割爱 了。

 

2、 写查询语句时,要把查询的字段一一列出

查询时不要使用类似select * from x_table的语句,要尽量使用select id,name from x_table,以避免查询出不需要的数据浪费资源。对于海量数据而言,一个字段所占用的资源和查询时间是相当可观的。

 

3、 减少不必要的查询条件

当我们在做查询时,常常是前台提交一个查询表单到后台,后台解析这个表 单,而后进行查询操作。在我们解析表单时,为了方便起见,常常喜欢将一些不需要查询的条件用永真的条件来代替(如:select count(id) from x_table where name like ‘%’),其实这样的SQL对资源的浪费是相当可怕的。我试过对于同样的近一千万条记录的查询来说,使用select count(id) from x_table 进行表查询需要11秒,而使用select count(id) from x_table where name like ‘%’却花了33秒。

 

4、 避免在查询时使用表连接

在做海量数据查询时,应尽量避免表连接(特别是左、右连接),万不得已要进行表连接时,被连接的另一张表数据量一定不能太大,若连接的另一张表也是数万条的话,那估计可以考虑重新设计库表了,因为那需要等待的时间决不是正常用户所能忍受的。

 

5、 嵌套查询时,尽可能地在第一次select就把查询范围缩到最小

在有多个select嵌套查询的时候,应尽量在最内层就把所要查询的范围缩到最小,能分页的先分页。很多时候,就是这样简单地把分页放到内层查询里,对查询效率来说能形成质的变化。

 

特别是银行系统的,数量级是亿级别的,所以更要考虑下面的方法。

1,怎样造Java对象。有句话说得好:尽可能的少造对象。别说千万级,就是上万级都不要考虑造对象了。因为几个请求一并发,喀嚓,系统肯定完蛋。

2,合理摆正系统设计的位置。大量数据操作,和少量数据操作一定是分开的。大量的数据操作,肯定不是ORM框架搞定的。绝对不能ORM,因为1,要少造对象;2,数据库资源合理利用。就像博主的例子:id分配就是一个好例子。

3,合理利用数据库的分区、索引技术。

4,有的时候可以考虑临时表之类的,尤其是大数据量。

5,有人说非常大的数据量,一定要用存储过程:jdbc,效果非常好

6,控制好内存,让数据流起来,而不是全部读到内存再处理,而是边读取边处理;

7,合理利用内存,有的数据要缓存;

 

30个oracle的查询插入的方法:

 

 1对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。

  2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:

 

  select id from t where num is null

 

  可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:

 

  select id from t where num=0

 

  3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。

 

  4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:

 

  select id from t where num=10 or num=20

 

  可以这样查询:

 

  select id from t where num=10

 

  union all

 

  select id from t where num=20

 

  5.in 和 not in 也要慎用,否则会导致全表扫描,如:

 

  select id from t where num in(1,2,3)

 

  对于连续的数值,能用 between 就不要用 in 了:

 

  select id from t where num between 1 and 3

 

  6.下面的查询也将导致全表扫描:

 

  select id from t where name like '?c%'

 

  若要提高效率,可以考虑全文检索。

 

  7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:

 

  select id可以改为强制查询使用索引:

 

  select id from t with(index(索引名)) where num=@num

 

  8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:

 

  select id from t where num/2=100

 

  应改为:

 

  select id from t where num=100*2

 

  9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:

 

  select id from t where substring(name,1,3)='abc'--name以abc开头的id

 

  select id from t where datediff(day,createdate,'2005-11-30')=0--‘2005-11-30’生成的id

 

  应改为:

 

  select id from t where name like 'abc%'

 

  select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'

 

  10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

 

  11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。

 

12.不要写一些没有意义的查询,如需要生成一个空表结构:

 

  select col1,col2 into #t from t where 1=0

 

  这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:

 

  create table #t(...)

 

  13.很多时候用 exists 代替 in 是一个好的选择:

 

  select num from a where num in(selectnum from b)

 

  用下面的语句替换:

 

  select num from a where exists(select 1 from b where num=a.num)

 

  14.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。

 

  15.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。

 

  16.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。

 

  17.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

 

  18.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

 

  19.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。

 

  20.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。

 

  21.避免频繁创建和删除临时表,以减少系统表资源的消耗。

 

  22.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。

 

  23.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

 

  24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

 

  25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

 

  26.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。

 

  27.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。

 

  28.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。

 

  29.尽量避免大事务操作,提高系统并发能力。

 

  30.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

 

"25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。"

这是常见的说法,不知是什么场景。

我,恰恰是使用游标,进行千万级的批量插入,效率非常高。

因为,ORACLE OCI,进行批量插入,只能通过游标进行批量绑定,这也是sqlldr的做法。

游标一次打开多次使用,效率会非常高,性能超过你的想像。

通常,我们1000行一批,每批只需对游标bind,execute,再加上多线程并行,再加上RAC,速度无敌。

而且这一切都在一个框架的支撑下进行----C的hibernate。。。

离了这个框架,这事还真不好做。

 

在C的世界里,游标可以用,hibernate也可以用。

 

数据,其源头都是外来的。多数部门,不允许数据库间的直接访问。必须卸载-传输-加载。

 

我目前所在的系统就是这么一个有实时插入又需要大数据的查询的一个系统。

 

我们公司曾经想过用hbase 开发过这样的系统, 高速写入同时,高速查询,查询的数据是有时间范围,不超过当前点的 5分钟到10分钟。 我们考虑使用hbase , 主要hbase里有专门内存区域来存放数据,满了32M/或者根据设定值就会写入到磁盘里面。 如果读磁盘里面的数据就会很慢,如果读内存的数据就会很快。根据业务,增加足够的节点机,保证 5-10分种的数据缓存在 hbase的内存中,这样就可以保证高速写入的同时也可以高速查询,这样的结构实际上取巧,使用了hbase作为一个小型内存数据库。

我们实现了高速写入。 但是由于其他原因没有做下去,很可惜。

 

 

一般大数据的维护多出现在 OLAP 环境中

 

对于大数据的维护

 

1、首选 表分区 + 压缩 采用“交换分区”操作,瞬间完成数据的转入转出,数据压缩能减少I/0, 提高查询性能

2、在DB2中,可以采用MDC,由于维度键值对相同的记录都被放在相邻的块中,可以很快的进行删除

3、对于业务处理复杂的操作,可 采用“临时表”来存储中间处理过程,分段处理,大表变小表

 

大数据量的查询:

 

1、合理设计索引,尽量形成索引覆盖

2、采用簇表

3、使用ETL工具,在DBMS负载空闲时,将需要查询最终数据(汇总、统计)的结果计算后存入汇总、统计表,减少对大表的多次扫描!

 

并行多次查询,并且读写分离。