minio update meta info 更新minio S3對象存儲meta的實現方法


minio update meta info 更新minio S3對象存儲meta的實現方法

應用場景

對象存儲就不多作介紹了

亞馬遜,阿里雲,金山雲,都有相應的對象存儲服務

常見的方案有ceph,minio等

amazon S3 是事實上的標准,金山雲,minio都支持S3協議

通常對象存儲,存一些小文件,又以互聯網的媒體文件為主,例如圖片,視頻等等

現將大數據中的部分媒體文件遷至minio,該工作基本已全部做完

要對媒體文件,附加深度學習/機器學習算法應用文本識別,語音識別,特征抽取,向量信息等算法結果

通過sql/nosql外部存儲,保存映射關系的方案就不提了,各有優缺點

缺點主要是又引入了一重依賴,復雜度相對較高,考慮在直接在minio的對象meta上附加信息

因為要應用各種算法,所以為了方便對接,直接相關項目,也部分采用了python實現,以下示例全部為python,其他語言也類似

首先 minio上傳文件時可以附加meta信息

https://docs.min.io/cn/python-client-api-reference.html#put_object


# Put an object 'myobject' with contents from '/tmp/otherobject', upon success prints the etag identifier computed by server.
try:
    print(minioClient.fput_object('mybucket', 'myobject', '/tmp/otherobject'))
except ResponseError as err:
    print(err)

# Put on object 'myobject.csv' with contents from
# '/tmp/otherobject.csv' as 'application/csv'.
try:
    print(minioClient.fput_object('mybucket', 'myobject.csv',
                             '/tmp/otherobject.csv',
                             content_type='application/csv'))
except ResponseError as err:
    print(err)
    

在metadata中存儲附加信息,但是這只是文件上傳時可以附加,而大數據和各種數據挖掘算法的應用較為耗時,通常是異步,延后更新的,並不適用

需要找到update object metadata的方法

遺憾的是 https://docs.min.io/cn/python-client-api-reference.html 查官方各語言的api文檔,都不提供update方法

所以就只能用外部存儲了?看官方文檔沒有update方法,通常就放棄了,有精力去官方提issue,或改代碼提merget,精力有限,這個先跳過

只是總覺得不應該這樣,個人評判一個技術產品和一個功能需求時,大概會考慮這個需求是否普遍,再考慮技術產品的生態成熟度

update object metadata 是較為普遍的功能需求,minio是較為成熟優秀的對象存儲方案,這種功能,在未看文檔前,我預期的是會支持才對


總之沒有找到原生的update object metadata sdk/api 看能不能在其他sdk/api上想辦法實現

put_res = client.put_object(bucket_name, object_name, BytesIO(
        msg), len(msg), content_type=content_type, metadata={})
('f124631fb276876f23e0c8d81f3649ae', None)

調用put_object的返回值put_res 包含上傳對象的md5值

minio 實現put_object會不會檢查客戶端和服務器同名object的md5?如果完全一致,就免去該次put_object的網絡io呢?(百度雲的秒傳,就是這種思路)

來作測試

def test_bif_file_multi_put_same_file():
    MINIO_ACCESS_KEY = "AKIAIOSFODNN7EXAMPLE"
    MINIO_SECRET_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
    ENDPOINT = "192.168.5.196:9000"
    start_time = time.time()
    fileBytes = readFileBytes("./NVIDIA-Linux-x86_64-450.51.06.run")
    minioClient = Minio(ENDPOINT, access_key=MINIO_ACCESS_KEY,
                        secret_key=MINIO_SECRET_KEY, secure=False)
    put_res = putObject(minioClient, "testbucketname",
                        "objectest2.jpeg", fileBytes, 'image/jpeg', None)
    first_finish_time = time.time()
    print("第一次上傳時間時間開銷", first_finish_time-start_time)
    put_res = putObject(minioClient, "testbucketname",
                        "objectest2.jpeg", fileBytes, 'image/jpeg', None)
    secend_finish_time = time.time()
    print("第二次上傳時間時間開銷", secend_finish_time-first_finish_time)

第一次上傳時間時間開銷 19.879034519195557
第二次上傳時間時間開銷 22.13285183906555
重復上傳了一次,minio垃圾

很遺憾並不是期望中的那樣,大文件的第二次上傳,依然會花費同樣的時間,很失望

另外又看到一個api

import os
# Put a file with default content-type, upon success prints the etag identifier computed by server.
try:
    with open('my-testfile', 'rb') as file_data:
        file_stat = os.stat('my-testfile')
        print(minioClient.put_object('mybucket', 'myobject',
                               file_data, file_stat.st_size))
except ResponseError as err:
    print(err)

https://docs.min.io/cn/javascript-client-api-reference.html#copyObject

var conds = new Minio.CopyConditions()
conds.setMatchETag('bd891862ea3e22c93ed53a098218791d')
minioClient.copyObject('mybucket', 'newobject', '/mybucket/srcobject', conds, function(e, data) {
  if (e) {
    return console.log(e)
  }
  console.log("Successfully copied the object:")
  console.log("etag = " + data.etag + ", lastModified = " + data.lastModified)
})

上傳從客戶端到服務端,要上傳完整的文件流

但copy總不會也這樣,對存儲服務,底層二進制文件,上層都只是一個引用,copy一般是加一個引用信息,不會把文件io再跑一次

而copy_object(bucket_name, object_name, object_source, copy_conditions=None, metadata=None)

copy_object 又是可以附加文件信息的

stat_object(bucket_name, object_name)

而舊文檔的metadata是可以拿到的

已知這些信息,就有了一個實現方案,是否可執行還需驗證

有經驗的開發人員應該都也想到了

  • 1 stat_object 獲取old object meta
  • 2 old object meta 附加新的值,生成new object meta
  • 3 copy old object -> other ojbect

其實這一步對一些場景已經可以滿足了
原始文件放在 /old/object1
附加meta的文件放在 /new/object1 新地址的object1已經有附加的meta信息

但還不夠好,因為路徑變了,我們要的變更原文件的信息,這個也簡單,再copy回去就行

  • 4 對要修改old object meta值的 再執行 copy other ojbect -> old ojbect

  • 5 完成更改后 remove other ojbect

已驗證可行,具體代碼如下,代碼比較粗略,主要是提供一種思路和方案

def updateObjectMeta(client=minioClient, bucket_name=None, object_name=None, metadata=None, is_merge=True):
    """
    更新meta
    !注意,官方不提供該功能,目前通過多次copy實現,網絡io較多
    """
    if not metadata:
        raise Exception("未定義待更新meta")
    new_metadata = metadata
    # merge meta
    if is_merge:
        exist_res = client.stat_object(bucket_name, object_name)
        old_metadata = exist_res.metadata
        for key in metadata:
            old_metadata[key] = metadata[key]
        new_metadata = old_metadata
    tmp_object_name = object_name+"_update_metadata_tmp"
    # source obj -> tmp-obj
    tmp_obj_res = client.copy_object(
        bucket_name, tmp_object_name, bucket_name+"/"+object_name)
    print("copy to tmp", bucket_name, tmp_object_name,
          bucket_name+"/"+object_name, tmp_obj_res)
    # tmp-obj -> source obj with new metadata
    print("new_metadata", new_metadata)
    org_obj_res = client.copy_object(
        bucket_name, object_name, bucket_name+"/"+tmp_object_name, metadata=new_metadata)
    print("copy to back", bucket_name, object_name,
          bucket_name+"/"+tmp_object_name, org_obj_res)
    # remove tmp-obj
    client.remove_object(bucket_name, tmp_object_name)

缺點也很明顯,每次api調用,都是一次網絡io,一共幾次我懶得數了,單機本地測試,每次io在300ms左右,需根據項目評估性能,但對我個人而言還可以接受

經個人實際使用最大的缺點是object meta的空間很小,具體多少我忘記了,大概2000個中文?

這主要是因為http協議的問題meta放在http response header中間,除過其他必須的header項,剩下的空間可以放meta,但總體空間很少

也因此最好只能放一些定長meta,不然會一部分數據能更新上meta,另一部分數據更新不上,不統一


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM