在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称:AvenirSQL开源软件地址:https://gitee.com/onlyyyy/AvenirSQL开源软件介绍:AvenirSQL介绍用Node.js设计一个数据库,支持常见的SQL语句 安装使用
更新配置需要重启数据库
建议使用高版本Node.js(v14+),通过版本管理工具n进行更新: 目前n只支持Mac和Linux npm i n -gn lts//下载最新版Nodejs 技术特点
代码结构./database/ : AvenirSQL核心实现 ./AvenirSQL.js : AvenirSQL启动程序 ./curl.js : cli程序 ./dtest.js : 测试程序 ./jmeter/ : Jmeter测试用例文件 ./db/ : 数据库和表数据 ./run.ini : 数据库配置文件 ./AvenirSQL/ : Nodejs版的AvenirSQL操作库 以下是项目中使用的Avenir开源组织开发的Node模块:
支持的数据类型
数字,默认长度10
其实也是数字 并不是整数,默认长度10
不限制长度的数字
字符型 默认长度20
字符型 默认长度20
不限制长度的字符 具体技术实现
async response(type, client) { let res = null; if (typeof type == 'string') { res = getError(type); if (!res) { res = unknown; } } else { let code = type.code; let data = type.data; res = getError(code); if (!res) { res = unknown; } res.data = data; } client.write(JSON.stringify(res)); //如果没有配置默认短连接 if( ini.db.keepAlive != true) { toLog("主动踢掉客户端的连接"); client.end(); } } 2.数据库结构 数据库:文件夹名 表:数据文件、哈希索引文件、B+树索引文件(聚合索引)
3.连接管理 为了区分不同的用户对数据库进行的不同操作,如同一秒内多个进程进行多次请求,AvenirSQL会生成一个签名,用户登录后需使用此签名进行操作。 4.串行锁 进行操作前加锁,操作完成后解锁,并刷新缓存(select语句不会刷新缓存) //自动释放锁防止数据库死锁 async releaseLock() { let now = moment().valueOf(); let releaseLockTime = ini.db.releaseLockTime; releaseLockTime = releaseLockTime > ini.db.checkLockTime ? releaseLockTime : ini.db.checkLockTime; for(let key in this.table) { let tables = this.table[key]; for(let subKey in tables) { let times = tables[subKey]; if(moment(now).diff(moment(times),'seconds') > releaseLockTime) { delete tables[subKey]; toLog("自动释放了锁 ",tables[subKey]); } } } } 5.缓存 目前共五类缓存,数据库配置文件缓存和表结构缓存不会刷新,哈希索引、表数据、B+树索引缓存会定时刷新。 6.解析SQL 在此感谢阿里巴巴的sql解析器 node-sqlparser AvenirSQL独有的sql会先解析,除此之外的SQL会转交给node-sqlparser。 //包含原生SQL和能够被AvenirSQL识别的语句 async parse(sql, sign) { //先解析AvenirSQL特有的语句 再解析原生SQL toLog("要解析的 sql为 ", sql); let raw = this.getArray(sql); if (raw.length === 0 || !sql) { throw ('SQL_PARSE_ERROR'); } else { //AvenirSQL解析出错不报错,转给解析器解析,解析器报错直接throw try { await this.parseAvenirSql(raw, sql, sign); } catch (error) { //不是内部定义的错误就代表程序处理出错了 toLog('error = ', error); if (error == SUCCESS || error != 'error') { throw (error); } //不需要try catch了,底层会抓住错误 let par = this.parseSql(sql); await this.doSql(par, sign); } } } 7.事务 事务操作在缓存中临时处理,使用串行锁避免并发,不会出现重复读,读未提交等。 rollback操作直接清除缓存,commit操作提交 写文件,释放锁,同样会释放缓存。 超时自动释放锁 避免出现数据库死锁的情况。 8.日志 暂时只支持记录类日志,未实现undolog,binlog等日志,通过Trace类,avenir-log模块实现。 9.cli程序 提供一个与AvenirSQL交互的工具,实现自动重连功能,当发现报错为code 2 签名失效则直接重连。 //检查返回值,超时就自动重发async function checkError(response) { if (response && response.code == 2) { //返回值是2代表签名失效 重新登录即可 let data = await safeConnect(); if (data.code === 0) { data = data.data; } else { //说明重新登录也报错了 数据库故障 return { code: -1, data: null, } } return { code: 1, data }; } else if (response.code == 0) { return { code: 0, data: null }; } else { return { code: -1, data: null, }; //其他错误 其实返回这个没啥用 只需要判断1 就行 }}
在返回了结果集之后,根据distinct参数来对数据去重,通过依次插入到多叉树中,一旦发现插入失败则表示数据重复了。 //处理distinct的函数 20210222 考虑用多叉树来处理 async doDistinct(data, columns, tableDetail) { let mulTree = new MultipleTree(); if (columns.length == 1 && columns[0].expr.column == tableDetail.key) { //如果是主键的话本身就是distinct了 toLog("优化器跳过distinct"); return data; } //遍历结果集 剔除重复的列 for (let i = 0; i < data.length; i++) { let line = data[i]; if(mulTree.insert(line) === false) { data.splice(i,1); i--; } else { continue; } } return data; }
导出即执行select操作,并将SQL语句转换为insert语句。 导入则按分隔符执行SQL语句 导出文件名规范:dump[table || database]_dbname_tableName_YYYYMMDDHHmmss.sql 接口规范数据库连接使用tcp通信,传输文本为JSON数据格式。
{ user:"root", password:"123456" type:"login"} 返回值: { code:0, message:"success", data:"ef3d843f26c4e900e9ab4979f324d5571a4cb5f5c011278b36985b2802c828185ad0bdd7e19390cfffe479afe1b09d1c"}
{ sign:"ef3d843f26c4e900e9ab4979f324d5571a4cb5f5c011278b36985b2802c828185ad0bdd7e19390cfffe479afe1b09d1c", type:"sql", data:"select * from test"} 返回值: { code : 0, message:"success", data:[{ name:"test", id:"1", }]}
{ sign:"ef3d843f26c4e900e9ab4979f324d5571a4cb5f5c011278b36985b2802c828185ad0bdd7e19390cfffe479afe1b09d1c", id:"transID" //事务执行流程: begin->sql->commit,begin的时候会给id 后续用id来执行sql type:"trans", data:"delete from test",} 返回值: { code : 0, message:"success",} 行数据分隔符分隔符为∫,故所有的列数据不可以含有∫符号,否则会报错 错误代码表{ //不该发生的错误 UNKNOWN_CMD: { code: -1, message: 'unknown command', }, SYSTEM_BUSY: { code: -2, message: 'system busy', }, BAD_REQUEST: { code: -3, message: 'bad request', //请求格式无法JSON序列化 }, UNKNOWN_ERROR: { code: -100, message: 'unknown error' }, //成功 AVENIR_SUCCESS: { code: 0, message: 'success' }, //程序级别错误 SQL_PARSE_ERROR: { code: 1, message: 'sql parse error' }, NOT_CONNECTED: { code: 2, message: "no connect info, please login first" }, //大意了没有3 4不吉利 年轻人不讲5的 FILE_NOT_EXIST: { code: 6, message: 'file not exist' }, GEN_SIGN_ERROR: { code: 7, message: 'generate sign error', }, SET_SIGN_EXIT: { code: 8, message: 'sign exit', }, LACK_OF_SIGN: { code: 9, message: 'lack of sign, please login first', }, INVALID_NAME: { code: 10, message: "invalid name", }, BAD_USER: { code: 11, message: 'user or password error', }, PERMISSION_DENIED: { code: 12, message: 'permission denied', }, //以下是数据库类错误 DATABASE_NOT_FOUND: { code: 1001, message: 'database not found' }, DATABASE_EXIST: { code: 1002, message: 'database already exist', }, TABLE_NOT_FOUND: { code: 1003, message: 'table not found' }, TABLE_EXIST: { code: 1004, message: 'table already exist' }, INVALID_SQL_ERROR: { code: 1005, message: "invalid sql or sql parse Error" }, TOO_MANY_COLUMNS: { code: 1006, message: 'too many columns', }, COLUMN_NOT_FOUND: { code: 1007, message: 'column not found', }, COLUMN_NOT_MATCH: { code: 1008, message: 'columns and values not match', }, COLUMN_REPEAT: { code: 1009, message: 'columns repeat', }, COLUMN_NOT_NULL: { code: 1010, message: 'some columns cant be null', }, COLUMN_NOT_CHECK: { code: 1011, message: 'column not check error', }, ONLY_ONE_KEY: { code: 1012, message: 'AvenirSQL only support one key', }, LACK_OF_PRIMARY_KEY: { code: 1013, message: 'lack of primary key', }, KEY_EXIST: { code: 1014, message: 'duplicate primary key value', }, OPER_NO_ROW: { code: 1015, message: 'the operated row not found, may be not a error' }, VALUE_NOT_NUMBER: { code: 1016, message: 'compared value is not a number', }, SQL_TOO_LONG: { code: 1017, message: 'sql is too long', }, SQL_NOT_SUPPORT: { code: 1018, message: 'AvenirSQL dont support this sql yet', }, GET_LOCK_FAILED: { code: 1019, message: 'AvenirSQL get lock timeout', }, RELEASE_LOCK_FAILED: { code: 1020, message: 'AvenirSQL release lock failed', }, USER_EXISTS: { code: 1021, message: 'user already exists', }, USER_NOT_FOUND: { code: 1022, message: 'bad user or password', }, //事务类错误 TRANS_NOT_FOUND: { code: 1023, message: 'invalid trans id', }, TRANS_TIME_OUT: { code: 1024, message: 'trans timeout', }, NO_GROUP_DIS: { code: 1025, message: 'AvenirSQL dont support group by yet' }, COUNT_NO_TABLE: { code: 1026, message: `count(colums) cant be [table.column]` }, WHITE_SPACE_ERROR: { code: 1027, message: `sql cant contain AvenirSQL's separator:${this.WHITE_SPACE}`, }, NOT_SUPPORT_DATA: { code: 1028, message: 'AvenirSQL dont support such data type', }, COLUMN_TYPE_ERROR: { code: 1029, message: 'table columns type error(number or string)', }, COLUMN_OUT_OF_LENGTH: { code: 1030, message: 'columns over the length', }, DUMP_TABLENAME_ERROR: { code: 1031, message: 'dumped table name error' }, DUMP_SQL_ERROR: { code: 1032, message: "dump sql error" },} SQL规范
{"code":0,"message":"success","data":["home_def","hot","t"]}
不支持不带where条件的全表更新 如
distinc a,b,c 即代表数据库里面a,b,c三个列都重复才会判断重复。 通过多叉树模块multiple-tree来实现
dump table dbname.tableName as select * from tableName where a > 10000 如果不带SQL语句,则导出全表,在配置文件配置是否是覆盖导出和导入 配置文件示例文件名必须是run.ini,在安装目录下 若找不到此文件,AvenirSQL会自动创建默认的配置文件,内容如下:
[main]ip=127.0.0.1port=44944#数据库的工作目录path=./db#是否输出日志至显示屏 频繁I/O操作将影响性能ifConsoleLog=true[db]#maxConnect=100 目前存在问题#签名的有效期(秒)signValidTime=100#事务自动回滚时间(秒)rollbackTime=10#是否维持长连接keepAlive=false#是否记录debug类的日志debug=false#每个连接的超时时间 毫秒timeOut=10000#数据库表列的上限maxColoums=100#数据库SQL的最大长度maxSqlLength=200#默认的用户数据库user=User#缓存失效的时间(秒)cacheInvalid=200#检查缓存失效的频率(秒)clearCache=500#upda |
请发表评论