【全网独家】bytearray+bytes管理redis数据

目录

0.前言

1. redis的哈希表的value,只能是int、string、bytes 

2. bytearray和bytes可以互转,并且bytearray可以改变长度

3. array.array将传给bytearray的float数组先转为正确格式

4. 参考:

5. TODO

 


0.前言

  • 问题背景:python下用redis管理数据库

  • 数据库结构为:

  • A(用户) 管理多个B(物品), 每个B下有多个C(物品的不同图片),其中C是float数组(直接拿到时是numpy.array)

  • 原本操作是一个用户一个redis的db(分页库一样的东西),每个db里按照key去以list格式存放C(C本来就是float数组),另外“比较”只涉及同个用户底下的数据的比较(不同db不能比较),因此这种按list进行数据库管理的考虑是很自然的

  • 最大的问题在于一点,redis的db库只有16个,因此上种写法在用户数达到16个时,无法进行新的录入

  • =======================================================

  • 因此考虑使用redis的hash(哈希表)格式管理数据库

  • 形式为:db=0下,用户名为哈希表(key),物品为键值对的域(field),一个物品所有的图片合成一个存为键值对的值(value)

  • 在“一个物品所有的图片合成一个存为键值对的值(value)”这步遇到了坑,以下是debug历程和结论:


1. redis的哈希表的value,只能是int、string、bytes 

  1.  int和string都有局限性,bytes是二进制,理论上所有的数据都能转成二进制压起来。所以考虑用bytes
  2.  一开始考虑用np.tobytes和np.frombuffer来把整个numpy.array压缩,然后用b',,,'进行分隔,读取时用split(b",,,")去分割。这步我当时写的时候也觉得很有问题,因为传给数据库是一个bytes,每次增加新的C(float数组)都是按照“+b",,,"+bytes(float_array)”的形式在这个value尾部加上。
  3. 后来我才确定bytes是不能改变长短的。

2. bytearray和bytes可以互转,并且bytearray可以改变长度

后来我发现了好东西bytearray,其实就是bytes的array,和普通array的函数基本一致。我试验了下面这段代码,很成功:

import numpy as np

a = np.array([0.11,0.22,0.44]) #len(a) =3
b = np.array([0.55,0.66,0.77]) #len(b) =3

ba = bytearray(b'')
ba.extend(bytearray(a))
blist = bytes(ba)

ba = bytearray(blist)
ba.extend(bytearray(b))
blist = bytes(ba)
print(np.frombuffer(blist))  # 结果:[0.11 0.22 0.44 0.55 0.66 0.77]

但是我改了一下a,b的值,出现了奇怪的结果:

import numpy as np

a = np.array([1,2,3]) 
b = np.array([4,5,6]) 

ba = bytearray(b'')
ba.extend(bytearray(a))
blist = bytes(ba)

ba = bytearray(blist)
ba.extend(bytearray(b))
blist = bytes(ba)
print(np.frombuffer(blist))  # 结果:[4.24399158e-314 8.48798317e-314 1.27319747e-313]

但是改成float就没问题:

import numpy as np

a = np.array([1.,2.,3.]) 
b = np.array([4.,5.,6.]) 

ba = bytearray(b'')
ba.extend(bytearray(a))
blist = bytes(ba)

ba = bytearray(blist)
ba.extend(bytearray(b))
blist = bytes(ba)
print(np.frombuffer(blist))  # 结果:[1. 2. 3. 4. 5. 6.]

当时我有点侥幸心理,因为我的数据肯定是float,但是遇到一个bug:

我的数据是512维的数组,就测一个数组的情况下(存给value对应的数组总长就512),

但是先做一遍bytearray→bytes(存入数据库),再做一遍bytes→bytearray(从数据库取出)时,取出来的数组长度为256。


一开始我搜错了,以为bytearray最大长度是256。后面发现不是这样。仔细考虑了一下,觉得应该是我程序里获取到的那个float数组和我上面测试里的float数组格式不一样(32位和64位的区别)

因此我去找bytearray或者bytes的转格式时能不能指定数据的格式,搜了一圈发现没法通过bytearray指定这个数组里元素的格式。


3. array.array将传给bytearray的float数组先转为正确格式
 

既然bytearray()函数没法指定数组元素的格式,所以我就转而找到array.array先进行转格式。

我看了一下array.array支持的格式:

Type code C Type Python Type Minimum size in bytes
'c' char character 1
'b' signed char int 1
'B' unsigned char int 1
'u' Py_UNICODE Unicode character 2
'h' signed short int 2
'H' unsigned short int 2
'i' signed int int 2
'I' unsigned int long 2
'l' signed long int 4
'L' unsigned long long 4
'f' float float 4
'd' double float 8

看到上表最后两行,应该是发现了某个python坑,因此改动上面的测试代码,同时同步更新我的程序里的写法为:

import numpy as np
import array

a = np.array([1,2,3]) 
b = np.array([4,5,6]) 

ba = bytearray(b'')
ba.extend(bytearray(array.array('d',a)))
blist = bytes(ba)

ba = bytearray(blist)
ba.extend(bytearray(array.array('d',b)))
blist = bytes(ba)
print(np.frombuffer(blist))  # 结果:[1. 2. 3. 4. 5. 6.]

 在我正式的程序中也正确。
另外,在np.frombuffer()中指定np.float32或者np.float64似乎也能解决问题。
后话:我看到有很多SO上的回答都是推荐用struct,我看那个形式太丑了就不想用。 


4. 参考:

  1. 【Python】array.array by 吉吉于
  2. StackOverflow: How can I convert a byte array to a double in python?  by Martijn Pieters♦
  3. StackOverflow: Convert bytearray to bytes-like object?
  4. StackOverflow: Python bytes concatenation by Jasper

5. TODO

  •  时间测试
  •  

猜你喜欢

转载自blog.csdn.net/weixin_36047799/article/details/89337477