基于IRIS列存储,我们能做什么

简介

列存储是一种数据存储方式,与传统的行存储数据库(如关系数据库)不同。在列存储中,数据被存储在列中,而不是行中。

这种存储方式的主要优点在于,它可以更有效地存储和查询结构化数据,特别是需要执行大量聚合查询的数据。列存储数据库通常具有高性能,可以快速处理大量数据,并且适用于分析型数据仓库,时序数据,设备数据等。

列存储数据库通常使用NoSQLNot Only SQL)数据库技术,并且不需要严格的数据模式。因此,它们更加灵活,可以快速适应数据变化,并且适用于处理大量数据。

使用场景

  • 提高查询性能:列存储将同一列的数据存储在一起,减少了需要读取的磁盘块数量,因此在查询时可以更快地检索需要的数据,特别是当查询需要检索一部分列时,列存储可以提供更好的查询性能。
  • 提高压缩比率:列存储可以使用列级别的压缩算法,由于同一列的数据通常具有相似性质,因此可以使用更高效的压缩算法,从而减少存储空间。
  • 支持大规模数据分析:列存储是大规模数据分析的关键技术之一,因为它可以对大量的数据进行快速的分析和计算。通过将同一列的数据存储在一起,可以提供更高效的数据分析和聚合功能。

如何使用列存储

定义列存储需要在持久类中添加参数 STORAGEDEFAULT = "columnar并上类关键字Final

定义User.Person表如下:

Class User.Person Extends %Persistent [ DdlAllowed, Final ]
{

Parameter STORAGEDEFAULT = "columnar";

Property Name As %String [ SqlColumnNumber = 2 ];

Property Number As %Integer [ SqlColumnNumber = 3 ];

Property Price As %Integer [ SqlColumnNumber = 4 ];

Property Amout As %Integer [ SqlColumnNumber = 5 ];


}

编译该类会产生如下所示的存储定义:

Storage Default
{
<Data name="_CDM_Amout">
<Attribute>Amout</Attribute>
<Structure>vector</Structure>
</Data>
<Data name="_CDM_Name">
<Attribute>Name</Attribute>
<Structure>vector</Structure>
</Data>
<Data name="_CDM_Number">
<Attribute>Number</Attribute>
<Structure>vector</Structure>
</Data>
<Data name="_CDM_Price">
<Attribute>Price</Attribute>
<Structure>vector</Structure>
</Data>
<DataLocation>^User.PersonD</DataLocation>
<IdLocation>^User.PersonD</IdLocation>
<IndexLocation>^User.PersonI</IndexLocation>
<StreamLocation>^User.PersonS</StreamLocation>
<Type>%Storage.Persistent</Type>
}

此存储定义看起来与为标准类定义创建的定义略有不同,后者使用传统的基于行的存储布局。虽然Global名称相同,但每个使用列式存储的属性都有一个 <Data> 元素。 (CDM代表列数据映射。) <Data> 标签的名称属性用作 ^User.PersonI Global中的下标,<Attribute> 元素包含属性名称,<Structure> 元素带有值向量表示向量用于以列形式表示属性的值。

User.Person表添加数据,代码如下:

ClassMethod Save(num)
{
 	ts
 	
	for i = 1 : 1 : num {
		
		s obj = ##class(User.Person).%New()
		s obj.Name = "yx" _ $random(100)
		s obj.Number = $random(100)
		s obj.Price = $random(100) 
		s obj.Amout = obj.Number * obj.Price
		s sc = obj.%Save()
		ret:($$$ISERR(sc)) $system.Status.GetErrorText(sc)
		
	}
	
	b
	tc
	
	q $$$OK
}

列存储持久类中每个属性的数据存储在列中,数据并不存储在行中。所以^User.GlobalD中仅包含ID,不包含数据。

USER>w ##class(M.ColumnStorage).Save(1)
1
USER>w ##class(M.ColumnStorage).Save(1)
1
USER>w ##class(M.ColumnStorage).Save(1)
1
USER>zw ^User.PersonD
^User.PersonD=3
^User.PersonD(1)=""
^User.PersonD(2)=""
^User.PersonD(3)=""

数据保存在^User.PersonI索引中,以Json格式保存数据。

  • type - 字段类型。
  • count - 数据数量。
  • vector- 数据值。
  • length - 向量长度。

USER>zw ^User.PersonI
^User.PersonI("$Person",1)=$zwc(412,1,0)/*$bit(2..4)*/
^User.PersonI("_CDM_Amout",1)={"type":"integer", "count":3, "length":4, "vector":[,7533,42,3496]}  ; <VECTOR>
^User.PersonI("_CDM_Name",1)={"type":"string", "count":3, "length":4, "vector":[,"yx80","yx49","yx55"]}  ; <VECTOR>
^User.PersonI("_CDM_Number",1)={"type":"integer", "count":3, "length":4, "vector":[,81,6,76]}  ; <VECTOR>
^User.PersonI("_CDM_Price",1)={"type":"integer", "count":3, "length":4, "vector":[,93,7,46]}  ; <VECTOR>

这里我们注意索引第二个节点,代表当前索引存储数据的序号,当数据length超过64,000时,下标会自动加一。

注:count计数低于length,因为该列包含 NULL 值。

添加10W条数据。

USER>w ##class(M.ColumnStorage).Save(100000)
1

基于`IRIS`列存储,我们能做什么-LMLPHP

什么情况下使用列储存

创建User.People表字段与User.Person表相同,但User.People表为行存储,User.Person表为列储存。

Class User.People Extends %Persistent
{

Property Name As %String [ SqlColumnNumber = 2 ];

Property Number As %Integer [ SqlColumnNumber = 3 ];

Property Price As %Integer [ SqlColumnNumber = 4 ];

Property Amout As %Integer [ SqlColumnNumber = 5 ];
}

分别给User.People表与User.Person表添加1000W条数据。

如下示例计算的数据量均为1000W条数据。

统计数据数量count

统计User.People表,数据数量,如图执行时间为1.6561秒。引用Global数量11100322个。

基于`IRIS`列存储,我们能做什么-LMLPHP

统计User.Person表,数据数量,如图执行时间为0.0026秒。引用Global数量496个。

基于`IRIS`列存储,我们能做什么-LMLPHP

如上可观察到列储存User.Person聚合count效率是行储存User.People636倍。

计算字段平均值avg

计算User.People表,Amount字段平均值,如图执行时间为2.3046秒。引用Global数量11101379个。

基于`IRIS`列存储,我们能做什么-LMLPHP

计算User.Person表,Amount字段平均值,如图执行时间为0.0179秒。引用Global数量2447个。

基于`IRIS`列存储,我们能做什么-LMLPHP

如上可观察到列储存User.Person聚合avg效率是行储存User.People128倍。

计算字段和sum

计算User.People表,Amount字段所有数据之和,如图执行时间为2.2902秒。引用Global数量88801366个。

基于`IRIS`列存储,我们能做什么-LMLPHP

计算User.Person表,Amount字段数据之和,如图执行时间为0.0075秒。引用Global数量1922个。

基于`IRIS`列存储,我们能做什么-LMLPHP

如上可观察到列储存User.Person聚合sum效率是行储存User.People305倍。

列存储与行存储区别

基于`IRIS`列存储,我们能做什么-LMLPHP

总结

  • 选择正确的存储方式可以将查询性能提高一个数量级。

以上是个人对基于列存储的一些理解,由于个人能力有限,欢迎大家提出意见,共同交流。

05-09 04:20