中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 编程语言 > C/C++
钢筋混凝土的UNIX C编程技巧(一、内存映射表)[原创]
作者:未知 时间:2005-09-13 19:30 出处:ChinaUnix.net 责编:chinaitpower
              摘要:钢筋混凝土的UNIX C编程技巧(一、内存映射表)[原创]

[code:1:827619af4e]
钢筋混凝土的UNIX C编程技巧(一、内存映射表)
作者:钢筋混凝土
版本:2004-11-3 2:43 第一稿

关键词:

unix、数据库、E-SQL嵌入式开发、共享内存、回调函数

前言:

大学毕业后从事unix上的银行综合业务系统开发工作已有一年半的时间,向众多前辈高手学习了很多经验和技巧,自己也创新了些好的开发技术,特写出来与奋斗在一线的unix程序员们共享。本人大学时专注于windows平台应用开发,工作后才转入unix平台,故沿袭了不少windows编码风格。

--------------------------------------------------------------------------------

正文:

在一个带有数据库的unix系统中进行E-SQL嵌入式开发,必然用到很多混合式编程方式。当系统对表的SELECT操作频繁时,会使数据库效率大幅下降。于是我们很当然的这样设计:当应用开始运行时把数据库中需要频繁查询的表装载入共享内存,通过编写一批共享内存查询函数实现对表数据的快速查询、定位。这里借用windows的一些名词把这一技术命名为“内存映射表”技术。
内存映射表的格式设计有很多方式,下面介绍一下我设计的一种格式,该格式已经应用于某省级银行信用卡全省大前置系统,取得非常好的效果。

|                    |                    |                    |     |
| 内存映射表记录条数 | 第一条记录结构单元 | 第二条记录结构单元 | ... |
| 10个字节           | 记录结构的大小     | 记录结构的大小     |     |
|                    |                    |                    |     |

共享内存数据存放格式如上图所示。开头的10个字节存放内存映射表的记录条数数值,由于标准c的有符号长整数类型最大值约为21亿,所以预留10个字节存放ASCII编码的记录条数数值已绰绰有余且取得最大限度值了。第11个字节开始存放数据库表第一条记录对应的c语言结构体,称为一个结构单元。后面依次存放所有数据库表记录形成结构体数组。
一张数据库表装载入一块共享内存,可以通过表名给共享内存的ipckey取名。比如“公共系统参数表”对应的内存映射表的ipckey在头文件里这样添加"#define SHMY_KEY_GGXTCS 0x00001138 /* 4408 */",以便于在程序里引用。
内存映射表共占用共享内存大小为该表记录对应的数据结构体大小乘以记录条数加上10个字节。比如“公共系统参数表”记录条数为10条,表定义如下。那么总占用共享内存大小=(20+30+40)*10+10=910个字节。

字段名 字段属性 长度 空值标志 备注 包括中文注释和取值范围
csxh   char       20      N.N           参数序号
csz    char       30      N.N           参数值
cssm   char       40                    参数说明
索引1 unique csxh

内存映射表的操作大致有装载和查询两种操作,其它还可以有简单的更新操作。考虑到每个内存映射表的操作大致一样以及以某个关键字段查询、更新操作的相似性,再以“公共系统参数表”我这样设计内存映射表的操作函数原形:

int LoadMapGGXTCS();
int FetchMapGGXTCS ( void *pvCondValue ,
struct REPLACE_STRUCT_TYPE *pREPLACE_STRUCT_ARG ,
int (* REPLACE_FUNCNAME_COMPARE_PROC)
( void *pvCondValue ,
struct REPLACE_STRUCT_TYPE *pREPLACE_STRUCT_ARG
)
);
int UpdateMapGGXTCS ( void *pvCondValue ,
void *pvUpdateValue ,
int (* REPLACE_FUNCNAME_UPDATE_PROC)
( void *pvCondValue ,
void *pvUpdateValue ,
struct REPLACE_STRUCT_TYPE *pREPLACE_STRUCT_ARG
)
);

两个函数内所有涉及到具体表名、结构体名、回调函数名我都已宏的方式替换掉,这样做的好处是可以形成代码模板,如果以后要添加一张表的映射只要复制代码模板到实现文件的最后面,把代码模板最前面的宏定义成具体的值。代码模板最后面把所有用过的宏都反定义掉,不妨碍后面的程序使用。
装载表函数我不用多说了,即把表数据装载入共享内存,不需要参数。
查询函数第一个参数为关键字段值,与REPLACE_FUNCNAME_COMPARE_PROC回调函数配合使用。参数类型为void*类型,这样就可以兼容所有类型的数据甚至是结构体、共用体,额外麻烦的只是把变量传入前强制传换成void*,在回调函数里再转换回具体的变量类型。第二个参数是结构体宏,用于函数成功返回时把符合要求的记录结构体返回。第三个参数是指向回调函数的指针,其作用是针对某一关键字段,分别取出共享内存里的每条记录进行比较,当条件符合时,回调函数返回0,否则返回1,这样可以不改变外层遍历函数的条件下,使用不同判断方式、不同的判断值对内存映射表中所有记录进行遍历。
本文最后附件中附有装载、查询和更新三个内存映射表的代码模板,由于完全采用参数化宏替换方式设计,甚至可以不加修改的立即应用到您的系统中去。
下面跳跃的介绍一下查询函数极其回调函数的操作原理。更新函数原理与之相似,结构稍稍复杂一些,重点是回调函数。

查询函数
{
...
遍历内存映射表中所有结构记录
{
调用遍历函数最后一个参数即回调函数指针(条件值,当前结构记录);
如果回调函数返回0,即条件符合且更新成功
{
复制内存映射表中当前结构记录内容到pREPLACE_STRUCT_ARG指针空间里,输出给用户
跳出遍历;
}
偏移到内存映射表中下一条结构记录;
}
...
返回回调函数的返回值
}

查询回调函数(条件值,内存映射表中当前结构记录)
{
把便利函数传入的条件值强制转换成char*类型;
与内存映射表中当前结构记录的csxh进行比较,如果相等即找到
返回0;
否则
返回1;
}

查询函数的使用示例

...
/* 获取 对帐场次 */
iRt = FetchMapGGXTCS( (void *)"dzcc" , &stGgxtcs , &CompareKeyFromGGXTCSWhereCSXHProc );
if( iRt != 0 )
printf( "获取对帐场次 失败" );
else
printf( "当前对帐场次为[%s]" , stGgxtcs.csz );
...

使用该设计通过创建与数据库表映射的共享内存数据提高对数据库静态表(运行时数据内容不改变或者只做少量更新的表)的查询访问速度,而不需要额外占用数据库宝贵的效率,尤其在一个对数据库操作频繁的系统中能很大程度的提高整个系统的运行效率。本设计也有不足之处,主要是只能代替数据库简单的SELECT操作和UPDATE操作,不支持INSERT、DELETE操作,不过对于一些静态表的查询使用已经足够了,不是吗 ^___^

--------------------------------------------------------------------------------

附件一、内存映射表 代码模块(以“公共系统参数表”为例)

/********************************************
** 获取 公共系统参数表 内存映射表 相应记录 **
********************************************/

#define REPLACE_STRUCT_TYPE ggxtcs
#define REPLACE_STRUCT_ARG stGgxtcs
#define REPLACE_SHEKEY SHMY_KEY_GGXTCS
#define REPLACE_TABLENAME ggxtcs
#define REPLACE_TABLEDESC "公共系统参数表"
#define REPLACE_CURSORNAME curGGXTCS
#define REPLACE_DBVAR R_GGXTCS
#define REPLACE_DBVARFUNC pubVtoSGgxtcs
#define REPLACE_FUNCNAME_FETCH "FetchMapGGXTCS"
#define REPLACE_FUNCNAME_LOAD "LoadMapGGXTCS"
#define REPLACE_FUNCNAME_COMPARE_PROC CompareKeyFromGGXTCSProc
#define REPLACE_FUNCNAME_UPDATE_PROC UpdateValueFromGGXTCSProc

int FetchMapGGXTCS ( void *pvCondValue ,
struct REPLACE_STRUCT_TYPE *pREPLACE_STRUCT_ARG ,
int (* REPLACE_FUNCNAME_COMPARE_PROC)
( void *pvCondValue ,
struct REPLACE_STRUCT_TYPE *pREPLACE_STRUCT_ARG
)
);
{
int iReturnValue;

struct REPLACE_STRUCT_TYPE *pmpREPLACE_STRUCT_ARG;
char *pmp;
char acRecordAmount[11];
long lRecordAmount;
long l;

_IPC_ID_T ipcid;

/* 判断 内存映射表 是否存在 */
iReturnValue = IPCIsShareMemoryExist( REPLACE_SHEKEY );

if( iReturnValue == IPC_SHAREMEMORY_RETURN_ISNT_EXIST )
{
/* 若不存在 则创建之 */
iReturnValue = LoadMapGGXTCS();

if( iReturnValue != 0 )
{
WriteLog( gacLogFilename,
"%s | "REPLACE_FUNCNAME_FETCH" | "LOG_LINELEN" | 创建 内存映射表 "REPLACE_TABLEDESC" 失败 错误码[%d] errno[%d],请重启应用\n",
GetLocalTimeString( gacTimeStringBuffer , 256 , "%Y-%m-%d %H:%M:%S" ),
__LINE__,
iReturnValue,
errno );

return -1;
}
}

/* 打开 内存映射表 */
ipcid = IPCOpenShareMemory( REPLACE_SHEKEY );

if( ipcid < 0 )
{
/* 打开失败,写出错日志,函数返回 */
WriteLog( gacLogFilename,
"%s | "REPLACE_FUNCNAME_FETCH" | "LOG_LINELEN" | 打开 内存映射表 "REPLACE_TABLEDESC" 失败 errno[%d],请重启应用\n",
GetLocalTimeString( gacTimeStringBuffer , 256 , "%Y-%m-%d %H:%M:%S" ),
__LINE__,
errno );

return -2;
}

/* 连接 内存映射表 地址 */
pmp = IPCAttachShareMemory( ipcid );

if( pmp == NULL )
{
/* 连接失败,写出错日志,函数返回 */
WriteLog( gacLogFilename,
"%s | "REPLACE_FUNCNAME_FETCH" | "LOG_LINELEN" | 连接 内存映射表 "REPLACE_TABLEDESC" 失败 errno[%d],请重启应用\n",
GetLocalTimeString( gacTimeStringBuffer , 256 , "%Y-%m-%d %H:%M:%S" ),
__LINE__,
errno );

return -3;
}

memset( acRecordAmount , 0x00 , sizeof( acRecordAmount ) );
strncpy( acRecordAmount , pmp , 10 );
lRecordAmount = atol( acRecordAmount );

pmpREPLACE_STRUCT_ARG = (struct REPLACE_STRUCT_TYPE *)( pmp + 10 ) ;

/* 搜寻 内存映射表 */
for( l=0 ; l<lRecordAmount ; l++ )
{
/* 调用搜寻回调函数 */
iReturnValue = REPLACE_FUNCNAME_COMPARE_PROC( pvCondValue , pmpREPLACE_STRUCT_ARG ) ;

if( iReturnValue != 1 )
{
memset( pREPLACE_STRUCT_ARG , 0x00 , sizeof( struct REPLACE_STRUCT_TYPE ) );
memcpy( pREPLACE_STRUCT_ARG , pmpREPLACE_STRUCT_ARG , sizeof(struct REPLACE_STRUCT_TYPE) );

break;
}

pmpREPLACE_STRUCT_ARG ++ ;
}

/* 断开 内存映射表 地址连接 */
IPCDetachShareMemory( pmp );

return iReturnValue;
}

/***********************************
** 更新 公共系统参数表 内存映射表 **
***********************************/

int UpdateMapGGXTCS ( void *pvCondValue ,
void *pvUpdateValue ,
int (* REPLACE_FUNCNAME_UPDATE_PROC)
( void *pvCondValue ,
void *pvUpdateValue ,
struct REPLACE_STRUCT_TYPE *pREPLACE_STRUCT_ARG
)
);
{
int iReturnValue;

struct REPLACE_STRUCT_TYPE *pmpREPLACE_STRUCT_ARG;
char *pmp;
char acRecordAmount[11];
long lRecordAmount;
long l;

_IPC_ID_T ipcid;

/* 判断 内存映射表 是否存在 */
iReturnValue = IPCIsShareMemoryExist( REPLACE_SHEKEY );

if( iReturnValue == IPC_SHAREMEMORY_RETURN_ISNT_EXIST )
{
/* 若不存在 则创建之 */
iReturnValue = LoadMapGGXTCS();

if( iReturnValue != 0 )
{
WriteLog( gacLogFilename,
"%s | "REPLACE_FUNCNAME_FETCH" | "LOG_LINELEN" | 创建 内存映射表 "REPLACE_TABLEDESC" 失败 错误码[%d] errno[%d],请重启应用\n",
GetLocalTimeString( gacTimeStringBuffer , 256 , "%Y-%m-%d %H:%M:%S" ),
__LINE__,
iReturnValue,
errno );

return -1;
}
}

/* 打开 内存映射表 */
ipcid = IPCOpenShareMemory( REPLACE_SHEKEY );

if( ipcid < 0 )
{
/* 打开失败,写出错日志,函数返回 */
WriteLog( gacLogFilename,
"%s | "REPLACE_FUNCNAME_FETCH" | "LOG_LINELEN" | 打开 内存映射表 "REPLACE_TABLEDESC" 失败 errno[%d],请重启应用\n",
GetLocalTimeString( gacTimeStringBuffer , 256 , "%Y-%m-%d %H:%M:%S" ),
__LINE__,
errno );

return -2;
}

/* 连接 内存映射表 地址 */
pmp = IPCAttachShareMemory( ipcid );

if( pmp == NULL )
{
/* 连接失败,写出错日志,函数返回 */
WriteLog( gacLogFilename,
"%s | "REPLACE_FUNCNAME_FETCH" | "LOG_LINELEN" | 连接 内存映射表 "REPLACE_TABLEDESC" 失败 errno[%d],请重启应用\n",
GetLocalTimeString( gacTimeStringBuffer , 256 , "%Y-%m-%d %H:%M:%S" ),
__LINE__,
errno );

return -3;
}

memset( acRecordAmount , 0x00 , sizeof( acRecordAmount ) );
strncpy( acRecordAmount , pmp , 10 );
lRecordAmount = atol( acRecordAmount );

pmpREPLACE_STRUCT_ARG = (struct REPLACE_STRUCT_TYPE *)( pmp + 10 ) ;

/* 搜寻 内存映射表 */
for( l=0 ; l<lRecordAmount ; l++ )
{
/* 调用搜寻回调函数 */
iReturnValue = REPLACE_FUNCNAME_UPDATE_PROC( pvCondValue , pvUpdateValue , pmpREPLACE_STRUCT_ARG ) ;

if( iReturnValue != 1 )
{
break;
}

pmpREPLACE_STRUCT_ARG ++ ;
}

/* 断开 内存映射表 地址连接 */
IPCDetachShareMemory( pmp );

return iReturnValue;
}

/***********************************
** 装载 公共系统参数表 内存映射表 **
***********************************/

int LoadMapGGXTCS()
{
int iReturnValue;

struct  REPLACE_STRUCT_TYPE REPLACE_STRUCT_ARG;
struct  REPLACE_STRUCT_TYPE *pmpREPLACE_STRUCT_ARG;
char *pmp;

_IPC_ID_T ipcid;

long lMapSize;

memset( &REPLACE_STRUCT_ARG , 0x00 , sizeof( struct REPLACE_STRUCT_TYPE ) );

/* 获取表记录总条数 */
EXEC SQL
SELECT count(*)
INTO :dlRecordAmount
FROM REPLACE_TABLENAME ;

if( sqlca.sqlcode )
{
WriteLog( gacLogFilename,
"%s | "REPLACE_FUNCNAME_LOAD" | "LOG_LINELEN" | 获取 "REPLACE_TABLEDESC" 总记录数 失败 sqlcode[%d]\n",
GetLocalTimeString( gacTimeStringBuffer , 256 , "%Y-%m-%d %H:%M:%S" ),
__LINE__,
sqlca.sqlcode );

return -1;
}

/* 计算内存映射表大小 */
/* 前十个字节为储存头,存放储存单元的个数 */
lMapSize = dlRecordAmount * sizeof( struct  REPLACE_STRUCT_TYPE ) + 10 ;

/* 创建 内存映射表 */
ipcid = IPCCreateShareMemory( REPLACE_SHEKEY , lMapSize );

if( ipcid < 0 )
{
if( errno == EEXIST )
return 0;

WriteLog( gacLogFilename,
"%s | "REPLACE_FUNCNAME_LOAD" | "LOG_LINELEN" | 创建 内存映射表 "REPLACE_TABLEDESC" 失败 errno[%d],请重启应用\n",
GetLocalTimeString( gacTimeStringBuffer , 256 , "%Y-%m-%d %H:%M:%S" ),
__LINE__,
errno );

return -2;
}

/* 定义游标 */
EXEC SQL
DECLARE REPLACE_CURSORNAME CURSOR FOR
SELECT *
FROM REPLACE_TABLENAME ;

/* 打开游标 */
EXEC SQL
OPEN REPLACE_CURSORNAME ;

/* 打开游标 失败 */
if( sqlca.sqlcode )
{
WriteLog( gacLogFilename,
"%s | "REPLACE_FUNCNAME_LOAD" | "LOG_LINELEN" | 打开 游标 "REPLACE_TABLEDESC" 失败 sqlcode[%d]\n",
GetLocalTimeString( gacTimeStringBuffer , 256 , "%Y-%m-%d %H:%M:%S" ),
__LINE__,
sqlca.sqlcode );

return -3;
}

/* 连接 内存映射表 地址 */
pmp = IPCAttachShareMemory( ipcid );

if( pmp == NULL )
{
/* 连接失败,写出错日志,函数返回 */
WriteLog( gacLogFilename,
"%s | "REPLACE_FUNCNAME_LOAD" | "LOG_LINELEN" | 连接 内存映射表 "REPLACE_TABLEDESC" 失败 errno[%d],请重启应用\n",
GetLocalTimeString( gacTimeStringBuffer , 256 , "%Y-%m-%d %H:%M:%S" ),
__LINE__,
errno );

return -4;
}

sprintf( pmp , "%-010d" , dlRecordAmount );
pmpREPLACE_STRUCT_ARG = (struct REPLACE_STRUCT_TYPE *)( pmp + 10 ) ;

while(1)
{
/* 获取游标 */
EXEC SQL
FETCH REPLACE_CURSORNAME
INTO REPLACE_DBVAR;

/* 如果记录已取完,跳出循环 */
if( sqlca.sqlcode == 100 )
break;

/* 获取游标 失败 */
if( sqlca.sqlcode != 0 )
{
WriteLog( gacLogFilename,
"%s | "REPLACE_FUNCNAME_LOAD" | "LOG_LINELEN" | 获取游标 "REPLACE_TABLEDESC" 失败 sqlcode[%d],请重启应用\n",
GetLocalTimeString( gacTimeStringBuffer , 256 , "%Y-%m-%d %H:%M:%S" ),
__LINE__,
sqlca.sqlcode );

/* 断开 内存映射表 地址连接 */
IPCDetachShareMemory( pmp );

/* 关闭游标 */
EXEC SQL CLOSE REPLACE_CURSORNAME;

return -5;
}

REPLACE_DBVARFUNC( &REPLACE_STRUCT_ARG );

memcpy( pmpREPLACE_STRUCT_ARG , &REPLACE_STRUCT_ARG , sizeof( struct REPLACE_STRUCT_TYPE ) );
pmpREPLACE_STRUCT_ARG ++ ;
}

/* 断开 内存映射表 地址连接 */
IPCDetachShareMemory( pmp );

/* 关闭游标 */
EXEC SQL CLOSE REPLACE_CURSORNAME;

WriteLog( gacLogFilename,
"%s | "REPLACE_FUNCNAME_LOAD" | "LOG_LINELEN" | %ld条记录进入 内存映射表 "REPLACE_TABLEDESC" \n",
GetLocalTimeString( gacTimeStringBuffer , 256 , "%Y-%m-%d %H:%M:%S" ),
__LINE__,
dlRecordAmount );

return 0;
}

#undef REPLACE_STRUCT_TYPE
#undef REPLACE_STRUCT_ARG
#undef REPLACE_SHEKEY
#undef REPLACE_TABLENAME
#undef REPLACE_TABLEDESC
#undef REPLACE_CURSORNAME
#undef REPLACE_DBVAR
#undef REPLACE_DBVARFUNC
#undef REPLACE_FUNCNAME_FETCH
#undef REPLACE_FUNCNAME_LOAD
#undef REPLACE_FUNCNAME_COMPARE_PROC
#undef REPLACE_FUNCNAME_UPDATE_PROC

附件二、下面这些函数被上面的程序调用过,代码存放在本人设计的iIPC、iLibX基础函数库中。

/*
** 函数名 : IPCIsShareMemoryExist
** 函数描述 : 判断共享存储块存在
** 输入参数说明 : _IPC_ID_T ipckey 共享存储块的id
** 返回值 : 存在 返回 IPC_SHAREMEMORY_RETURN_EXIST
** 不存在 返回 IPC_SHAREMEMORY_RETURN_ISNT_EXIST
*/

int IPCIsShareMemoryExist( _IPC_KEY_T ipckey )
{
_IPC_ID_T ipcid;

ipcid=shmget( ipckey , 1 , IPC_CREAT | IPC_EXCL | _giIPCPermission );

if( ipcid == -1 )
return IPC_SHAREMEMORY_RETURN_EXIST;

IPCDestroyShareMemory( ipcid );

return IPC_SHAREMEMORY_RETURN_ISNT_EXIST;
}

/*
** 函数名 : IPCOpenShareMemory
** 函数描述 : 打开共享存储块
** 输入参数说明 : _IPC_KEY_T ipckey 共享存储块的key
** 返回值 : 成功,返回 共享存储块的id
** 失败,返回 错误代码
*/

_IPC_ID_T IPCOpenShareMemory( _IPC_KEY_T ipckey )
{
int iReturnValue;
_IPC_ID_T ipcid;

iReturnValue = IPCIsShareMemoryExist( ipckey ) ;

if( iReturnValue == IPC_SHAREMEMORY_RETURN_ISNT_EXIST )
return IPC_SHAREMEMORY_RETURN_ISNT_EXIST;

ipcid = shmget( ipckey , 0 , IPC_CREAT | _giIPCPermission );

if( ipcid == -1 )
return IPC_SHAREMEMORY_ERROR_CANT_OPEN;

return ipcid;
}

/*
** 函数名 : IPCAttachShareMemory
** 函数描述 : 连接共享存储块首地址
** 输入参数说明 : _IPC_ID_T ipcid 共享存储块的id
** 返回值 : 存在 返回 首地址
** 不存在 返回 NULL
*/

void *IPCAttachShareMemory( _IPC_ID_T ipcid )
{
void *pvAttach;

pvAttach = shmat( ipcid , NULL , 0 );

if( pvAttach == (void *)-1 )
return NULL;
else
return pvAttach;
}

/*
** 函数名 : IPCDetachShareMemory
** 函数描述 : 断开共享存储块首地址
** 输入参数说明 : void *pvDetach 共享存储块连接首地址
** 返回值 : 存在 返回 IPC_SHAREMEMORY_RETURN_SUCCESS
** 不存在 返回 IPC_SHAREMEMORY_ERROR_CANT_DETACH
*/

int IPCDetachShareMemory( void *pvDetach )
{
int i;

i = shmdt( pvDetach );

if( i == -1 )
return IPC_SHAREMEMORY_ERROR_CANT_DETACH;
else
return IPC_SHAREMEMORY_RETURN_SUCCESS;
}

/*
** 函数名 : WriteLog
** 函数描述 : 正常写日志函数
** 输入参数说明 : char *pcLogFileName 日志文件名
** char *pcFormatString 日志格式串
** ... 日志参数列表
** 返回值 : 成功,返回 TRUE
** 失败,返回 FALSE
** 更新日志 : 2003/10/18 创建
*/

BOOL WriteLog( char *pcLogFileName, char *pcFormatString, ... )
{
va_list valist;
BOOL ret;

va_start( valist, pcFormatString );

ret=DoLog(pcLogFileName, LOG_WRITE_APPEND, LOG_MODE_RETURN, pcFormatString, valist );

va_end( valist );

return ret;
}

/*
** 函数名 : DoLog
** 函数描述 : 日志记录原始函数
** 输入参数说明 : char *pcLogFileName 日志文件名
** int iWriteFlag 日志写标志
** int iModeFlag 日志写完后操作标志
** char *pcFormatString 日志格式串
** va_list valist 日志参数列表
** 返回值 : 成功,返回 TRUE
** 失败,返回 FALSE
** 更新日志 : 2003/10/18 创建
*/

BOOL DoLog( char *pcLogFileName, int iWriteFlag, int iModeFlag, char *pcFormatString, va_list valist )
{
FILE *fpLogFile=NULL;

if( iWriteFlag == LOG_WRITE_NEW )
fpLogFile=fopen(pcLogFileName,"w");
else if( iWriteFlag == LOG_WRITE_APPEND )
fpLogFile=fopen(pcLogFileName,"a");

if( fpLogFile == NULL )
return FALSE;

if( vfprintf( fpLogFile, pcFormatString, valist ) < 0 )
return FALSE;

fflush(fpLogFile);

fclose(fpLogFile);

if( iModeFlag == LOG_MODE_RETURN )
return TRUE;
else if( iModeFlag == LOG_MODE_EXIT )
exit(0);

return TRUE;
}

/*
** 函数名 : GetLocalTimeString
** 函数描述 : 格式化时间字符串(本地时间)
** 输入参数说明 : size_t bufsize 用于存放格式化后字符串的缓冲区大小
** const char *format 格式化串
** 输出参数说明 char *buf 用于存放格式化后字符串的缓冲区首地址(建议预分配256字节)
** 返回值 : 成功,返回 TRUE
** 失败,返回 FALSE
** 更新日志 : 2003/10/18 创建
** 2004/3/3 修改 格式化后字符串的缓冲区首地址 原型
*/

char *GetLocalTimeString(char *buf,size_t bufsize,const char *format)
{
struct tm *time_tm;

time_tm=GetLocalTimeStruct();

if( strftime(buf,bufsize,format,time_tm) != 0 )
return buf;
else
return NULL;
}

/*
** 函数名 : GetLocalTimeStruct
** 函数描述 : 把UNIX纪元到现在的秒数以struct tm结构的形式返回(本地时间)
** 返回值 : 秒数
** 更新日志 : 2003/10/18 创建
*/

struct tm *GetLocalTimeStruct()
{
time_t time_cl;

time(&time_cl);

return localtime( &time_cl );
}

[/code:1:827619af4e]

 fwizard 回复于:2004-11-03 08:20:16
支持

 aero 回复于:2004-11-03 08:36:53
^_^,好,支持原创。而且名称尤其有创意。

 albcamus 回复于:2004-11-03 08:52:52
Donald E. Knuth有本书叫做《Concrete Math》,楼主的文章题目是从这里借鉴的吧?呵呵好文章

 BetonArmEE 回复于:2004-11-03 10:38:59
说实话,饿没看过Donald E. Knuth《Concrete Math》, 呵呵~

 lghit 回复于:2004-11-03 11:17:55
好东东,谢谢共享,(不过用文件处理也应该比较快的)

 BetonArmEE 回复于:2004-11-03 11:57:06
楼上的 每次去读文件和读内存哪个快呢?呵呵

 tyffly 回复于:2004-11-03 12:24:41
支持!请问一下楼主有没有试过最多多少条记录的查询
与数据库直接的select的差别?

 zhujunsoft 回复于:2004-11-03 13:59:37
楼主使用了共享内存,请问用共享内存与用内存映射文件(mmap)有什么区别,哪个更好呢,希望大家顶下

 BetonArmEE 回复于:2004-11-03 14:48:52
[quote:28875a175d="tyffly"]支持!请问一下楼主有没有试过最多多少条记录的查询
与数据库直接的select的差别?[/quote:28875a175d]
我承认,如果记录巨多的话,还是数据库的select快,因为数据库用了索引来select,但是如果记录不是很多,并且select比较简单,并且数据库其它操作负荷比较大,让共享内存代替数据库静态表的查询,也不失为一种提高效率的选择吧

 BetonArmEE 回复于:2004-11-03 14:55:43
[quote:a35e68f16b="zhujunsoft"]楼主使用了共享内存,请问用共享内存与用内存映射文件(mmap)有什么区别,哪个更好呢,希望大家顶下[/quote:a35e68f16b]
你说的mmap是windows平台的ipc技术吧,我很长时间没接触windows开发了,据我以前的理解,mmap使用范围比unix的共享内存广,它既可直接用于磁盘文件的映射也可以作为一块内存来使用,但我曾经用过mmap,个人感觉它的稳定性没unix的共享内存好 ^_^

 windflowers1976 回复于:2004-11-03 17:04:09
[quote:f1337fb28e]你说的mmap是windows平台的ipc技术吧[/quote:f1337fb28e]

mmap 是UNIX系统V标准函数,和WIN 没有关系,WIN下实现共享内存机制是使用和mmap 类似的函数createfile ... 创建共享内存文件来实现的。
回答问题还是考究些。

去GOOGLE 一下
http://www.opengroup.org/onlinepubs/009695399/functions/mmap.html

 FH 回复于:2004-11-03 17:29:26
效率包括两个方面:模块的效率和整体的效率。
也见过一些为了片面追求自己模块的效率而降低了软件系统整体效率的例子,特别是当考量系统整体效率时,这些人还振振有辞地说自己模块的效率没有问题,问题在其它模块上面。殊不知,正是个别人为了某一模块局部效率而对系统资源的无端浪费,才造成了整体系统效率的低下。
说些题外话,供各位参考。

 BetonArmEE 回复于:2004-11-03 18:37:45
学习中...

 啥都不懂 回复于:2004-11-03 20:55:35
这些东西应该是后台而不是大前置系统的吧。

 ballball2 回复于:2004-11-03 22:48:56
真不错,支持原创!

 BetonArmEE 回复于:2004-11-03 23:15:24
[quote:5637b97638="啥都不懂"]这些东西应该是后台而不是大前置系统的吧。[/quote:5637b97638]
同行,你也是搞银行业务系统的吧
大前置为什么不能用,比如“公共机构对照表”,就是用来联系各个后台帐务机,由前置来控制,不大的静态表,放入共享内存提高查询速度。

 lishenglin 回复于:2004-11-04 11:10:53
共享内存结构如果能设计成hash表的结构,查询效率会高很多。完全可以参考或使用std的hash容器的方法设计。

 linux_newbie 回复于:2004-11-04 13:46:52
你这个应该只能在单进程且单线程环境中使用吧?
但是你又用到了共享内存,应该是特意用于可以并发的。

如果都是只读操作,例如你的FetchMapGGXTCS,这个到没有问题。
但是我看到你有一个函数UpdateMapGGXTCS,这个东西如果不提供保护,多进程更新同一个记录马上出现问题。

如果要上锁,可能要考虑读写锁,可能为了效率的原因,不能锁定整个表,还要实现细粒度的锁。这个复杂度马上提升10倍都不止。
还是让数据库去干吧。

 FH 回复于:2004-11-04 15:10:30
楼上看得真仔细!呵呵
俺见过很多用共享内存时不使用锁机制的,以为只是读就不需要上锁。
实际上,要想使应用365*24地运行,那么共享内存中内容的更新就不可避免。如果在读数据时不使用锁机制,那么就不能保证读到的数据的可靠性!
至于是否需要记录级锁,我认为倒是没有必要,更新时全部刷新一下就是了。

 robinliu76 回复于:2004-11-04 16:45:37
做过一个比较,如果在使用共享内存的时候采用HASH在效率上能够提高很多。另外将记录放入共享内存中采用平衡二叉树的结构能够解决数据量大的时候的读写问题。
另外同意楼上说的,不一定需要记录级的锁,可以采用刷新的方法。
希望大家多多指教!

 unicorns 回复于:2004-11-04 16:56:20
放到内存里是很常用的一种方法.
放到共享内存显然是为了让系统中有需要的进程都可以资源共享.
应该考虑给把内存中的数据重新组织.比如HASH或者AVL树.

关于更新的问题我曾经用过一个比较笨的办法,不过可以近乎实时的更新.
方法是在内存中开两片同样的区域,一边用于应用中的查找,另一边在更新,可以指定更新的间隔时间.更新完毕后将两块数据的角色对换.
优点是逻辑简单,调试容易,维护也简单.
缺点显然是大量的浪费了内存,所以这种做法不到万不得以,不是必须要求高效率,实时性很强的关键应用是不值得推荐的。
不过我觉得也不失为解决问题的一种办法,拿出来给大家参考.

 robinliu76 回复于:2004-11-04 17:12:27
楼上的方法如果在共享内存小的情况下应该是很好的一个解决办法,但是如果需要的内存空间大的话,就没有办法使用了。

希望学到更多东西......

 unicorns 回复于:2004-11-04 17:19:38
数据量非常大的情况下,可以考虑不把全量数据LOAD进内存.
只LOAD部分,然后模拟LRU算法来做.
我原来是这么计划的,后来发现内存够了也就没管了.

 linux_newbie 回复于:2004-11-04 18:33:29
[quote:af011cf3a3]方法是在内存中开两片同样的区域,一边用于应用中的查找,另一边在更新,可以指定更新的间隔时间.更新完毕后将两块数据的角色对换. [/quote:af011cf3a3]

我倒是有点疑问。那个用于更新的是不是允许并发呢?如果允许,那么同样需要锁定机制。如果对整个区域加锁,那么任何更新无法同时运行,即使它们是更新不同的记录。如果需要细粒度的锁,那么复杂性就提升了。

既然都需要锁,如果为什么不直接使用一个区域,不过是使用读写锁而已。

而且更新完毕后两块数据的角色对换,这个操作也必须是一个原子操作才行。

总之,麻烦。

 unicorns 回复于:2004-11-05 10:56:18
更新不存在并发问题,
在同一时间肯定只有一个线程(或进程)访问用于更新的那块空间.

两块数据块角色对换,只要设置一个指示器,给这个指示器加个锁就可以实现了.

 ibmxp 回复于:2004-11-05 13:52:30
不同的应用存在的效率瓶劲不同,应用的方法也不同!!!
还得看具体的环境!

 linux_newbie 回复于:2004-11-05 18:03:14
[quote:9eb3b45176]在同一时间肯定只有一个线程(或进程)访问用于更新的那块空间[/quote:9eb3b45176]

你要是这种情况的话,我估计只需要多一块内存可以容纳单条记录的就可以了。

先在这个私有内存中更新,填好后,直接将Read List中的某个指针“突然”指向这个刚刚填好的结构就可以了。不过这要求你最好使用单链表。一般32位机器对一个Int赋值都是原子操作。

 idyms 回复于:2004-11-05 20:39:59
将系统重要参数的数据表,在系统启动时导入共享MEM,包括在以后工作中通过对系统参数表修改,同时做到更新共享MEM,对提高系统性能很好.
但要是对当日明细表要是也这样做,哈哈!!!

 playmud 回复于:2004-11-06 00:11:58
对于专用系统,用到这个的确不错,我想作者的重点放在把数据库操作转换成了对内存操作,显然对于操作频繁的表来讲给数据库的压力减少了很多,
读于读取数据更有用吧,其实要考虑的事情还很多,更新的时候需要同时更新结构和数据库==,这些对于静态表来说可以不用考虑,如码表,它是固定的东西。所有有些人多虑了。简单的事情不要搞复杂,实用是第一位。

 FH 回复于:2004-11-06 22:56:53
不是实时系统的话,这么做还可以。否则,数据库更新了,而共享内存还没有更新时,会有问题。

 BetonArmEE 回复于:2004-11-08 22:28:45
[quote:114118a012="linux_newbie"]你这个应该只能在单进程且单线程环境中使用吧?
但是你又用到了共享内存,应该是特意用于可以并发的。

如果都是只读操作,例如你的FetchMapGGXTCS,这个到没有问题。
但是我看到你有一个函数UpdateMapGGXTCS,?.........[/quote:114118a012]

所以我说只适用于简单的查询和更新操作。
在银行搞开发的人都知道,每天晚上对帐的时候,“公共系统参数表”中的“当前对帐场次”只由一个进程更改,所以不会出现问题的。
我们的系统中使用了“内存流水”,这个共享内存中的一条记录结构可能存在被多个进程同时更改,这种情况我的“内存映射表”就不适用了。我们专门设计了“内存流水”操作函数库共享内存(加入了锁机制)。

 BetonArmEE 回复于:2004-11-08 22:30:30
[quote:149e7e099d="lishenglin"]共享内存结构如果能设计成hash表的结构,查询效率会高很多。完全可以参考或使用std的hash容器的方法设计。[/quote:149e7e099d]

我们使用的是ansi c编译器

 BetonArmEE 回复于:2004-11-08 22:33:43
[quote:fb68645763="unicorns"]放到内存里是很常用的一种方法.
放到共享内存显然是为了让系统中有需要的进程都可以资源共享.
应该考虑给把内存中的数据重新组织.比如HASH或者AVL树.

关于更新的问题我曾经用过一个比较笨的办法,不过可以近乎实..........[/quote:fb68645763]

我设计的共享内存存放数据的结构只是很简单的结构数组方式,楼上所说的是在我的基础上设计更合理的逻辑存放方式,非常值得考虑,谢谢 ^___^

 BetonArmEE 回复于:2004-11-08 22:35:39
[quote:aae11e1ea3="robinliu76"]楼上的方法如果在共享内存小的情况下应该是很好的一个解决办法,但是如果需要的内存空间大的话,就没有办法使用了。

希望学到更多东西......[/quote:aae11e1ea3]

没错,当表数据比较大时还是使用表查询速度快,因为表使用了索引
当表数据不是很大时,使用内存便历更快些

 BetonArmEE 回复于:2004-11-08 22:38:08
[quote:d69ea4630c="idyms"]将系统重要参数的数据表,在系统启动时导入共享MEM,包括在以后工作中通过对系统参数表修改,同时做到更新共享MEM,对提高系统性能很好.
但要是对当日明细表要是也这样做,哈哈!!![/quote:d69ea4630c]

当然不会了,呵呵。

 BetonArmEE 回复于:2004-11-08 22:53:47
楼上很多朋友提到“内存映射表”的更新操作 需要 同时更新数据库表和内存表
我给出更新操作的回调函数,供大家批正,同样以“公共系统参数表”为例

[code:1:7a0ab146ee]

int UpdateCSZFromGGXTCSWhereCSXHProc( void *pvCondValue , void *pvUpdateValue , struct ggxtcs *pstGgxtcs )
{
char *pcCSXH = (char *)pvCondValue ;

ClearRight( pstGgxtcs->csxh );

if( STRCMP( pcCSXH , == , pstGgxtcs->csxh ) )
{
strcpy( dacCSXH , (char *)pvCondValue );
strcpy( dacCSZ , (char *)pvUpdateValue );

EXEC SQL
UPDATE ggxtcs
SET csz=:dacCSZ
WHERE csxh=:dacCSXH ;

if( sqlca.sqlcode )
{
EXEC SQL COMMIT;

return 2;
}
else
{
EXEC SQL COMMIT;

strcpy( pstGgxtcs->csz , dacCSZ );

return 0;
}
}
else
return 1;
}
[/code:1:7a0ab146ee]

再次强调,该更新操作只适用于简单非并发的更新操作,不希望楼下的再在更新的加锁问题上议论。复杂、并发的更新操作当然需要加锁,这是大家都知道的。
我写这篇的前提是:把某些记录比较少、查询、更新操作相对简单、操作非并发的数据库表放入共享内存来实现查询、更新,请大家不要把问题复杂化,我们不是在设计数据库系统。

谢谢广大同仁积极参与讨论,很多朋友提出的见解都能使我的设计更好的优化(比如,使用hash表代替结构数组),在此感谢楼上朋友们的精彩发言。
让我们分享自己的经验和技巧,共同成长,壮大中国unix程序员社群 ^___^

 影子-飞翔 回复于:2004-11-09 09:48:00
收到

 tommy7788 回复于:2004-11-09 21:45:53
cu里真是卧虎藏龙啊!

 cattiger 回复于:2004-11-11 23:09:13
ding

 dkknight 回复于:2004-11-13 15:13:54
不知道楼主的查询函数是如何实现的
不过在下以为在装入共享内存时,可以采用快速排序qsort()
查找的时候采用快速查找bsearch()
这样越是数据量大越是能体现出采用此中方法的好处
目前我对百万级数据量的处理都是采用此种方法,效果还不错

 frompinge 回复于:2004-11-13 16:43:34
在内存足够的情况下,使用共享内存,装载后快速排序,查询时二分查询。效果的确很明显。但在内存不足以装载合部数据的情况下,(比如数据大于N个G),系统会自动进行页面切换,使用二分查找的话,仍然显慢(系统资源耗费过大),这时候如果能使用一次或二次HASH,效率依然不错。
个人处理案例:某银行,日终批量,原有表都在千万级,日终批量时间5-6小时,优化后不到1小时。
但是,感觉如果对此类公用函数(装载/查询/更新/备份/切换/一致性)越提炼,慢慢就向数据库的方向去了。后来懒了,其实觉得数据库本身亦有太多学问,对大表的操作,在不同的环境配置下差别亦很大,都需要自己不断去测试的。

 FH 回复于:2004-11-13 22:02:04
日终的时候在内存里做倒是头一次听说。
出现意外了怎么办?比如说地震,瞬间电源全部切断,机器被烧毁,……等等,帐务怎么办?岂不是成了无头的乱账?客户的利益怎么保证?
我希望楼上的把这家机构披露出来,好让坛子里的各位早做打算。毕竟谁攒俩钱儿也不容易,是吧?

 frompinge 回复于:2004-11-14 12:47:24
这就是我上面说到的一致性问题了。楼上也用不着大惊小怪的,仔细想想数据库是怎么做的,不是一样的道理吗?

 cellar 回复于:2004-11-14 16:41:45
[quote:78ec1d8e59="FH"]日终的时候在内存里做倒是头一次听说。
出现意外了怎么办?比如说地震,瞬间电源全部切断,机器被烧毁,……等等,帐务怎么办?岂不是成了无头的乱账?客户的利益怎么保证?
我希望楼上的把这家机构披露出来,好让..........[/quote:78ec1d8e59]
看了很多C++版的贴子,发现FH老师总是持反对意见哦,我觉得FH老师知识面非常广,说的也很有见地,只是话语稍赚刻簿了些,这对于发贴者恐怕多少也是个打击,不利于我们这些菜菜学习啊:)

 FH 回复于:2004-11-14 23:06:46
[quote:f3205f3058="cellar"]
看了很多C++版的贴子,发现FH老师总是持反对意见哦,我觉得FH老师知识面非常广,说的也很有见地,只是话语稍赚刻簿了些,这对于发贴者恐怕多少也是个打击,不利于我们这些菜菜学习啊:)[/quote:f3205f3058]
呵呵,承蒙夸奖,不敢当!
确实我有时候说话是刻薄了些,但我觉得我没有恶意。
有些时候坛子里的一些同志喜欢使用祈使句,而其提供的结论或方法又确实是禁不住推敲的,这种时候我就比较容易冲动,生怕个别人将初学者引上歧路,也就慌不择言了。
谢谢阁下的提醒,俺以后一定多多注意。

 linux_newbie 回复于:2004-11-15 11:13:44
FH的帖子很有启发性,确实如果只在某个时刻由一个进程进行一次更新的话,这样当然避免了并发,但是你如何又保证这些被你缓存了的修改能够最后提交到数据库中去呢?例如在你更新的那个时刻之前OS crash了,内存属于volatile storage,掉电后信息丢失,client所做的修改如果仅仅只buffer保存在内存中,那么全部修改将丢失。或者你这个表中存储的信息根本就无须保证这种语义?


[quote:a810eb9d6a]这就是我上面说到的一致性问题了。楼上也用不着大惊小怪的,仔细想想数据库是怎么做的,不是一样的道理吗? [/quote:a810eb9d6a]

数据库一般都是通过stable storage来实现一致性。通常也就是指硬盘。一般数据库都有log,有一套recovery机制,在内存掉电、系统crash的情况下,可以根据stable storage来恢复。可能接下来再问,硬盘也可能坏,怎么办?一般再使用物理冗余的方式,例如硬盘阵列。阵列坏了?首先这个错误应该马上被检测到,然后系统可以拒绝服务。如果还检测不出来继续运行,可能会混入错误的数据,这种概率太小,那就祷告吧。

 odin_free 回复于:2004-11-16 10:37:42
c版的气氛真好啊~~~看来得多学习斑竹的管理

 aero 回复于:2004-11-16 10:42:24
[quote:e814d67266="odin_free"]c版的气氛真好啊~~~看来得多学习斑竹的管理[/quote:e814d67266]

呵呵,和从前差的远呢。

 yimian0706 回复于:2004-11-16 21:09:11
不错,很经典

 BetonArmEE 回复于:2004-11-16 21:48:37
[quote:685da9c3bc="dkknight"]不知道楼主的查询函数是如何实现的
不过在下以为在装入共享内存时,可以采用快速排序qsort()
查找的时候采用快速查找bsearch()
这样越是数据量大越是能体现出采用此中方法的好处
目前我对百万级数据量的处理都是?.........[/quote:685da9c3bc]

原来是采用结构数组方式 目前正在考虑改造成hash或者二叉数方式

 BetonArmEE 回复于:2004-11-16 21:51:08
[quote:5d6e5de896="frompinge"]在内存足够的情况下,使用共享内存,装载后快速排序,查询时二分查询。效果的确很明显。但在内存不足以装载合部数据的情况下,(比如数据大于N个G),系统会自动进行页面切换,使用二分查找的话,仍然显慢(系统资源..........[/quote:5d6e5de896]

"但是,感觉如果对此类公用函数(装载/查询/更新/备份/切换/一致性)越提炼,慢慢就向数据库的方向去了。后来懒了,其实觉得数据库本身亦有太多学问,对大表的操作,在不同的环境配置下差别亦很大,都需要自己不断去测试的。"
同感,呵呵 ^_^

 BetonArmEE 回复于:2004-11-16 21:55:17
[quote:aa7450c530="FH"]日终的时候在内存里做倒是头一次听说。
出现意外了怎么办?比如说地震,瞬间电源全部切断,机器被烧毁,……等等,帐务怎么办?岂不是成了无头的乱账?客户的利益怎么保证?
我希望楼上的把这家机构披露出来,好让..........[/quote:aa7450c530]

如果真的地震了、机器烧毁了,硬盘数据都没了,何止内存丢失
这类事毕竟概率很小,小到可以忽律不记
我们这里防灾备还是有的,我总行在杭州,与上海做了互备,同时在衢州也做了一备

 BetonArmEE 回复于:2004-11-16 21:57:38
[quote:29f5bc1ef0="FH"]
呵呵,承蒙夸奖,不敢当!
确实我有时候说话是刻薄了些,但我觉得我没有恶意。
有些时候坛子里的一些同志喜欢使用祈使句,而其提供的结论或方法又确实是禁不住推敲的,这种时候我就比较容易冲动,生怕个别人将初?.........[/quote:29f5bc1ef0]

FH老师想的比较多,考虑比较细,这是对帖友们负责,呵呵

 gqm 回复于:2004-11-19 15:13:24
想问一下我想直接访问内存这样可以吗
mmap(NULL,
     dmabuffer_size,         /* length of region to map */
     PROT_WRITE|PROT_READ             /* select the output buffer
(PROT_READ alone selects input) */
     /* NOTE: I had to add PROT_READ to
make this work with FreeBSD.
However, this causes the code to
fail under Linux. SNAFU. */
     MAP_FILE | MAP_SHARED,  /* see the mmap() manual page */
     dsp,  /* opened file to map */
     0);  /* start at offset zero */

 FH 回复于:2004-11-19 15:54:59
[quote:e32356e660="BetonArmEE"]

如果真的地震了、机器烧毁了,硬盘数据都没了,何止内存丢失
这类事毕竟概率很小,小到可以忽律不记
我们这里防灾备还是有的,我总行在杭州,与上海做了互备,同时在衢州也做了一备[/quote:e32356e660]
如果关键任务在内存中进行,灾难备份不一样是摆设?
数据库可以保存事务的轨迹,一旦出现问题,一切还能恢复,而内存就不一样了。
有些任务可以通过内存映像的方式提高效率,但我不主张关键任务也这么做。

 unicorns 回复于:2004-11-19 17:35:04
[quote:50e0432211="BetonArmEE"]

原来是采用结构数组方式 目前正在考虑改造成hash或者二叉数方式[/quote:50e0432211]

单从效率上考虑的话
我觉得象楼主这样的需求如果可以的话使用HASH是最好的.
这取决于是否能找到很好的散列函数.

平衡二叉树的时间复杂度和qsort后在bsearch是一样的.
因为你的需求是固定的码表,成员不会动态增加所以没有必要用平衡二叉树.

如果再考虑比如程序的可读性,可维护性,工作量等其他效率外的因素
还是用qsort和bsearch可行一些.

 unicorns 回复于:2004-11-19 17:39:53
[quote:6a354189a9="frompinge"]这就是我上面说到的一致性问题了。楼上也用不着大惊小怪的,仔细想想数据库是怎么做的,不是一样的道理吗?[/quote:6a354189a9]

对银行的业务不是很熟悉.
能大致讲讲你是怎么保证一致性的吗,谢谢.

 cattiger 回复于:2004-11-19 20:25:13
看了各位的帖子,说得都很不错,本人从事航天测控软件的开发3年,在效率和稳定有冲突的时候稳定是第一位的,接触的系统都是高可用双机热备份的。在链表和数组的选择上,能用数组解决的绝不用链表。实时性强的坚决用文件,而不选择实时入数据库!共享内存用得也非常多,但共享内存的结构大小都是事先定义且固定的,块首有信号量单元,方便读写者互斥读写!

楼主做银行业务的,稳定可靠应该是第一位的,用过多的技巧去实现是不可取的,应尽量逻辑简单,可维护性好。牺牲效率和内存,换来稳定是值得的!!

 I love c too 回复于:2004-11-19 22:47:16
路过,虽然不懂,但是我会努力学习的
希望大家帮助n_n

 BetonArmEE 回复于:2004-11-20 17:23:36
[quote:6a1c511695="cattiger"]看了各位的帖子,说得都很不错,本人从事航天测控软件的开发3年,在效率和稳定有冲突的时候稳定是第一位的,接触的系统都是高可用双机热备份的。在链表和数组的选择上,能用数组解决的绝不用链表。实时性强的坚决用?.........[/quote:6a1c511695]

全省的ATM、柜面跑全省大前置,你估计一下日交易量吧,呵呵
当然,我也不主张过多的使用技巧,我们可以在逻辑简单和技巧之间取一个合适点

 BetonArmEE 回复于:2004-11-20 17:24:58
[quote:f7938f4167="unicorns"]

对银行的业务不是很熟悉.
能大致讲讲你是怎么保证一致性的吗,谢谢.[/quote:f7938f4167]

这个涉及到前置的交易调度系统和冲正系统,恕我不能说的太多 ^_^

 BetonArmEE 回复于:2004-11-20 17:25:48
[quote:c048aa5e68="unicorns"]

单从效率上考虑的话
我觉得象楼主这样的需求如果可以的话使用HASH是最好的.
这取决于是否能找到很好的散列函数.

平衡二叉树的时间复杂度和qsort后在bsearch是一样的.
因为你的需求是固定的码表,成?.........[/quote:c048aa5e68]

恩 ^_^

 BetonArmEE 回复于:2004-11-20 17:26:50
[quote:2c16af2550="FH"]
如果关键任务在内存中进行,灾难备份不一样是摆设?
数据库可以保存事务的轨迹,一旦出现问题,一切还能恢复,而内存就不一样了。
有些任务可以通过内存映像的方式提高效率,但我不主张关键任务也这么做。[/quote:2c16af2550]

对 ^_^

 linux_newbie 回复于:2004-11-20 21:56:24
也别Hash,qsort了,我还是不明白。
你不是使用共享内存吗,那么你这个共享内存应该只在一台服务器上吧?应该不会上海、杭州都有备份的共享内存,而且它们之间都进行实时的同步更新吧。

既然你使用共享内存,地震这些先不说,由于系统、程序故障造成宕机、crash而内存丢失的情况你如何处理?这个概率可不小。

我估计你的备份是数据库的备份,可是这些在共享内存中的东西,按照你的说法,不是要到晚上才更新数据库吗?那么如果下午程序crash了,上午那些更新是否就丢失了呢?

 BetonArmEE 回复于:2004-11-21 21:53:39
[quote:b6f2ef6d04="linux_newbie"]也别Hash,qsort了,我还是不明白。
你不是使用共享内存吗,那么你这个共享内存应该只在一台服务器上吧?应该不会上海、杭州都有备份的共享内存,而且它们之间都进行实时的同步更新吧。

既然你使用共享内存,地?.........[/quote:b6f2ef6d04]

备份的是交易数据
内存里的是交易中的数据映像,和一些静态表

 啥都不懂 回复于:2004-11-22 17:52:05
[quote:de30bcbf06="BetonArmEE"]

这个涉及到前置的交易调度系统和冲正系统,恕我不能说的太多 ^_^[/quote:de30bcbf06]

一致性不都是由数据库来完成的,搞那么神秘干什么,交易调度和l冲正也不过是通过事务来完成的。其实我觉得做银行核心系统的技术门槛很低,关键是懂业务,能和别人交流。需求谈好后一般懂C的培训两个星期就可以开发了。

 BetonArmEE 回复于:2004-11-22 21:06:12
[quote:b65448e6a0="啥都不懂"]

一致性不都是由数据库来完成的,搞那么神秘干什么,交易调度和l冲正也不过是通过事务来完成的。其实我觉得做银行核心系统的技术门槛很低,关键是懂业务,能和别人交流。需求谈好后一般懂C的培训两个星期就可以开?.........[/quote:b65448e6a0]

错,在多个后台的环境下,一致性不能仅既通过数据库事务提交来完成,以前我们使用cics中间件来保证,由于cics的某些局限性,现在新的系统通过自己设计一套一致性体系来保证,交易调度系统和冲正系统配合是关键。

 啥都不懂 回复于:2004-11-23 18:27:34
[quote:6d55994289="BetonArmEE"]

错,在多个后台的环境下,一致性不能仅既通过数据库事务提交来完成,以前我们使用cics中间件来保证,由于cics的某些局限性,现在新的系统通过自己设计一套一致性体系来保证,交易调度系统和冲正系统配合是关键。[/quote:6d55994289]

多个后台又怎么样,你们的系统的交易调度和冲正不是下面的这些模式吗?
1、利用数据库分布式事务来控制
2、利用中间件的全局事务来控制
3、自己写交易调度平台来控制:
[code:1:6d55994289]
PLATFORM()
{
    if ( Service1() < 0 ) {
       EXEC SQL rollback work ;
       返回;
   }
    if ( Service2() < 0 ) {
      EXEC SQL rollback work;
      返回;
   }
   .....
   .....
   EXEC SQL commit work ;
   返回;
}
这也实际上是上面两种方式之一。
[/code:1:6d55994289]
4、利用数据库的XA接口开发。

你们自己设计的一致性体系难道没用到数据库的事务?如果是,那是我无知。

 BetonArmEE 回复于:2004-11-23 19:18:13
3、自己写交易调度平台来控制

算我无知好了

关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有