《优化- 构架高性能的interbase_firebird系统》由会员分享,可在线阅读,更多相关《优化- 构架高性能的interbase_firebird系统(10页珍藏版)》请在金锄头文库上搜索。
1、构架高性能的构架高性能的 InterBase/FireBird 系统系统日期:2007-04-23 | 分类:数据库版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明 http:/ Generator支持下的自增长整数,作为记录编号(命名为 xxx_key),把“业务主键”定义为不可重复的索引(甚至于根本不用定义,如果没有特殊的检索要求的话)。在这种主键设计策略中,隐含着一个很深的道理,那就是“将业务逻辑和物理逻辑脱离开”的原则,实体的业务逻辑并不直接运用到真实的物理逻辑实现上。以单据表作为一个典型的例子,单据往往具备着一个不可重复的“单据编号”,一般它的数据类型是一个几十字节的
2、字符串,它相当于是“单据”这个实体在业务逻辑上体现出来的主键。而在这里的规范中,却并不以这个字符串编号为真正物理上的主键,而是采用一个自增长的 KEY 取代它成为真正的物理上的主键。以此为基础,表与表之间的关联也同样以一个单独整数字段进行关联,这个字段对于主表,一般就是主表的主键字段。在外键的创建中,一般也尽量避免多字段关联的联合外键。在 FireBird 数据库中,这种设计方式利用了 FireBird 特有的生成器(Generator)机制来实现。在创建外键时,一般都将外键的 cascade delete 打开,利用数据库的级联删除功能达到了自动删除从表记录。2、 索引的建立要为可能频繁发生
3、的检索的条件组合建立相应的索引。如果定义了主从表的外键关联,那么系统会自动创建所需的索引。但针对不同的数据类型,索引的执行效率也是不同的。对于复杂的查询,可以通过 plan 子句告诉服务器采用怎样的索引进行数据检索,格式如下:Select 语句PLAN(表名 1 INDEX(索引名称),表名 2 INDEX(索引名称)3、 自增长字段的建立建立表的时候,在 IBExpert 中通过打开字段的 AutoInc 属性设定某字段是自增长字段后,同时建立相应的生成器和触发器;通过调用 SQL 语句来得到服务器端生成器的值并使之加 1:Select gen_id(AGenerator,1) as id
4、from rdb$database 用一个 ibquery 控件来执行该语句产生永不重复整数序列。一般情况下,一个表对应着一个生成器。但是在某些情况下,可能多个表共用一个生成器,也可能一个表用多个生成器4、应尽量避免字符串字段作为关联查询的关联条件。5、应尽量避免多重字段、字符串字段(特别是常字符串)作为主键、关联查询的关联字段6、在系统构架方面应该采取划分业务数据生存期的方式,在系统中采用动态创建数据库的机制,使不同的业务区间对应一个单独的数据库文件,比如说,一个会计核算年对应一个 GDB文件,到年底核算时,为下年创建一个新的数据库,新数据库中的常量数据从老数据库中继承过来,而动态的业务数据
5、从 0 开始。划分动态数据库分区的原则就是,把每个数据库文件大约控制在 600M 左右(更大些也可以,但不太利于备份刻录到光盘上)看了你的经验收益良多.你在里边提到:在 SQL 语句不是很多的情况下,应避免在代码中书写动态 SQL 语句,特别是对于把数据显示界面相关联的 Select 语句更不推荐使用动态 SQL,而是要将不同的语句放在不同的Query 元件中,而且将查询控件的字段实体化=这样的话只要用良用到条件查询,就要切换 DataSource 相关的数据集组件,而且代码中也要用 DataSource.DataSet 的方式引用数据集来进行同步数据集操作.TIME: 2006-12-2 1
6、4:05:31 IP: Protected .NET 时代的 win32 英雄 广泛的使用自增长整数作为主键,是不是会在大批量数据的导入时遇到很大的麻烦?如果两表关联还是用的业务主键相关联 那么逻辑主键似乎没有存在的必要逻辑主键存在的必要就是提供检索=两表用逻辑主键相关联 这样的确很大程度的精简了两表关联的 SQL 处理可是会不会造成两表关联的不正确呢?说的非常好。在数据导入过程中,尤其是从逻辑主键的设计升级到物理主键的设计时,两表的关联确实需要一个将逻辑主键关联转化为物理主键关联的过程。处理这种做法,我有两个方法。其一就是客户端处理,方法是在客户端放一个内存表,预先载入被关联的基础数据表,其
7、中既包含物理主键,又包含逻辑主键(别的字段可以省略),然后进行关联表的导入,导入时,每插入一条记录之前,都通过内存表查一下逻辑主键对应的物理主键的值,把该值放入记录相应的关联字段中,再插入数据库。第二就是服务器端处理。是在服务器端的触发器中判断如果整数关联字段为空,那么自动通过原始的逻辑主键关联字段查出物理关联字段对应的值,赋值进去。意思差不多。如果是系统本身就采用了这种物理主键的思想进行设计的情况,那么一切都 OK,只不过在数据导入导出过程中需要注意两个问题:1、尊重已形成的自增长主键的值,也就是,已形成的自增长主键的值不能让它重新生成,是多少就是多少,原样导出,原样倒入,这样就不会错乱。I
8、BExpert 生成的触发器因为有判断语句,所以并不会干扰。2、生成器对象的值也要随着变。特别是,在空数据库倒入历史数据时,一定要把生成器的值也继承过来。注意了这两点,你就能对我提出的“业务主键和数据库物理主键分离”的思想运用自如,设计出的系统也有很好的性能。我们公司的会计软件就是这样做的。一个主数据库,一个模板数据库,然后是一个套帐一个目录,一年一个数据库。但是,要跨年度查询时,需要同时或逐一连接多个数据库,备份一个套帐的数据时也要将多个数据库备份并打包。跨套帐汇总时,更是要逐一连接多个数据库(多的时候有 70 个套帐)。一个数据库一年的数据大小为 510MB。=这就是我所提倡的“流水库”机
9、制。这种机制的要点在于,根据业务在时间上的运转周期,将业务划分为一个一个的业务周期,每一个业务周期创建一个新的数据库,以一个个单独的数据库作为数据在业务时间周期的天然分区隔断。数据库分区的划分原则就是使跨数据库查询的操作尽量少,同时兼顾单个数据库数据的大小不要超过 700M,最好是一张光盘能容纳的范围。每当系统到达业务时间分区的临界点时,系统将新建一个数据库,新数据库把常量数据从当前数据库中继承过来,当前数据库封顶,新数据库启动。业务分区最常见的就是按照财务年、或财务月分区,各有优点。这样进行系统设计带来了两个巨大的好处。其一就是增加了系统的健壮性。数据在不断的产生中不断的形成新数据库,这样单
10、个数据库不会无限增大,同时也不容易集聚瑕疵,不易崩溃;其二,就是提升了系统的性能。由于久远的历史数据都位于不同的历史业务库中,所以当前活跃的业务库内就没有大量的历史陈旧数据,系统在进行数据检索和查询的时候,直接面对的就是与当前业务直接相关的数据,所以,系统的运行永远象刚投入使用不久那么快。但如果必须需要进行跨库查询的话,那么通过一定的算法也能够解决这个问题。我本人曾经提出过“数据集合并法”专门解决跨 N 个数据库查询的问题。在采取了强力的优化之后,跨数据库查询的性能也不错。TIME: 2007-2-13 18:28:21 IP: Protected 可以请 yangdong 兄介绍一下“数据集
11、合并法”吗?=其实是用内存表的方法进行数据集记录的合并,原理并不复杂,就是通过循环,在每个数据库中进行某查询,把结果通过一定的规则添加到内存表中。比如说,1、最简单的合并就是数据集相加,就是用循环把每个数据库的查询结果都放到一个内存表中2、求和(sum)合并,就是每个查询结构插入内存表中时,先按照主键判断是否已存在记录,如存在,则把特定字段的值加到已有记录的相应字段中(求最大值、最小值道理相同)。3、平均值合并,需要绕道处理,对总量和总数量分别求和,然后作除法。第三种情况几乎没有多少实际应用。第一种情况是简单的追加。第二种情况往往是最典型的场景。方法的关键在于1、性能:怎样能使跨库查询的速度不
12、低于在同一个数据库中的查询速度?2、巧妙的封装,使程序员编写跨库查询语句的时候,几乎都不用管切换数据库的细节,不必写过多的冗余代码处理复杂的数据集合并的功能?对于性能而言,数据集合并的关键在于记录的定位算法要非常快。目前最常用的方法就是使目标数据有一个具有不重复的单个整数字段的主键,暂且称之为 rec_key(这也是我热衷于使用自增字段的原因之一),内存表在使用之前需要创建一个针对 rec_key 的索引。在第二种情况中,在插入记录的时候先对内存表进行 Locate,定位成功,则进行字段的相加修改,失败,则把记录追加到内存表中。一般情况,具有单整数字段的索引的性能已经相当的好了,这种情况下,巨
13、大的数据集的合并也有很不错的性能。(然而,有的时候我们如果追求极端的速度,则有可能编写针对数据集的 Helper 数据结构,来获得比 Locate 函数更加迅速的定位算法,甚至于根本不用内存表,直接用动态结构数组来盛放数据,用自定义的定位方法来定位数据,这就是后话了,一般根本用不着),如果你的记录集根本没有单整数的主键,只有很长的字符串的主键,或者是好几个字段的联合主键,那这个方法就有相当的效率折损。当然创建索引仍然有巨大的帮助。在封装性方面,一定要达到把跨库查询机制化,使各种各样的跨库查询都能够很方便的进行,而不是每个查询都得写一大堆代码去进行多数据库循环。那么就需要把跨库查询机制固化到公用
14、 DataModule 中,成为一种共有功能,把这个功能封装成为一个函数,包含有三部分参数:一个 ibQuery 控件(或者 SQL 语句),一个时间范围(该时间范围跨多个数据库),数据集的主键字段名称,该函数在执行中就首先根据 SQL 把内存表的字段都创建出来,然后创建索引,然后根据时间区域计算出所跨的数据库列表,然后在循环中连接每个数据库,执行 SQL 语句,把记录集合并到内存表中,最终,内存表中的数据就是成果物。这种方法下,新增一个跨库查询就用不着写一堆代码了。这里需要提及的就是 IBQuery 控件的 UniDirectional 属性应该设定为 True,使之成为单向数据集,性能会更
15、好。在这个机制中,可以利用 ibquery 的 Field 对象的 Tag 来约定告诉跨库查询函数该字段是需要求和呢,还是求最大值,或者是不做任何操作。TIME: 2007-2-13 18:29:16 IP: Protected 某位仁兄的疑问:对于采用流水库,我有一个疑问,那就是在建立索引的情况下,查找第一个记录和最后一个记录的时间是一样的,不管其数据量多么庞大,因为记录指针的表示范围是不变的,指针驱动磁盘走向到某个扇区去读取数据,只要不是跨设备读取,就不会什么效率问题出现。因此,采用流水库是不是一个多余的做法?说到系统效率慢的问题,我在具有上百万条记录的数据库中读取一个只有几十条记录的表,发现效率并没有受影响,而在读取具有庞大数据量的表时,如果检索不当,会出现网络崩溃的现象,而如果我只运行 select top 10 * from table 这样的语句时,并没有受到效率的影响,可想而知,数据库的运行效率并不是记录的增长或者是数据库容量的变大而出现效率低下。那么是什么原因导致大数据量而效率低下的问题呢?那应该是设计的缺陷。有以下几