mysql 中整数数组的最小存储?
smallest storage of integer array in mysql?
我有一个 table 的用户条目,对于每个条目,我都有一个(2 字节)整数数组来存储(15-25,偶尔甚至更多)。数组元素将同时写入和读取,永远不需要更新或单独访问它们。他们的顺序很重要。将其视为数组对象是有道理的。
我有数百万个这样的用户条目,我想用尽可能少的磁盘存储这些条目 space。然而,我正在为 MySQL 缺少 Array
数据类型而苦苦挣扎。
我一直在考虑以下选项。
- 按照 MySQL 的方式进行。使用列
user_id
、data_id
和 data_int
创建 table my_data
。为了提高效率,需要在 user_id
上建立索引,每个整数总计超过 10 个字节。
- 以文本格式存储数组。每个整数需要 ~6.5 个字节。
- 制作 35-40 列 ("enough") 并且 -32768 为 'empty'(因为该值不会出现在我的数据中)。每个整数需要 3.5-4 个字节,但有点难看(因为我必须对数组中的元素数量施加严格限制)。
MySQL有更好的方法吗?我知道 MySQL 有一个有效的 varchar
类型,所以理想情况下我将我的 2 字节整数存储为 varchar
中的 2 字节字符(或类似的方法 blob
), 但我不确定该怎么做。这可能吗?应该怎么做?
您可以将它们存储为单独的 SMALLINT NULL
列。
在 MyISAM 中,这对每个值使用 2 个字节的数据 + 1 位空指示符。
在 InnoDB 中,空指示符被编码到列的 字段起始偏移量 ,因此它们不会占用任何额外的 space,并且空值实际上不是存储在行数据中。如果行足够小以至于所有偏移量都是 1 字节,那么这将对每个现有值(1 字节偏移量,2 字节数据)使用 3 个字节,对每个不存在的值使用 1 个字节。
这两种方法都比使用带有特殊值的 INT
来表示它不存在要好,因为每个值都需要 4 个字节的数据。
见NULL in MySQL (Performance & Storage)
评论里给出了最佳答案,所以我会把它和一些可用的代码重新发布在这里,以供进一步参考。
MySQL 有一个 varbinary
类型非常适合这个:你可以简单地使用 PHP 的 pack
/unpack
函数来转换它们来回二进制形式,并使用varbinary
将该二进制形式存储在数据库中。转换的示例代码如下。
function pack24bit($n) { //input: 24-bit integer, output: binary string of length 3 bytes
$b3 = $n%256;
$b2 = $n/256;
$b1 = $b2/256;
$b2 = $b2%256;
return pack('CCC',$b1,$b2,$b3);
}
function unpack24bit($packed) { //input: binary string of 3 bytes long, output: 24-bit int
$arr = unpack('C3b',$packed);
return 256*(256*$arr['b1']+$arr['b2'])+$arr['b3'];
}
我有一个 table 的用户条目,对于每个条目,我都有一个(2 字节)整数数组来存储(15-25,偶尔甚至更多)。数组元素将同时写入和读取,永远不需要更新或单独访问它们。他们的顺序很重要。将其视为数组对象是有道理的。
我有数百万个这样的用户条目,我想用尽可能少的磁盘存储这些条目 space。然而,我正在为 MySQL 缺少 Array
数据类型而苦苦挣扎。
我一直在考虑以下选项。
- 按照 MySQL 的方式进行。使用列
user_id
、data_id
和data_int
创建 tablemy_data
。为了提高效率,需要在user_id
上建立索引,每个整数总计超过 10 个字节。 - 以文本格式存储数组。每个整数需要 ~6.5 个字节。
- 制作 35-40 列 ("enough") 并且 -32768 为 'empty'(因为该值不会出现在我的数据中)。每个整数需要 3.5-4 个字节,但有点难看(因为我必须对数组中的元素数量施加严格限制)。
MySQL有更好的方法吗?我知道 MySQL 有一个有效的 varchar
类型,所以理想情况下我将我的 2 字节整数存储为 varchar
中的 2 字节字符(或类似的方法 blob
), 但我不确定该怎么做。这可能吗?应该怎么做?
您可以将它们存储为单独的 SMALLINT NULL
列。
在 MyISAM 中,这对每个值使用 2 个字节的数据 + 1 位空指示符。
在 InnoDB 中,空指示符被编码到列的 字段起始偏移量 ,因此它们不会占用任何额外的 space,并且空值实际上不是存储在行数据中。如果行足够小以至于所有偏移量都是 1 字节,那么这将对每个现有值(1 字节偏移量,2 字节数据)使用 3 个字节,对每个不存在的值使用 1 个字节。
这两种方法都比使用带有特殊值的 INT
来表示它不存在要好,因为每个值都需要 4 个字节的数据。
见NULL in MySQL (Performance & Storage)
评论里给出了最佳答案,所以我会把它和一些可用的代码重新发布在这里,以供进一步参考。
MySQL 有一个 varbinary
类型非常适合这个:你可以简单地使用 PHP 的 pack
/unpack
函数来转换它们来回二进制形式,并使用varbinary
将该二进制形式存储在数据库中。转换的示例代码如下。
function pack24bit($n) { //input: 24-bit integer, output: binary string of length 3 bytes
$b3 = $n%256;
$b2 = $n/256;
$b1 = $b2/256;
$b2 = $b2%256;
return pack('CCC',$b1,$b2,$b3);
}
function unpack24bit($packed) { //input: binary string of 3 bytes long, output: 24-bit int
$arr = unpack('C3b',$packed);
return 256*(256*$arr['b1']+$arr['b2'])+$arr['b3'];
}