STVM 高效缓存数据库

STVM

STVM(truck of Virtual memory table)是一个开源的使用ANSI C语言编写、支持本地API调用和网络调用,全表数据基于IPC共享内存方式存储、是一款全新的内存数据库、百万级的内存无锁队列,第一个版本于2018年3月1号提交至githup。

基于C语言struck结构定义记录行,RB-Tree和hash作为主要算法的内存数据库,是一款介于SQL和NOSQL之间的一款高速缓存数据库, 一款将队列与数据库完美结合的缓存数据库。

数据全部存储在内存中,支持本地接口和网络接口。

定义

stvm是一款其介于SQL和NOSQL之间的数据库,拥有以下主要特点

支持SQL基本语法(本版本支持insert、update、delete、select、group order、count,first,游标)功能。

操作语法简单。

支持序列

支持百万级别队列(gcc版本 >= 4.12)

支持唯一索引、查询索引和组合索引(组合索引不必考虑索引字段先后)

支持条件动态查询

内置记录点击量

集群、主-子分布同步。

事务功能(假性事务)

数据持久性,进程异常退出,不会导致数据丢失,除非系统宕机。

支持网络API和函数API, 支持多线程、多进程等等

内置数据版本,维护数据一致安全。

多机集群,数据分区

当然也有一些缺陷,索引长度限制目前版本64位,修改长度需编译项目、启动后不能修改、删除表字段,不能联表查询。

STVM是一款高速缓存数据库,高速队列表,表字段定义来源与C语言STRUCK,动态条件赋值只需要对结构体成员赋值即为查询条件。

整个操作提供特定API访问,减少解析SQL和字段的时间。

全表数据分3个区(表头区、索引区、数据区),并且全部存储在一块内存中。

每张表定义一个数据库块,每张表拥有自己读写锁,支持线程。

预留拓展表空间参数,但效率会降低扩展个数倍

 

性能测试

STVM本地调用性能主要优势:

  • 不需要任何IO
  • 不要任何解析
  • 整个过程在内存中完成。
  • 每张表都有独自的读写锁。

STVM网络调用性能主要优势:

  • 数据解析模块。

REDIS哈希与STVM性能比较

REDIS列表与STVM无锁队列性能比较

支持语言

CC++Java……
支持支持待支持待支持

 

字段类型

字段类型有LONG,CHAR,DOUB(对应整型、字符型、浮点型)

在走IO调用下,STVM为了自我保护最大单条记录最大1M,因此建议不要超过此值,但是本地调用方式行记录大小将受该机器限制。

为了避免内存的浪费,唯一索引和查询索引最多各一组。

 

使用手册

启动环境变量:

TVMDBD=/home/stvm/tvmdb 指向stvm工作目录

TVMCFG=/home/stvm/tvmdb/.tvm.run 启动参数

配置完成后,stvm -w 将采用缺省参数以本机模式启动系统。

如果集群模式请配置 stvm.conf,然后用stvm -c stvm.conf编译配置文件即可。

STVM对外提供2个开发运维工具stvm和调试工具detvm。

stvm -w启动系统

stvm -s停止系统

STVM也提供一个类型sqlpuls类型简单工具,输入:

stvm SQL 进入SQL界面(该工具仅仅用来运维调试使用)。

stvm DOM 进入域的维护

建议运行在64位机器上,因为32位机器最大寻址4G,因此创建单表不能超过4G。

 

使用场景

    该软件本版本建议用于数据库缓存, 减少对数据的操作,提高应用性能。    

    目前该软件仅本地api功能用于公司生产服务器上的数据库缓存。

 

开发流程

创建表

方式一:

// 定义表序号

#define TBL_USER_INFO 10

必须用C定义STRUCK:

typedef struct  __TBL_USER_INFO
{
   long    acct_id;
   char    user_no[21];
   char    user_type[2];
   char    user_nm[81];
   char    user_addr[161];
}dbUser;

定义创建表函数

CREATE  lCreateUserInfo()
{
   DEFINE(TBL_USER_INFO, "", dbUser)
   FIELD(dbUser,    acct_id,        FIELD_LONG)
   FIELD(dbUser,    user_no,        FIELD_CHAR)
   FIELD(dbUser,    user_type,      FIELD_CHAR)
   FIELD(dbUser,    user_nm,        FIELD_CHAR)
   FIELD(dbUser,    user_addr,      FIELD_CHAR)
 
   CREATE_IDX(NORMAL)         //    创建查询索引
   IDX_FIELD(dbUser, acct_id,       FIELD_LONG)
 
   CREATE_IDX(UNQIUE)         //    创建唯一索引
   IDX_FIELD(dbUser, user_no,       FIELD_CHAR)
   IDX_FIELD(dbUser, user_type,     FIELD_CHAR)
 
   FINISH
}

调用api:

lCreateTable(pstSavm, TBL_USER_INFO, 100000, lCreateUserInfo)  完成对TBL_USER_INFO表的创建。


TBL_USER_INFO 创建的表序号,其中pstSavm为线程句柄

100000初始化内存记录空间条数。

lCreateUserInfo创建表定义函数。

调用成功则返回RC_SUCC, 失败返回RC_FAIL, 失败可调用pstSavm->m_lErrno获取处理失败错误码,sGetTError(pstSavm->m_lErrno)获取错误信息。

方式二:

set TABLE=20
set TABLESPACE=10000

create table TBL_USER_INFO
(
    acct_id     long,
    user_no     char(21),
    user_type   char(2),
    user_nm     char(81),
    user_addr   char(161),
    user_phone  char(31)
);

-- Create indexes
create unique index (user_no, user_type);
create index  (acct_id);

comment on TBL_USER_INFO.user_phone is 'phone';

将创建表脚本,保存至文件 tbl_user_info.def, 表字段支持类型char、shor、int、long、llong、double、float。

在stvm SQL执行 create tbl_user_info.def来创建该表。

表对应的结构体可以通过 stvm -t table来导出。

操作表(单条查询)

dbUser stUser;
SATvm  *pstSavm = (SATvm *)pGetSATvm();
 
//    初始化TBL_USER_INFO表,每张表都需要初始化一次, 对于表重建后,需要重新初始化一次。
if(RC_SUCC != lInitSATvm(pstSavm, TBL_USER_INFO))
{
    fprintf(stderr, "init failed, err:(%d)(%s)\n", pstSavm->m_lErrno, sGetTError(pstSavm->m_lErrno));
    return ;
}

conditinit(pstSavm, stUser, TBL_USER_INFO);                // 绑定变量
conditnum(pstSavm, stUser, acct_id, 10021);                // 查询条件赋值
conditstr(pstSavm, stUser, user_no, "20180223");           // 查询条件赋值
 
if(RC_SUCC != lSelect(pstSavm, (void *)&stUser))           // 单条记录查询
{
    fprintf(stderr, "Select错误ep:%d, err:(%d)(%s)\n", pstSavm->m_lEType, pstSavm->m_lErrno,
        sGetTError(pstSavm->m_lErrno));
    return ;
}
 
fprintf(stdout, "acct_id:%ld, user_no:%s, user_type:%s, user_nm:%s, user_addr:%s\n",
    stUser.acct_id, stUser.user_no, stUser.user_type, stUser.user_nm, stUser.user_addr);

操作表(多条查询)

size_t  i = 0, lOut;
dbUser  *pstUser = NULL;
SATvm   *pstSavm = (SATvm *)pGetSATvm();

//  初始化TBL_USER_INFO表,每张表都需要初始化一次, 对于表重建后,需要重新初始化一次。                
if(RC_SUCC != lInitSATvm(pstSavm, TBL_USER_INFO))
{
    fprintf(stderr, "init failed, err:(%d)(%s)\n", pstSavm->m_lErrno, sGetTError(pstSavm->m_lErrno));
    return ;
}
     
conditinit(pstSavm, stUser, TBL_USER_INFO);                   // 绑定变量
conditstr(pstSavm, stUser, user_type, "1");                   // 查询条件赋值
conditstr(pstSavm, stUser, user_no, "20180223");              // 查询条件赋值
 
 
if(RC_SUCC != lQuery(pstSavm, &lOut, (void **)&pstUser))      // 批量查询查询  
{
    fprintf(stderr, "Select错误ep:%d, err:(%d)(%s)\n", pstSavm->m_lEType, pstSavm->m_lErrno,
        sGetTError(pstSavm->m_lErrno));
    return ;
}
  
for(i = 0; i < lOut; i ++)
{
    fprintf(stdout, "acct_id:%ld, user_no:%s, user_type:%s, user_nm:%s, user_addr:%s\n", 
       pstUser[i].acct_id, pstUser[i].user_no, pstUser[i].user_type, pstUser[i].user_nm, 
       pstUser[i].user_addr);
}
TFree(pstUser);

操作表(新增记录)

dbUser  stUser;
SATvm   *pstSavm = (SATvm *)pGetSATvm();

//    初始化TBL_USER_INFO表,每张表都需要初始化一次, 对于表重建后,需要重新初始化一次。                
if(RC_SUCC != lInitSATvm(pstSavm, TBL_USER_INFO))
{
    fprintf(stderr, "init failed, err:(%d)(%s)\n", pstSavm->m_lErrno, sGetTError(pstSavm->m_lErrno));
    return ;
}

conditinit(pstSavm, stUser, TBL_USER_INFO);                    // 绑定变量

#if   0   // 效率高
stUser.acct_id = 10021;                                        // 对结构体赋值
strcpy(stUser.user_no, "20180223");                            // 对结构体赋值
strcpy(stUser.user_type, "1");                                 // 对结构体赋值

#else
conditnum(pstSavm, stUser, acct_id, 10021);                    // 对结构体赋值
conditstr(pstSavm, stUser, user_type, "1");                    // 对结构体赋值
conditstr(pstSavm, stUser, user_no, "20180223");               // 对结构体赋值
#endif

if(RC_SUCC != lInsert(pstSavm))      // 插入记录  
{
    fprintf(stderr, "Select错误ep:%d, err:(%d)(%s)\n", pstSavm->m_lEType, pstSavm->m_lErrno,
        sGetTError(pstSavm->m_lErrno));
    return ;
}

操作表(删除记录)

dbUser  stUser;
SATvm   *pstSavm = (SATvm *)pGetSATvm();

//  初始化TBL_USER_INFO表,每张表都需要初始化一次, 对于表重建后,需要重新初始化一次。                
if(RC_SUCC != lInitSATvm(pstSavm, TBL_USER_INFO))
{
    fprintf(stderr, "init failed, err:(%d)(%s)\n", pstSavm->m_lErrno, sGetTError(pstSavm->m_lErrno));
    return ;
}

conditinit(pstSavm, stUser, TBL_USER_INFO)                     // 对结构体赋值
conditstr(pstSavm, stUser, user_type, "1");                    // 查询条件赋值
conditstr(pstSavm, stUser, user_no, "20180223");               // 查询条件赋值

if(RC_SUCC != lDelete(pstSavm))      // 删除记录  
{
    fprintf(stderr, "Delete err:(%d)(%s)\n", pstSavm->m_lEType, pstSavm->m_lErrno,
        sGetTError(pstSavm->m_lErrno));
    return ;
}

操作表(修改记录)

dbUser  stUser, stUpd;
SATvm       *pstSavm = (SATvm *)pGetSATvm();

if(RC_SUCC != lInitSATvm(pstSavm, TBL_USER_INFO))
{
    fprintf(stderr, "init failed, err:(%d)(%s)\n", pstSavm->m_lErrno, sGetTError(pstSavm->m_lErrno));
    return ;
}
 
updateinit(pstSavm, stUpd);    
conditinit(pstSavm, stUser, TBL_USER_INFO)                        // 绑定变量
conditstr(pstSavm, stUser, user_type, "1");                       // 查询条件赋值
conditstr(pstSavm, stUser, user_no, "20180223");                  // 查询条件赋值

updatestr(pstSavm, stUpd, user_addr, "china");

if(RC_SUCC != lUpdate(pstSavm, (void *)&stUpd))
{
    fprintf(stderr, "Update err:(%d)(%s), (%ld)\n", pstSavm->m_lErrno,
        sGetTError(pstSavm->m_lErrno), lGetEffect());
    return ;
}

其他函数lTruncate、lCount、lExtreme、lGroup,lDropTable、lRenameTable 和网络API如lTvmDelete、lTvmInsert、lTvmSelect、lTvmUpdate、lTvmTruncate、lTvmCount、lTvmExtreme、lTvmGroup、lTvmQuery等,用法相似,不做一一枚举。[1] 

版本发布

2018年03月01日,首次开源

2018年06月01日,STVM 1.2.4 。 https://github.com/DeffPuzzL/STVM

转载于:https://my.oschina.net/deffpuzzl/blog/1627626

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值