RJSON数据库系统 —— 一款高性能混合模型数据库的设计与实践

举报
yd_281241722 发表于 2025/08/20 14:32:44 2025/08/20
【摘要】 RJSON数据库系统

RJSON数据库系统

 

RJSON数据库系统(以下简称RJSON)是基于RJSON内存数据库构建的关系型数据库系统,基于C#开发,可运行于WindowsLinux(Unix)平台。该数据库系统支持高并发,具有高易用性,是结合了非关系型数据库一些特点设计的关系型数据库,以文档的形式存储数据,可用于基于互联网的数据库平台。RJSON支持事务,一般情况下只使用行锁(只有整理表文件比如收缩表使用表锁)RJSON在读取记录时支持两种模式:提交读和脏读。提交读是指当记录处于其它连接的事务中被修改或删除状态时,不能读取,此时读取将等待那个连接的事务提交或回滚后,这也是默认的读取模式;而脏读则是不管记录状态如何,都能读取,读取不会等待事务提交或回滚,所以读取的数据可能是记录修改之前的数据,也可能是已修改但尚未提交的数据。由于不受锁的限制,脏读具有更快的读取速度。与其它数据库不同的是,RJSON在事务中新增的记录,在事务提交前,只有本连接才能读取,其它连接不能读取这部分记录,也不会阻止其它连接读取已提交的记录,通过扩展索引来维持插入数据主键和唯一索引的一致性(不同连接事务插入的记录主键和唯一索引不能重复)RJSON提供了事务的灾难性恢复,在执行事务提交时会提前将事务写入日志,若在执行事务写入的过程中出现了宕机现象,则可以在重启服务时自动提交未执行完的事务来维持数据的一致性。

RJSON数据库系统支持集群和节点实时备份(针对每个集群服务器节点的备份,建议13),最多支持256个主节点集群服务器(不包含备份服务器),以集群ID(每个主节点服务器会分配一个0255之间的一个独立的ID,备份服务器的集群ID与主节点相同),只需通过设置集群服务器各个节点的访问地址及密码,即可实现整个集群的一体化访问。从任何一个服务器接收到的用户对数据库的访问,均会在整个集群服务器上并行执行(性能取决于最慢的服务器)。如果是查询数据,则会将所有集群服务器查询的结果集合并输送给用户;如果是插入数据,则会将插入的数据保存在用户访问的服务器上;如果是更新或删除数据,则会直接在所有集群服务器上同步执行,并支持分布式事务以维持所有节点修改和删除数据的一致性。RJSON数据库支持实时备份,可以为一个节点设置备份服务器,此时对这个节点的数据修改会精准地以异步的方式同步到备份服务器上(对于数据备份属于最终一致性,但对于分布式事务属于强制一致性),如果这个节点服务器故障,则可自动启用备份服务器替代故障服务器,而这样的功能只需要一个简单的配置,即可实现。RJSON集群既是一个整体同时各个节点又是相对独立的部分。对于表主键和唯一索引,节点只维护自己的数据不能重复,而不维护其在整个集群范围内的唯一性。所以集群用户在设置表的主键时,一定要结合这个节点的特点,给出使主键在整个集群唯一的方案。RJSON提供了雪花ID(Int64类型)来生成整个集群唯一值的方案。雪花ID类似于传统的自增ID,由服务器自动产生而不需要用户维护,与自增ID不同的是:系统每次生成的雪花ID在整个集群服务器内都是唯一的,这种唯一性可维持1115年。用户可以使用雪花ID来生成表的主键值,也可以使用 select cluster_id()来获取当前集群ID值,用这个值再结合当前节点主键值生成一个在集群内唯一的主键。

RJSONJSON文本格式来存储数据。类似于MySqlRJSON的数据库是一个文件夹,所有表及其相关文件均保存在此文件夹下。一张表的数据分多个文件保存,文件名均为表名,类型分别是表结构文件(.str文本文件)、数据文件(文本文件,没有扩展名或扩展名为12…,因为当数据文件大于1GB时,RJSON会在适当的时候将数据文件分割成多个1GB左右的数据文件)、表链接文件(文本文件,扩展名为lin+数据文件的扩展名,与数据文件形成一一对应关系)、记录索引文件(.pos二进制文件)和字段索引文件(扩展名由.i开始的二进制文件,后面接一个由0开始递增的数字。字段索引文件与数据文件形成一一对应关系)和删除索引文件(.del.del_二进制文件)RJSON的数据文件、表链接文件和表结构文件均为文本文件,基本上不会被损坏,即使有损坏也便于修复。其它文件均属于二进制文件,如果出现问题,均可以在删除后重启数据库服务时自动修复,所以RJSON更易于维护。RJSON的数据文件还可以选择不压缩和压缩两种格式,不压缩格式的数据文件在使用文本编辑器打开后会直接展现JSON格式的数据,而压缩格式的数据文件在使用文本编辑器打开后呈现的是Base64字符串,用户看不出数据的实际内容。这两种格式各有利弊。不压缩格式的优势是数据内容透明,具有更快的读取速度,一条记录的数据轻微损坏也能读取部分字段的值,并且后期手工压缩数据文件备份时具有极高的压缩比,能节省大量的磁盘空间,劣势是数据文件较大;而压缩格式的优势是数据文件较小,数据内容不透明(如果用户希望数据是这样的),劣势是读取速度相对较慢,如果部分内容损坏会导致记录解压失败,丢失整条(或多条)记录,并且后期压缩备份的话压缩比会比不压缩格式的低很多。系统默认不压缩格式。可使用 set compress 命令来切换这两种格式,此命令后面有说明。这两种格式后面还可延伸出更多种格式,比如数据加密。如果用户需要将数据加密,则可以将加密后的数据用base64文本保存,类似于压缩格式,也可以将数据压缩后再加密等等。

RJSON具有复杂的层次结构。一个数据库文件夹下可以存在子数据库文件夹,子文件夹中还可以套子文件夹,理论上文件夹的层数仅受操作系统限制,不同文件夹下的表名可以相同,RJSON可以对所有这些文件夹里的表文件作出统一的管理。在访问这些子文件夹数据库时,只需要在表名前面加上限定的文件夹名称,用.分隔即可,这类似于MySql数据库同时管理多个数据库时,表名称前面要加限定的数据库名称一样。比如这句Sql语句:select a.item_id,a.item_nm,a.unit,b.qty,b.netrate from zl.u_item a join ls.ls1.u_billitem b on a.item_id=b.item_id where a.item_id like ‘20%’ order by a.item_id,表示的是:在主数据库文件夹下有两个子数据库文件夹zllszl数据库中有表u_itemls数据库中又有一个子数据库ls1,这个子数据库中有表u_billitem,然后将这两张表用相关条件连接起来,从这两张表中筛选数据。

RJSON32位版本和64位版本。32位版本理论上单表支持的最大记录数为40亿条,单表支持的数据文件最大能到16TB(仅文本数据文件,不包括其它二进制文件)64位版本理论上单表支持的最大记录数超过2814749亿条,目前单表支持的数据文件大小能到128 PB(131072TB)。这两个版本单条记录最大支持4GB数据,设计思路完全一样。32位版本用8个字节保存记录索引,4个字节保存记录序号,而64位版本用10个字节保存记录索引,6个字节保存记录序号,所以64位版本的记录索引、字段索引及删除索引文件均比32位版本的要大。但64位版本单表支持海量数据,这是32位版本无法相比的。如果用户单表没有那么大,或者大的表考虑分表储存的话,则32位版本已经足够满足用户需要,且需要的存储空间更小。这两个版本的区别只在于单表储存的记录数不同,对于整个数据库的大小都没有限制,可以无限增长。以上单表支持的记录数只限于单服务器,对于集群服务器,则单表支持的记录数会成倍增长(集群中有几个主节点服务器,就是几倍)

RJSON具有非关系型数据库的一些特点。RJSON将一张超大表的数据文档分割成多个1GB大小的小文档,所以很容易实现数据的水平扩展。随着企业数据量的增加,只需要增加相应的储存节点即可。RJSON支持跨磁盘创建表,也支持将已经存在的表及其数据移动到另一个磁盘中,还支持将同一张表的数据分布到不同的磁盘上,只要用户硬件的储存空间够大,它可以实现数据库数据的无限制增长。RJSON可以随意地添加或修改、删除字段,且这些操作与数据文档无关,速度极快,不像其它关系型数据库增加、修改或删除字段时同时要修改数据文件,当表很大时操作会变得异常艰难。所以在数据的存储和表结构的修改上,更像非关系型数据库,能适应企业数据结构的不断变化和数据的快速增长。

RJSON的表字段数据类型是JSON数据类型,即:字符串(String),整数(Int32Int64),小数(DoubleDecimal)、布尔(Bool),但RJSON还增加了DataTime(日期时间类型,两端用#将一个日期格式的文本围起来)TimeSpan类型(两端用#将一个TimeSpan格式的文本围起来,但TimeSpan格式的文本最后以字母N结尾),而对于二进制数据,则直接使用Base64文本来储存。对RJSON的一些特定的数据类型还有更精准的类型限制,具体是:String:通用字符串值;String(list):字符串列表,值只能为列表中的某一项;String(checked):字符串复选,值只能为列表中的某一项或某几项,多项用半角逗号分隔;Int32:32位整型,Int32(list):Int32类型的列表,只能取列表中某项的值;DateTime:不带时区的日期格式,DateTime(date):只保存日期的日期格式;DateTime(offset):带时区的日期格式;Int64:64位整型;Int64(snow):雪花ID,此类型的值一旦生成,不能修改,在整个集群服务器内唯一。以上所有数据类型均不用设置长度,单条记录最大支持的字节长度可达4GB,足以容纳任意类型的数据。

RJSON 支持标准的SQL查询(包括子查询)。结合了SqlServerMySql的查询语法,SqlServerMySql的查询语句大部分不用修改直接可以在RJSON中运行。比如MySql支持的limitSqlServer支持的topRJSON都支持。但对于表的join查询,RJSON支持inner join(可以只使用join)left join,不支持right join。对于group by语句,RJSONSqlServerMySql略有不同。RJSON对于group by查询的字段列表中非聚合函数的字段,应出现在group by后面,而不是字段列表中。比如:select sum(qty) qty,sum(netrate) netrate from u_billitem group by item_id,item_nm,unit order by item_id,这条语句在RJSONSqlServerMySql中都能执行,但RJSON中的结果会将item_id,item_nm,unit,qty,netrate都列出来,而SqlServerMySql中只会列出qty,netrate。如果要都列出来,SqlServerMySql必须这样写:select item_id,item_nm,unit,sum(qty) qty,sum(netrate) netrate from u_billitem group by item_id,item_nm,unit order by item_idRJSON还可以这样写:select item_id,item_nm,unit,sum(qty) qty,sum(netrate) netrate from u_billitem order by item_id,也就是说RJSON可以没有group by语句,如果字段列表中同时出现了聚合函数和非聚合的字段或表达式,则会将非聚合的字段或表达式自动转换成group by的形式执行。为了保持兼容,SqlServerMySql的写法在RJSON中也能执行,只是会忽略group by后面的字段列表,而将前面的非聚合字段转换成group by后面的字段。RJSON也支持having关键字,但RJSON having 关键字与SqlServer不同的是它可以使用在任何 Select 语句的where关键字之后作为对前面的查询结果集的二次筛选,所以having关键字后面的条件字段必须是前面的查询结果集中的字段,比如:

select item_id,item_nm,unit,sum(rate) sumrate from u_item where item_id between '0101001' and '4000000' having sumrate>100having关键字在一条Sql查询语句中只能使用一次。

RJSON支持union,可以将多个查询结果集合并成一个。但RJSONunion相当于SqlServerunion all,他不会自动去除重复的记录。同时也保留了union all关键字,使用方法与SqlServerMySql相同。

RJSON除了主键和唯一索引外,一般索引不需要用户人为设置,是因为RJSON的表索引会在需要时自行创建,这是RJSON在数据查询方面的方便之处,不需要用户人为设置索引,又能保证数据查询始终高效。RJSON自行创建的索引均为单字段索引,如果用户要对多个字段使用and操作查询,则RJSON会直接创建其中每个字段的索引(RJSON内存数据库则会直接创建多字段复合索引),在查询过程中使用多个索引联合查询(姑且把这种索引称之为柱状索引),并会根据索引的实际情况自动优化索引条件的查询顺序,用户不用担心条件顺序对查询性能的影响,可以随心所欲地写高性能的Sql,而不用考虑是否要组织查询条件使查询更高效。虽然索引会影响数据库增删改的性能,但RJSON索引的设计原理使得这种影响并不明显(如果每个字段都有索引,在插入记录时相当于把整条记录写入两遍,删除记录对性能没有影响,修改记录时索引对性能的影响小于增加记录)RJSON的索引使用的是字典,能快速精准地定位到表记录在数据文件中的位置,且对任何查询表达式(包括函数、计算列),均使用索引。RJSON的索引不仅仅是索引,还是浓缩的数据,当RJSON使用聚合函数进行统计时,实际上就是从索引中提取数据进行计算的,所以极为高效,性能远胜SqlServer的同类查询(对一张记录数为两百多万条的流水表使用聚合函数进行统计,测试性能为SqlServer50倍左右,且内存的使用仅为SqlServer1/8)。与MySql比较,在系统内存充足的条件下,RJSON使用聚合函数统计一张超大表的所有数据(全盘扫描)所占用的内存要远小于MySql,在系统初次统计时所使用的时长也是远小于MySql,在记录数不是特别大(500万条左右)的情况下,即使MySql将所有记录加载到内存,直接使用缓存统计,RJSON仍比其快1倍以上,且用到的内存远小于Msql,且这种情况随着记录数的不断增加又会产生质的变化。通过对拥有4500多万条记录的流水库使用聚合函数统计其中某一年的所有数据,给MySql最大20GB的内存,经过多次测试平均单次花费18分钟左右,统计过程中把20GB的内存全部用完。而RJSON的平均单次花费时间为30秒左右,整个过程大概只用到不超过2GB的内存,效率是MySql30倍以上,而内存占用仅为其1/10。在数据库的访问过程中,RJSON对磁盘的读写量也是远低于SqlServerMySql,更有利于延长储存设备的寿命。RJON数据库使用到了二级索引,其中一级索引可理解为索引的索引,要加载到内存中(一级索引特别精简,占用内存极低),二级索引为数据文件对应的索引,不用全部加载到内存。二级索引文件和数据文件相对应,在查询时只需加载查询条件所涉及的部分索引,并且在超出设置内存(可在配置文件中设置RJSON使用的最大系统内存)的情况下会自动卸载长时间不使用的索引,所以更能合理有效地利用内存。数据库单表的数据量越大,记录数越多,相比SqlServerMySql越能体现出RJSON在数据查询及更新方面的性能优势。RJSON先进的索引设计理念使得其即使在海量数据的访问中,仍能保持相当的性能,且无需占用海量的内存,能轻松处理万亿级别的单表数据。可以把一级索引理解为页面(RJSON的一个页面为1GB左右)的索引,记录了这个页面数据的范围,而二级索引为这个页面中记录的索引,二级索引会精准记录这条记录在文件中的位置和长度。如果把单字段索引看作是按列存储数据,把数据文件看作按行存储数据的话,那RJSON是以行存储为主,列存储为辅的存储方式。行数据是基础数据,列数据由行数据生成,可重建,并为数据库提供高效便捷的查询服务。

使用insertdeleteupdate语句可以实现数据库的修改,这些语句的使用完全符合Sql标准。与其它数据库不同的是,RJSONinsert values语句支持where条件,比如:insert into u_item (item_id) values ('0101001') where not exists (select * from u_item where item_id='0101001')。这种写法有个好处:只有u_item表中不存在item_id0101001的记录时才插入,这样避免了主键重复时插入失败导致的程序报错。除此以外,RJSONinsert values语句的values部分的参数不仅支持常量,还支持表达式、函数和子查询。RJSONdeleteupdate语句均支持limit关键字。使用时将limit关键字放在语句的最后,与MySqlselect语句的limit用法相同。如果在某些特殊情况下某个表需要极快的插入速度而不是查询,则可以在创建此表时不设置主键和唯一索引(但可以设置自增列),这将会明显地提高插入速度(使用insert values语句,多条记录用逗号分隔,建议一条语句同时插入1万条记录)。如果要对此表进行查询,则可以创建子数据库,将表复制到子数据库下专供查询。

类似于SqlServerRJSON支持使用 select into 的方式创建一张表,比如:select item_id,item_nm,unit,rate into u_itemtemp from u_item 会将u_item表中所有记录的item_id,item_nm,unit,rate字段复制到一张新表u_itemtemp(这是一张实体表),可供所有客­户端访问。可以使用 drop table u_itemtemp 删除这张表。RJSON没有像SqlServer那样的临时表,SqlServer的临时表表名以#开始,存储在服务器上。RJSON支持真实的内存表,驻留在客户端的内存中供客户端独立使用,不占用服务器资源,并支持RJSON的所有Sql命令。严格地说这是一个驻留在客户端的内存数据库,没有实体表,使用方法和RJSON实体数据库没有差异,且内存数据库支持复合索引,拥有更快的修改和查询速度。

对于update语句中的联表操作,MySqlSqlServer语法并不兼容,但都符合Sql查询的语法规范。这两种语法RJSON都支持。

RJSON对记录增、删、改操作的性能排序为:删除>>增加>=修改。删除记录RJSON只是将记录做删除标记(并没有真正从数据文件中移除,不影响索引,除非使用收缩数据表功能),所以十分高效,性能远胜SqlServerMySql。增加记录RJSON要略快于SqlServerMySql。在修改记录的性能上,RJSONMySql相当,略逊于SqlServer在更新记录时,RJSON性能整体表现符合线性模式,更新记录时长与更新记录条数成正比,并且非常稳定。 RJSON的所有字段都支持null值,如果没有设置默认值,且在插入记录时也没有给这个字段赋值,则这个字段的默认值为null,那么这个字段将不会保存在存储设备上的相应记录中。所以一条记录中如果出现了大量值为null的字段时,可以节省磁盘存储空间。

RJSON支持反删除。由于RJSON的删除只是将记录做删除标记,并没有真正从数据表中移除,所以在没有执行收缩表功能的前提下,RJSON能实现反删除,将被删除的记录恢复。同时查询和更新命令也可以实现专门针对被删除记录的查询及更新。实现这些功能,只需在相应的命令前面加#(#select#update#delete#delete命令实现反删除),但这些命令必须是独立的,不能在一个事务中同时处理正常的Sql命令和针对删除记录的Sql命令。如果要彻底删除这些被做删除标记的记录,请使用收缩表功能(shrinktable)。对于select命令来说,除了可以使用#select查询被删除的记录外,还可以使用@select查询所有记录(包括所有未删除和删除的记录)

RJSON支持的计算运算符包括+()-()*()/()^(乘方)%(取模)。比较运算符有=(等于)>(大于)>=(大于等于)<(小于)<=(小于等于)<>(不等于,也可以使用!=)betweenisis notlikenot。对null值的比较,SqlServerMySql使用的是is,比如:is nullis not null,这两种写法RJSON都支持,同时RJSON也可以直接使用等于或不等于:=null <>nulllike运算符支持通配符%_%代表所有字符,_代表单个字符,若字符串中本身包含%_时,将这两个符号用中括号括起来,支持写法[单字符1-单字符2],代表单字符1至单字符2中的任意一个单字符,功能与SqlServerlike相同。

聚合函数sum(表达式)avg(表达式)count()min(表达式)max(表达式)的用法同标准Sql查询。除了这些常用的聚合函数外,RJSON还提供了一个字符串串联的聚合函数ssum(表达式,分隔符),使用此聚合函数可达到将分组的字符串以分隔符分隔进行串联的效果。其中分隔符参数可以是常量,也可以是表达式,这个参数是可选的,如果不传,则为空字符串。

RJSON除了iif(表达式,1,2)函数支持条件计算外,还提供了case when end结构可以更方便地实现条件计算。用法和SqlServer的一样,有两种用法:

(1)   case 表达式 when 1 then 表达式1 when 2 then 表达式2… else 表达式n end

(2)   case when 条件1 then 表达式1 when 条件2 then 表达式2… else 表达式n end

RJSON函数的参数部分根据需要会自动转换。比如一个参数为Int类型,而用户传入的是一个字符串或其他非Int类型的数字,则软件会将其自动转换成所需要的Int类型。RJSON中字符串常量用单引号括住(如果字符串中存在单引号,则用两个单引号表示),日期常量用#括住(比如#2022-1-1 12:0#表示一个日期常量,里面的字符串必须要能转换成日期,否则报错)

RJSON 在聚合函数及一些涉及到运算的表达式中若遇到的数字数据类型不同时会全部转换成位数大的数据类型进行运算,不会造成精度的损失(比如double decimal进行运算时,会将double转换成decimal之后进行运算)。如果表达式中某个值为null,则整个表达式结果为null。对于加法(+)运算,RJSON主体采用先入为主的原则:计算结果的数据类型取决于第一个表达式的数据类型,例如第一个表达式为字符串,则会将后面的表达式结果全部转换成字符串后进行串联,若第一个表达式为double类型,则将后面的表达式结果转换成double类型进行运算,如果数据转换失败,则抛异常。

RJSON支持使用变量。可使用set关键字设置变量,比如:set @a=last_insert_id() 表示将前面的操作产生的最后一个自增量的值存入变量@aset @a=(select item_id from u_item limit 1) 表示将u_item表中第一行的item_id存入变量@aset @a=item_id,@b=item_nm,@c=unit from u_item limit 1 则表示将u_item表中第一行的item_iditem_nmunit分别赋给变量@a@b@c。变量必须以@开始,可以在查询语句的任何地方使用。RJSON的变量均为私有变量,只能在当前连接中使用,不同连接的变量可以同名且互不干扰。正因为如此,所以RJSON在执行Sql操作时可以使用参数,参数的使用方式与SqlServer相同,正是借助于支持变量实现的。

RJSON支持字符串的内插表达式。比如:select $'ab{left('1234',3)}cd' as a,会输出ab123cd,字符串内插以$'开始,'结束,里面大括号内的为表达式运算,将运算结果和前后的字符串一起拼成一个新的字符串。字符串内插表达式支持嵌套。

 更详细的说明见附件。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。