楼主: yulihua49

[PRO*C] 看我做的数据库包装器

[复制链接]
论坛徽章:
14
2009新春纪念徽章
日期:2009-01-04 14:52:28沸羊羊
日期:2015-03-04 14:51:52优秀写手
日期:2014-03-14 06:00:13马上有房
日期:2014-02-18 16:42:022014年新春福章
日期:2014-02-18 16:42:022013年新春福章
日期:2013-02-25 14:51:24ITPUB 11周年纪念徽章
日期:2012-10-09 18:08:15蜘蛛蛋
日期:2012-06-27 21:08:142012新春纪念徽章
日期:2012-01-04 11:53:29ITPUB十周年纪念徽章
日期:2011-11-01 16:23:26
511#
 楼主| 发表于 2010-3-30 11:25 | 只看该作者

回复 #519 yulihua49 的帖子

DAU可以支持插入带RETURNING子句了:
http://blog.chinaunix.net/u3/92831/showart.php?id=2205055
   在设计表结构时,往往一个表没有合适的主键,常设计一个自增列,或default uuid列,插入数据时可以不插这个列。这个列往往用于外连接,可是插入后可能就找不到哪个是刚插入的了。此时必须使用带RETURNING子句的插入语句,在插入完成后立即得到键值。
    新的DAU/SDBC系统支持插入操作带RETURNING子句,定义一个或多个返回列。

[ 本帖最后由 yulihua49 于 2010-3-31 09:55 编辑 ]

使用道具 举报

回复
论坛徽章:
14
2009新春纪念徽章
日期:2009-01-04 14:52:28沸羊羊
日期:2015-03-04 14:51:52优秀写手
日期:2014-03-14 06:00:13马上有房
日期:2014-02-18 16:42:022014年新春福章
日期:2014-02-18 16:42:022013年新春福章
日期:2013-02-25 14:51:24ITPUB 11周年纪念徽章
日期:2012-10-09 18:08:15蜘蛛蛋
日期:2012-06-27 21:08:142012新春纪念徽章
日期:2012-01-04 11:53:29ITPUB十周年纪念徽章
日期:2011-11-01 16:23:26
512#
 楼主| 发表于 2010-4-2 13:12 | 只看该作者

回复 #520 yulihua49 的帖子

测试一下批量插入,在这个表存储过程单条插入100000条大约是3.6秒。
OCI批量插入,10条:1.2秒
100条:0.45秒。
1000条:0.27秒
单条:8.7秒。
批量还是有巨大优势,但不好管理。
下例在DAU环境下自己调sqlora完成:可以看到不用包装器,程序还是挺繁的,尤其是当列比较多时,而且还不通用。


  1. tuxticket@jgbticket:~/test> cat ti.c
  2. /*************************************

  3. create table T (
  4.         I1 number(6),
  5.         I2 number(6),
  6.         I3 number(6),
  7.         I4 number(6)
  8. );

  9. PK:
  10. create or replace procedure ti
  11. IS
  12. I   integer;
  13. BEGIN
  14.      FOR I IN 1..100000 LOOP
  15.       INSERT INTO T VALUES (I,I,I,I);
  16.      END LOOP;
  17. END;
  18. /
  19. 测试批量插入
  20. ***************************************/

  21. #include <DAU.h>

  22. #define BATCH 1000

  23. typedef struct {
  24.         int i1;
  25.         int i2;
  26.         int i3;
  27.         int i4;
  28.         short i1_ind;
  29.         short i2_ind;
  30.         short i3_ind;
  31.         short i4_ind;
  32. } ti_stu;

  33. main(int argc,char *argv[])
  34. {
  35. int i,j,num=0;
  36. int ret;
  37. T_SQL_Connect SQL_Connect;
  38. INT64 now;
  39. sqlo_stmt_handle_t cursor;
  40. char *result=0,stmt[1024];
  41. ti_stu ti[BATCH];

  42.         if(argc>1) ret=envcfg(argv[1]);

  43.         ret=db_open(&SQL_Connect);
  44.         if(ret) {
  45.                 printf("OPEN_Database %s,err=%d.%s\n",
  46.                         SQL_Connect.DBOWN,
  47.                         SQL_Connect.Errno,
  48.                         SQL_Connect.ErrMsg);
  49.                 return 1;
  50.         }

  51.         for(i=0;i<BATCH;i++)
  52.                 ti[i].i1_ind=ti[i].i2_ind=ti[i].i3_ind=ti[i].i4_ind=0;
  53.         sprintf(stmt,"INSERT INTO %s.T (I1,I2,I3,I4) VALUES(:1,:2,:3,:4)",SQL_Connect.DBOWN);
  54.         cursor=sqlo_prepare(SQL_Connect.dbh,stmt);
  55.         if(cursor < 0) {
  56.                 ___SQL_GetError(&SQL_Connect);
  57.                 printf("sqlo_prepare err=%d,%s\n",
  58.                         SQL_Connect.Errno,
  59.                         SQL_Connect.ErrMsg);
  60.                 ___SQL_CloseDatabase__(&SQL_Connect);
  61.                 return 2;
  62.         }
  63. printf("stmt=%s\n",stmt);
  64.         if((0>sqlo_bind_by_pos2(cursor,1,SQLOT_INT,&ti[0].i1,sizeof(int),&ti[0].i1_ind,0,sizeof(ti_stu))) ||
  65.            (0>sqlo_bind_by_pos2(cursor,2,SQLOT_INT,&ti[0].i2,sizeof(int),&ti[0].i2_ind,0,sizeof(ti_stu))) ||
  66.            (0>sqlo_bind_by_pos2(cursor,3,SQLOT_INT,&ti[0].i3,sizeof(int),&ti[0].i3_ind,0,sizeof(ti_stu))) ||
  67.            (0>sqlo_bind_by_pos2(cursor,4,SQLOT_INT,&ti[0].i4,sizeof(int),&ti[0].i4_ind,0,sizeof(ti_stu)))) {
  68.                 ___SQL_GetError(&SQL_Connect);
  69.                 printf("sqlo_bind err=%d,%s\n",
  70.                         SQL_Connect.Errno,
  71.                         SQL_Connect.ErrMsg);
  72.                 ___SQL_Close__(&SQL_Connect,cursor);
  73.                 ___SQL_CloseDatabase__(&SQL_Connect);
  74.                 return 3;
  75.         }

  76.         j=0;
  77.         now=now_usec();
  78.         for(i=0;i<100000;i++) {
  79.                 ti[j].i1=ti[j].i2=ti[j].i3=ti[j].i4=i;
  80.                 if(BATCH==++j) {
  81.                         ret=sqlo_execute(cursor,j);
  82.                                 j=0;
  83.                         if(ret) {
  84.                                 ___SQL_GetError(&SQL_Connect);
  85.                                 printf("sqlo_execute err=%d,%s\n",
  86.                                         SQL_Connect.Errno,
  87.                                         SQL_Connect.ErrMsg);
  88.                                 break;
  89.                         }
  90.                         num+=sqlo_prows(cursor);
  91.                 }
  92.         }
  93.         printf("insert %dRec's num=%d,TIMEVAL=%lldus\n",i,num,now_usec()-now);

  94.         strcpy(stmt,"TICKET.TI");
  95.         now=now_usec();
  96.         ret=ORA_Rpc(&SQL_Connect,stmt,&result);
  97.         printf("ORA_Rpc:return=%d,TIMEVAL=%lldus\n",ret,now_usec()-now);

  98.         ___SQL_Transaction__(&SQL_Connect,TRANROLLBACK);
  99.         ___SQL_CloseDatabase__(&SQL_Connect);
  100. }
  101. tuxticket@jgbticket:~/test> ./ti ld.ini
  102. stmt=INSERT INTO TICKET.T (I1,I2,I3,I4) VALUES(:1,:2,:3,:4)
  103. insert 100000Rec's num=100000,TIMEVAL=269855us
  104. ORA_Rpc:return=0,TIMEVAL=3628331us
复制代码

[ 本帖最后由 yulihua49 于 2010-4-2 15:12 编辑 ]

使用道具 举报

回复
论坛徽章:
14
2009新春纪念徽章
日期:2009-01-04 14:52:28沸羊羊
日期:2015-03-04 14:51:52优秀写手
日期:2014-03-14 06:00:13马上有房
日期:2014-02-18 16:42:022014年新春福章
日期:2014-02-18 16:42:022013年新春福章
日期:2013-02-25 14:51:24ITPUB 11周年纪念徽章
日期:2012-10-09 18:08:15蜘蛛蛋
日期:2012-06-27 21:08:142012新春纪念徽章
日期:2012-01-04 11:53:29ITPUB十周年纪念徽章
日期:2011-11-01 16:23:26
513#
 楼主| 发表于 2010-8-3 11:25 | 只看该作者
我最近做了一个程序,线程池服务器,驻留程序,为很多客户端服务。
其中把很多大家都要查询的内容都放到内存,减少了90%的语句执行。
臭显一下:要查找站名字典,调此函数结果在DP里。
int find_station(DAU *DP,T_Tree **root,char *stmt)
{
DG10_STATION_stu  stnnode;
T_Tree *tp;
int ret;
char *p;

        stnnode=*(DG10_STATION_stu *)DP->srm.rec;
        tp=BB_Tree_Find(*root,&stnnode,sizeof(stnnode),stn_cmp);
        if(tp) {
                *(DG10_STATION_stu *)DP->srm.rec=*(DG10_STATION_stu *)tp->Content;
                return 0;
        }
        if(DP->cursor<0) {
                p=DAU_mk_where(DP,"station_name",stmt);
                p=stpcpy(p," AND station_start_date <= :station_start_date"
                           " AND station_stop_date >= :station_start_date"
                           " AND a.city_code=b.city_code"
                           " AND city_start_date <= :station_start_date"
                           " AND city_stop_date >= :station_start_date");
        }
        if(0!=(ret=DAU_prepare(DP,stmt)))  return -1;
        else if(DAU_next(DP))  return -2;

        *root=BB_Tree_Add(*root,DP->srm.rec,sizeof(stnnode),stn_cmp,0);
        return 0;
}
数据在中间服务器缓存,极大的减少了数据库服务器的压力。

因为没有写表里的列名,所以表结构的改变与程序无关,别动我用到的几个列就行。存储过程没这个好处,表结构改变程序一定要修改的。

用C或其他编程语言处理业务逻辑,并不是简单的谁快谁慢的问题,能够利用到语言提供的许多资源与数据资源的深度整合,能够极大的方便编程和提高系统效率,这是存储过程难以达到的。

再声明一次,我不反对存储过程,而且DAU支持调用存储过程。

[ 本帖最后由 yulihua49 于 2010-8-3 11:40 编辑 ]

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
514#
发表于 2010-8-3 23:08 | 只看该作者
CACHE的思想不是自古有之吗?你这个有什么特别?
你如何判断缓存需要刷新?还是永远不刷新?
11G的RECULT_CACHE自动管理你的结果集,自动刷新。
在你的例子中,如果不同客户端传入不同的:station_start_date, 你保留几个缓存的拷贝?
谁说表结构改变就一定要修改存储过程?很多时候只是重新编译而已。
你的做法无非就是SELECT * (虽然你没有写SELECT *, 但是你的程序是所有列都读取,不管需不需要),这其实是很浪费的。

使用道具 举报

回复
论坛徽章:
821
授权会员
日期:2007-08-10 01:06:30山治
日期:2019-11-15 22:34:592015年新春福章
日期:2015-03-06 11:57:31暖羊羊
日期:2015-03-04 14:50:37马上有钱
日期:2014-12-21 16:14:33马上加薪
日期:2014-11-23 19:24:42 2014年世界杯参赛球队: 德国
日期:2014-07-09 15:28:06ITPUB元老
日期:2008-08-24 00:06:57会员2007贡献徽章
日期:2007-09-26 18:42:10托尼托尼·乔巴
日期:2020-03-23 10:49:16
515#
发表于 2010-8-4 12:12 | 只看该作者
原帖由 yulihua49 于 2010-4-2 13:12 发表
测试一下批量插入,在这个表存储过程单条插入100000条大约是3.6秒。
OCI批量插入,10条:1.2秒
100条:0.45秒。
1000条:0.27秒
单条:8.7秒。
批量还是有巨大优势,但不好管理。
下例在DAU环境下自己调sqlora完成:可以看到不用包装器,程序还是挺繁的,尤其是当列比较多时,而且还不通用。


tuxticket@jgbticket:~/test> cat ti.c
/*************************************

create table T (
        I1 number(6),
        I2 number(6),
        I3 number(6),
        I4 number(6)
);

PK:
create or replace procedure ti
IS
I   integer;
BEGIN
     FOR I IN 1..100000 LOOP
      INSERT INTO T VALUES (I,I,I,I);
     END LOOP;
END;
/
测试批量插入
***************************************/

#include

#define BATCH 1000

typedef struct {
        int i1;
        int i2;
        int i3;
        int i4;
        short i1_ind;
        short i2_ind;
        short i3_ind;
        short i4_ind;
} ti_stu;

main(int argc,char *argv[])
{
int i,j,num=0;
int ret;
T_SQL_Connect SQL_Connect;
INT64 now;
sqlo_stmt_handle_t cursor;
char *result=0,stmt[1024];
ti_stu ti;

        if(argc>1) ret=envcfg(argv[1]);

        ret=db_open(&SQL_Connect);
        if(ret) {
                printf("OPEN_Database %s,err=%d.%s\n",
                        SQL_Connect.DBOWN,
                        SQL_Connect.Errno,
                        SQL_Connect.ErrMsg);
                return 1;
        }

        for(i=0;isqlo_bind_by_pos2(cursor,1,SQLOT_INT,&ti[0].i1,sizeof(int),&ti[0].i1_ind,0,sizeof(ti_stu))) ||
           (0>sqlo_bind_by_pos2(cursor,2,SQLOT_INT,&ti[0].i2,sizeof(int),&ti[0].i2_ind,0,sizeof(ti_stu))) ||
           (0>sqlo_bind_by_pos2(cursor,3,SQLOT_INT,&ti[0].i3,sizeof(int),&ti[0].i3_ind,0,sizeof(ti_stu))) ||
           (0>sqlo_bind_by_pos2(cursor,4,SQLOT_INT,&ti[0].i4,sizeof(int),&ti[0].i4_ind,0,sizeof(ti_stu)))) {
                ___SQL_GetError(&SQL_Connect);
                printf("sqlo_bind err=%d,%s\n",
                        SQL_Connect.Errno,
                        SQL_Connect.ErrMsg);
                ___SQL_Close__(&SQL_Connect,cursor);
                ___SQL_CloseDatabase__(&SQL_Connect);
                return 3;
        }

        j=0;
        now=now_usec();
        for(i=0;i ./ti ld.ini
stmt=INSERT INTO TICKET.T (I1,I2,I3,I4) VALUES(:1,:2,:3,:4)
insert 100000Rec's num=100000,TIMEVAL=269855us
ORA_Rpc:return=0,TIMEVAL=3628331us


有没有试过批量绑定:

  1. SQL> DECLARE
  2. I   integer;
  3. BEGIN
  4.      FOR I IN 1..100000 LOOP
  5.       INSERT INTO T VALUES (I,I,I,I);
  6.      END LOOP;
  7. commit;
  8. END;
  9. /  2    3    4    5    6    7    8    9
  10. PL/SQL procedure successfully completed.
  11. Elapsed: 00:00:08.39
  12. SQL> DECLARE
  13.   2  TYPE NumList IS TABLE OF number;
  14.   3  listno  NumList;
  15.   4  BEGIN
  16.   5  select rownum BULK COLLECT INTO listno from dual connect by  rownum<100001;
  17.   6  FORALL i IN listno.first..listno.last
  18.   7  insert into T values(listno(i),listno(i),listno(i),listno(i));
  19.   8  commit;
  20.   9  END;
  21. 10  /
  22. PL/SQL procedure successfully completed.
  23. Elapsed: 00:00:00.43
  24. SQL>
复制代码

使用道具 举报

回复
论坛徽章:
14
2009新春纪念徽章
日期:2009-01-04 14:52:28沸羊羊
日期:2015-03-04 14:51:52优秀写手
日期:2014-03-14 06:00:13马上有房
日期:2014-02-18 16:42:022014年新春福章
日期:2014-02-18 16:42:022013年新春福章
日期:2013-02-25 14:51:24ITPUB 11周年纪念徽章
日期:2012-10-09 18:08:15蜘蛛蛋
日期:2012-06-27 21:08:142012新春纪念徽章
日期:2012-01-04 11:53:29ITPUB十周年纪念徽章
日期:2011-11-01 16:23:26
516#
 楼主| 发表于 2010-8-4 23:04 | 只看该作者
原帖由 newkid 于 2010-8-3 23:08 发表
CACHE的思想不是自古有之吗?你这个有什么特别?
你如何判断缓存需要刷新?还是永远不刷新?
11G的RECULT_CACHE自动管理你的结果集,自动刷新。
在你的例子中,如果不同客户端传入不同的:station_start_date, 你保留几个缓存的拷贝?
谁说表结构改变就一定要修改存储过程?很多时候只是重新编译而已。
你的做法无非就是SELECT * (虽然你没有写SELECT *, 但是你的程序是所有列都读取,不管需不需要),这其实是很浪费的。


新版DAU可以任意选择列或去除列:set_col(..,..,"列名,列名,。。。。");或except_col(..,..,"列名,列名,,,");
我认为第二种方法较好,数据比较独立,如果后来修改表,某些列没了,这语句也不错,不必修改。如果增加列,就自动被包含在选择集里。

其实你仔细看,我上边的例子实际上是两表连接,b表只用了4个列(结果集1个,条件3个),并非全部。(b表十几个列呢)。
功能不讨论了,存储过程能做的DAU都能做,反之亦然。
cache的有效性的确是问题。
现在在上海,有一个11G的DB平台和分离的中间件服务器。
只要调用服务器,速度就慢,只有减少数据库的调用,即使在数据库服务器不忙的情况。
只要给数据库一点压力,它的CPU立即上升到90%以上。所以只能尽量不调用数据库。

1000个用户压力,中间件CPU不到20%,数据库90%以上。都是8CPU的HP小型机。
26ms的调用在1000用户下延长到3秒。想提高响应能力,唯一的办法是减小数据库开销。尽量将开销转移到中间件。
这个数值就是26*1000/8,为什么不是/16呢?因为负载不均衡,中间件的8个CPU没用上。
为什么不把中间件的CPU也加到数据库?因为数据库是安腾64的,中间件是X86的,比较廉价。这就是说我所希望的廉价中间件分担昂贵的数据库,实现起来很不容易的。
数据库再怎么cache,也就是用内存和CPU取代IO。但CPU负担已经很重了。

[ 本帖最后由 yulihua49 于 2010-8-4 23:55 编辑 ]

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
517#
发表于 2010-8-4 23:55 | 只看该作者
“延长到3秒”的调用都在做什么?说不定你没优化好。

使用道具 举报

回复
论坛徽章:
14
2009新春纪念徽章
日期:2009-01-04 14:52:28沸羊羊
日期:2015-03-04 14:51:52优秀写手
日期:2014-03-14 06:00:13马上有房
日期:2014-02-18 16:42:022014年新春福章
日期:2014-02-18 16:42:022013年新春福章
日期:2013-02-25 14:51:24ITPUB 11周年纪念徽章
日期:2012-10-09 18:08:15蜘蛛蛋
日期:2012-06-27 21:08:142012新春纪念徽章
日期:2012-01-04 11:53:29ITPUB十周年纪念徽章
日期:2011-11-01 16:23:26
518#
 楼主| 发表于 2010-8-5 08:42 | 只看该作者
原帖由 newkid 于 2010-8-4 23:55 发表
“延长到3秒”的调用都在做什么?说不定你没优化好。

1个人时响应时间是26ms,比较稳定。
1000个人同时来,的平均响应时间是3秒左右,数据库服务器的8个CPU,平均90%,比较均衡。
中间件8个CPU,平均<20%,且不均衡。
1000客户端,32个进程,32数据库个连接,每个客户端连续10个调用。实际进行了10000次调用。


数据库CPU已经90%,响应时间已经符合理论计算,即:这系统的吞吐量已经达到极限。
若想增加系统能力,最好增加数据库的CPU数量或RAC。

[ 本帖最后由 yulihua49 于 2010-8-5 08:50 编辑 ]

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
519#
发表于 2010-8-5 08:53 | 只看该作者
你有没想过这些负担可能是你的编程方法引起的?如果用存储过程,你只需和数据库打一次交道;用你的方法,不得不频繁访问。
1000个连接是实际SESSION的数量?有没有用连接池?有时候你需要的物理连接并没那么多,几个客户可以共享,平均响应时间反而降低。
还是老规矩:把你的需求亮出来,我来写存储过程,你看看我的做法比你差多少!

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
520#
发表于 2010-8-5 09:04 | 只看该作者
刚刚看到你补充的内容,32个物理连接也太少了吧!你这10次调用,其实一次就够了。我问你绑定变量值修改之后,CACHE会不会复制新的拷贝而你没有答复,我猜想你的缓存都是静态的字典表之类的。这些缓存解决不了什么问题,DB的BLOCK缓存就够好了,何况我还可以在PGA里面用PLSQL的全局数组。

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表