如果我们需要在云或
中的某个地方存储不同格式和尺寸的数据,我们通常会使用blob存储
我们的内部存储。 Minio是S3兼容存储,您可以在私有云,裸机服务器
上运行它
甚至在边缘设备上。您也可以将其调整为将历史数据作为斑点的时间序列。最多的
直接解决方案是为每个数据源创建一个文件夹,并在其
中使用时间戳保存对象
名称:
bucket
|
|---cv_camera
|---1666225094312397.bin
|---1666225094412397.bin
|---1666225094512397.bin
如果您需要查询数据,则应请求cv_camera
文件夹中的对象列表,然后按名称过滤
根据给定的时间间隔。
这种方法易于实施,但有一些缺点:
- 文件夹的对象越多,查询的时间越长。
- 小对象的大开销:由于字符串和最小文件大小为1KB或512 文件系统。
- FIFO配额,要在我们达到一定限制时删除旧数据,可能对密集的写作操作不起作用。
ReductStore旨在解决这些问题。它具有强大的FIFO配额,用于通过时间查询数据的HTTP API
间隔,并将对象(或记录)组合到块中,以进行有效的磁盘使用和搜索。
Minio和ReductStore具有Python SDK,因此我们可以使用它们来实施读写操作并比较
性能。
用Minio读/写数据
对于基准测试,我们创建了两个函数来编写和读取CHUNK_COUNT
块:
from minio import Minio
import time
minio_client = Minio("127.0.0.1:9000", access_key="minioadmin", secret_key="minioadmin", secure=False)
def write_to_minio():
count = 0
for i in range(CHUNK_COUNT):
count += CHUNK_SIZE
object_name = f"data/{str(int(time.time_ns() / 1000))}.bin"
minio_client.put_object(BUCKET_NAME, object_name, io.BytesIO(CHUNK),
CHUNK_SIZE)
return count # count data to print it in main function
def read_from_minio(t1, t2):
count = 0
t1 = str(int(t1 * 1000_000))
t2 = str(int(t2 * 1000_000))
for obj in minio_client.list_objects("test", prefix="data/"):
if t1 <= obj.object_name[5:-4] <= t2:
resp = minio_client.get_object("test", obj.object_name)
count += len(resp.read())
return count
您可以看到minio_client
不提供任何带有模式的API查询数据,因此我们必须浏览整个文件夹
在客户端找到所需的对象。如果您有数十亿个对象,则停止工作。您必须存储
某些时间序列数据库中的对象路径或创建文件夹的层次结构,例如每天创建一个新文件夹。
使用还原店读取/编写数据
使用ReductStore这要容易得多:
from reduct import Client as ReductClient
reduct_client = ReductClient("http://127.0.0.1:8383")
async def write_to_reduct():
count = 0
bucket = await reduct_client.create_bucket("test", exist_ok=True)
for i in range(CHUNK_COUNT):
await bucket.write("data", CHUNK)
count += CHUNK_SIZE
return count
async def read_from_reduct(t1, t2):
count = 0
bucket = await reduct_client.get_bucket("test")
async for rec in bucket.query("data", int(t1 * 1000000), int(t2 * 1000000)):
count += len(await rec.read_all())
return count
基准
当我们具有写入/读取功能时,我们最终可以写下我们的基准:
import io
import random
import time
import asyncio
from minio import Minio
from reduct import Client as ReductClient
CHUNK_SIZE = 100000
CHUNK_COUNT = 10000
BUCKET_NAME = "test"
CHUNK = random.randbytes(CHUNK_SIZE)
minio_client = Minio("127.0.0.1:9000", access_key="minioadmin", secret_key="minioadmin", secure=False)
reduct_client = ReductClient("http://127.0.0.1:8383")
# Our function were here..
if __name__ == "__main__":
print(f"Chunk size={CHUNK_SIZE / 1000_000} Mb, count={CHUNK_COUNT}")
ts = time.time()
size = write_to_minio()
print(f"Write {size / 1000_000} Mb to Minio: {time.time() - ts} s")
ts_read = time.time()
size = read_from_minio(ts, time.time())
print(f"Read {size / 1000_000} Mb from Minio: {time.time() - ts_read} s")
loop = asyncio.new_event_loop();
ts = time.time()
size = loop.run_until_complete(write_to_reduct())
print(f"Write {size / 1000_000} Mb to ReductStore: {time.time() - ts} s")
ts_read = time.time()
size = loop.run_until_complete(read_from_reduct(ts, time.time()))
print(f"Read {size / 1000_000} Mb from ReductStore: {time.time() - ts_read} s")
用于测试,我们需要运行数据库。 docker-compose很容易:
services:
reduct-storage:
image: reductstorage/engine:v1.0.1
volumes:
- ./reduct-data:/data
ports:
- 8383:8383
minio:
image: minio/minio
volumes:
- ./minio-data:/data
command: minio server /data --console-address :9002
ports:
- 9000:9000
- 9002:9002
运行Docker组成配置和基准:
docker-compose up -d
python3 main.py
结果
脚本打印给定的chunk_size和CHUNK_COUNT
的结果。在我的设备上,我得到以下数字:
块 | 操作 | minio th> | 还原店 |
---|---|---|---|
10.0 MB(100个请求) | 写 | 8.69 S | 0.53 S |
阅读 | 1.19 S | 0.57 S | |
1.0 MB(1000请求) | 写 | 12.66 S | 1.30 S |
阅读 | 2.04 S | 1.38 S | |
.1 MB(10000请求) | 写 | 61.86 S | 13.73 S |
阅读 | 9.39 S | 15.02 S |
如您所见,ReductStore的写作操作总是更快(10 MB斑点的16倍!!!),有点
当我们有许多小物体时,阅读速度较慢。您可能会注意到,当我们
时,两个数据库的速度都会降低
减少块的大小。这可以用HTTP开销来解释,因为我们花费了专用的HTTP请求
每个写或读取操作。
结论
ReductStore可能是您需要使用时间戳和
存储斑点的应用程序的一个不错的选择
连续写数据。它具有强大的FIFO配额来避免磁盘空间问题,并且对于密集的
来说非常快
写操作。