วิธีจัดเก็บและเรียกค้นพจนานุกรมด้วย redis


94
# I have the dictionary my_dict
my_dict = {
    'var1' : 5
    'var2' : 9
}
r = redis.StrictRedis()

ฉันจะจัดเก็บ my_dict และเรียกคืนด้วย redis ได้อย่างไร ตัวอย่างเช่นรหัสต่อไปนี้ไม่ทำงาน

#Code that doesn't work
r.set('this_dict', my_dict)  # to store my_dict in this_dict
r.get('this_dict')  # to retrieve my_dict

คำตอบ:


160

คุณสามารถทำได้โดยhmset(สามารถตั้งค่าได้หลายปุ่มโดยใช้hmset)

hmset("RedisKey", dictionaryToSet)

import redis
conn = redis.Redis('localhost')

user = {"Name":"Pradeep", "Company":"SCTL", "Address":"Mumbai", "Location":"RCP"}

conn.hmset("pythonDict", user)

conn.hgetall("pythonDict")

{'Company': 'SCTL', 'Address': 'Mumbai', 'Location': 'RCP', 'Name': 'Pradeep'}

48
หากเป็นโครงสร้างข้อมูลที่ซ้อนกันแทนที่จะเป็นเพียงแค่เขียนตามคำบอกเช่นมีอาร์เรย์บางส่วนเป็นต้นจัดเรียงข้อมูลของคุณด้วยการjson.dumps()เขียนเป็นสตริงและหลังจากดึงข้อมูลจากผู้ใช้ redis json.loads()เพื่อแยกข้อมูลกลับไปยังโครงสร้างข้อมูล python
andilabs

7
json.dumps()และjson.loads()จะใช้งานได้ก็ต่อเมื่อคุณสบายดีโดยให้คีย์พจนานุกรมเป็นสตริง หากไม่เป็นเช่นนั้นคุณอาจลองใช้ของดอง
ryechus

6
json เข้ากันไม่ได้กับไบต์ดังนั้น json serilization ไม่ใช่โซลูชันระดับโลกเช่นหากคุณมี dict ที่มีค่าไบต์สิ่งนี้จะไม่ทำงาน
Tommy

8
ตามหมายเหตุเอกสารประกอบสำหรับhmsetไม่ได้บอกคุณในเรื่องนี้ แต่จะทำให้เกิด DataError ขึ้นหากคุณพยายามจัดเก็บคำสั่งที่ว่างเปล่า
hlongmore

1
@Pradeep เราจะทำให้คีย์ไดนามิกได้อย่างไร สมมติว่ามีการแทรกข้อมูลทุกๆ 15 นาทีดังนั้นฉันจะสร้างคีย์ไดนามิกได้อย่างไร
ak3191

36

คุณสามารถดองคำสั่งของคุณและบันทึกเป็นสตริง

import pickle
import redis

r = redis.StrictRedis('localhost')
mydict = {1:2,2:3,3:4}
p_mydict = pickle.dumps(mydict)
r.set('mydict',p_mydict)

read_dict = r.get('mydict')
yourdict = pickle.loads(read_dict)

12
นี่เป็นความจริง แต่ขึ้นอยู่กับอัตราการอ่านและเขียนสิ่งนี้อาจเพิ่มค่าใช้จ่ายที่ร้ายแรง การดองเป็นการดำเนินการที่ช้า
Tommy

1
โปรดทราบว่าถ้าเรื่องนี้ถูกนำมาใช้กับการป้อนข้อมูลของผู้ใช้เซิร์ฟเวอร์ของคุณมีแนวโน้มที่จะ exection โค้ดจากระยะไกล , pickle.loadsเป็นที่ควรจะนำมาใช้ในที่เชื่อถือได้ 100% ข้อมูล
Paradoxis

16

อีกวิธีหนึ่ง: คุณสามารถใช้RedisWorksไลบรารี

pip install redisworks

>>> from redisworks import Root
>>> root = Root()
>>> root.something = {1:"a", "b": {2: 2}}  # saves it as Hash type in Redis
...
>>> print(root.something)  # loads it from Redis
{'b': {2: 2}, 1: 'a'}
>>> root.something['b'][2]
2

มันแปลงประเภท python เป็นประเภท Redis และในทางกลับกัน

>>> root.sides = [10, [1, 2]]  # saves it as list in Redis.
>>> print(root.sides)  # loads it from Redis
[10, [1, 2]]
>>> type(root.sides[1])
<class 'list'>

Disclaimer: ฉันเขียนห้องสมุด นี่คือรหัส: https://github.com/seperman/redisworks


2
โดยวิธีการบันทึก RedisWorks ใช้hmsetภายใต้ประทุนหากคุณตั้งค่าตัวแปรเป็น Dict ดังนั้นหากคุณทำroot.something = {}คุณจะได้รับ DataError เนื่องจากhmsetไม่อนุญาตให้ใช้พจนานุกรมที่ว่างเปล่า ฉันพูดถึงเรื่องนี้เพราะเอกสารสำหรับ redis ไม่ได้บอกคุณเรื่องนี้
hlongmore

น่าสนใจ. hmsetใช่มันไม่ใช้ ฉันจะตรวจสอบสิ่งนี้ @hlongmore
Seperman

แต่ยังสามารถรองรับไบต์ในพจนานุกรมได้หรือไม่?
ZettaCircl

12

ตามคำตอบพื้นฐานที่คนอื่นให้ไว้แล้วฉันจึงอยากจะเพิ่มคำตอบนั้น

ต่อไปนี้เป็นคำสั่งในREDISการดำเนินการพื้นฐานกับHashMap/Dictionary/Mappingค่าประเภท

  1. HGET => ส่งคืนค่าสำหรับคีย์เดียวที่ส่งผ่าน
  2. HSET => ตั้งค่า / อัปเดตค่าสำหรับคีย์เดียว
  3. HMGET => ส่งคืนค่าสำหรับคีย์เดียว / หลายคีย์ที่ผ่าน
  4. HMSET => ตั้งค่า / อัปเดตค่าสำหรับหลายคีย์
  5. HGETALL => ส่งคืนคู่ (คีย์ค่า) ทั้งหมดในการแมป

ต่อไปนี้เป็นวิธีการตามลำดับในredis-pyห้องสมุด: -

  1. HGET => hget
  2. HSET => hset
  3. HMGET => hmget
  4. HMSET => hmset
  5. HGETALL => hgetall

เมธอด setter ทั้งหมดข้างต้นจะสร้างการแมปหากไม่มีอยู่ เมธอด getter ทั้งหมดข้างต้นไม่เพิ่มข้อผิดพลาด / ข้อยกเว้นหากไม่มีการแมป / คีย์ในการแมป

Example:
=======
In [98]: import redis
In [99]: conn = redis.Redis('localhost')

In [100]: user = {"Name":"Pradeep", "Company":"SCTL", "Address":"Mumbai", "Location":"RCP"}

In [101]: con.hmset("pythonDict", {"Location": "Ahmedabad"})
Out[101]: True

In [102]: con.hgetall("pythonDict")
Out[102]:
{b'Address': b'Mumbai',
 b'Company': b'SCTL',
 b'Last Name': b'Rajpurohit',
 b'Location': b'Ahmedabad',
 b'Name': b'Mangu Singh'}

In [103]: con.hmset("pythonDict", {"Location": "Ahmedabad", "Company": ["A/C Pri
     ...: sm", "ECW", "Musikaar"]})
Out[103]: True

In [104]: con.hgetall("pythonDict")
Out[104]:
{b'Address': b'Mumbai',
 b'Company': b"['A/C Prism', 'ECW', 'Musikaar']",
 b'Last Name': b'Rajpurohit',
 b'Location': b'Ahmedabad',
 b'Name': b'Mangu Singh'}

In [105]: con.hget("pythonDict", "Name")
Out[105]: b'Mangu Singh'

In [106]: con.hmget("pythonDict", "Name", "Location")
Out[106]: [b'Mangu Singh', b'Ahmedabad']

ฉันหวังว่ามันจะทำให้ทุกอย่างชัดเจนขึ้น


คุณจะสร้างคีย์ไดนามิกได้อย่างไร
ak3191

12

หากคุณต้องการจัดเก็บ python dict ใน redis ควรจัดเก็บเป็นสตริง json

import redis

r = redis.StrictRedis(host='localhost', port=6379, db=0)
mydict = { 'var1' : 5, 'var2' : 9, 'var3': [1, 5, 9] }
rval = json.dumps(mydict)
r.set('key1', rval)

ในขณะที่ดึง de-serialize โดยใช้ json.loads

data = r.get('key1')
result = json.loads(data)
arr = result['var3']

ประเภท (เช่นไบต์) ที่ไม่ต่อเนื่องโดยฟังก์ชัน json ล่ะ?

คุณสามารถเขียนฟังก์ชันตัวเข้ารหัส / ตัวถอดรหัสสำหรับประเภทที่ไม่สามารถต่ออนุกรมโดยฟังก์ชัน json ได้ เช่น. การเขียนฟังก์ชันตัวเข้ารหัส / ถอดรหัส base64 / ascii สำหรับไบต์อาร์เรย์


ฉันลงคะแนนสิ่งนี้เนื่องจากคำสั่งบางตัวไม่สามารถทำให้เป็นอนุกรมกับ JSON ได้เช่นเขียนตามคำบอกด้วยค่าไบต์
Tommy

1
คุณสามารถเขียนฟังก์ชันตัวเข้ารหัส / ตัวถอดรหัส (ตามข้อกำหนดเช่นการเข้ารหัส base64 / ascii) สำหรับประเภทที่ไม่สามารถเข้ารหัส / ถอดรหัสโดยค่าเริ่มต้น
Saji Xavier

@ ทอมมี่ - แม้ว่าจะใช้ hmset / hgetall คุณอาจต้องเข้ารหัส / ถอดรหัสประเภทที่ redis ไม่รองรับ
Saji Xavier

1
ไม่เห็นด้วยเกี่ยวกับ "... การดำเนินการหลังคือ O (N)" N คือจำนวนฟิลด์ที่คุณมีลิงก์ไปยังคีย์ การทำ N SET / GET หรือ 1 HGET / HSET ก็มีความซับซ้อนเหมือนกัน ดู: redis.io/commands/hmset Time-wise, HGET / HSET เป็นธุรกรรมปรมาณูดังนั้นจึงดำเนินการได้เร็วกว่าโดย REDIS คุณแค่ย้ายความซับซ้อนจาก Redis ไปเป็น Python Code
ZettaCircl

ข้อดีของ hmset คือความเป็นไปได้ในการดึงเฉพาะบางส่วนย่อยของ dict ด้วย json เราแพ้ดังนั้นนี่ก็ดีพอ ๆ กับของดองหรืออย่างอื่น
Jorge Leitao

5

อาจพิจารณาใช้MessagePackซึ่งรับรองโดย redis

import msgpack

data = {
    'one': 'one',
    'two': 2,
    'three': [1, 2, 3]
}

await redis.set('my-key', msgpack.packb(data))
val = await redis.get('my-key')
print(msgpack.unpackb(val))

# {'one': 'one', 'two': 2, 'three': [1, 2, 3]}

ใช้msgpack-pythonและaioredis


4

อีกวิธีหนึ่งที่คุณสามารถเข้าใกล้ประเด็นนี้:

import redis
conn = redis.Redis('localhost')

v={'class':'user','grants': 0, 'nome': 'Roberto', 'cognome': 'Brunialti'}

y=str(v)
print(y['nome']) #<=== this return an error as y is actually a string
conn.set('test',y)

z=eval(conn.get('test'))
print(z['nome']) #<=== this really works!

ฉันไม่ได้ทดสอบประสิทธิภาพ / ความเร็ว


3

คำสั่ง redis SET เก็บสตริงไม่ใช่ข้อมูลโดยพลการ คุณสามารถลองใช้คำสั่ง redis HSET เพื่อจัดเก็บ dict เป็นแฮช redis ด้วยสิ่งที่ต้องการ

for k,v in my_dict.iteritems():
    r.hset('my_dict', k, v)

แต่ประเภทข้อมูล redis และ python datatypes ไม่ค่อยเรียงกัน Python dicts สามารถซ้อนกันได้โดยพลการ แต่แฮช redis จะกำหนดให้ค่าของคุณเป็นสตริง อีกวิธีหนึ่งที่คุณสามารถทำได้คือการแปลงข้อมูล python ของคุณเป็นสตริงและจัดเก็บข้อมูลนั้นไว้ใน redis เช่น

r.set('this_dict', str(my_dict))

จากนั้นเมื่อคุณดึงสตริงออกมาคุณจะต้องแยกวิเคราะห์เพื่อสร้างวัตถุ python ขึ้นมาใหม่


1
เขาสามารถแปลงข้อมูลของเขาเป็น json และเก็บผลลัพธ์ไว้ใน redis
Narcisse Doudieu Siewe

3

HMSET เลิกใช้งานแล้ว ตอนนี้คุณสามารถใช้ HSET กับพจนานุกรมได้ดังนี้:

import redis
r = redis.Redis('localhost')

key = "hashexample" 
queue_entry = { 
    "version":"1.2.3", 
    "tag":"main", 
    "status":"CREATED",  
    "timeout":"30"
    }
r.hset(key,None,None,queue_entry)

ขอบคุณ! ฉันพยายามหาเอกสารที่สะกดทั้งหมดนี้ คุณรู้ไหมว่ามันอยู่ที่ไหน. ตัวอย่างเช่น "ไม่มี" สองรายการนี้มีไว้เพื่ออะไร
NealWalters

@NealWalters: ดูบรรทัดบนหน้าคำสั่งHMSET - redis.io/commands/hmsetสำหรับคำเตือนการเลิกใช้งาน
Saransh Singh

0

ลองrejson-pyซึ่งค่อนข้างใหม่ตั้งแต่ปี 2017 ดูบทนำนี้

from rejson import Client, Path

rj = Client(host='localhost', port=6379)

# Set the key `obj` to some object
obj = {
    'answer': 42,
    'arr': [None, True, 3.14],
    'truth': {
        'coord': 'out there'
    }
}
rj.jsonset('obj', Path.rootPath(), obj)

# Get something
print 'Is there anybody... {}?'.format(
    rj.jsonget('obj', Path('.truth.coord'))
)

# Delete something (or perhaps nothing), append something and pop it
rj.jsondel('obj', Path('.arr[0]'))
rj.jsonarrappend('obj', Path('.arr'), 'something')
print '{} popped!'.format(rj.jsonarrpop('obj', Path('.arr')))

# Update something else
rj.jsonset('obj', Path('.answer'), 2.17)

0

หากคุณไม่ทราบวิธีจัดระเบียบข้อมูลใน Redis อย่างชัดเจนฉันได้ทำการทดสอบประสิทธิภาพบางอย่างรวมถึงการแยกวิเคราะห์ผลลัพธ์ คำสั่งที่ฉันใช้ ( d ) มี 437.084 คีย์ (รูปแบบ md5) และค่าของแบบฟอร์มนี้:

{"path": "G:\tests\2687.3575.json",
 "info": {"f": "foo", "b": "bar"},
 "score": 2.5}

การทดสอบครั้งแรก (การแทรกข้อมูลลงในการแมปคีย์ - ค่า redis):

conn.hmset('my_dict', d)  # 437.084 keys added in 8.98s

conn.info()['used_memory_human']  # 166.94 Mb

for key in d:
    json.loads(conn.hget('my_dict', key).decode('utf-8').replace("'", '"'))
    #  41.1 s

import ast
for key in d:
    ast.literal_eval(conn.hget('my_dict', key).decode('utf-8'))
    #  1min 3s

conn.delete('my_dict')  # 526 ms

การทดสอบครั้งที่สอง (การแทรกข้อมูลลงในปุ่ม Redis โดยตรง):

for key in d:
    conn.hmset(key, d[key])  # 437.084 keys added in 1min 20s

conn.info()['used_memory_human']  # 326.22 Mb

for key in d:
    json.loads(conn.hgetall(key)[b'info'].decode('utf-8').replace("'", '"'))
    #  1min 11s

for key in d:
    conn.delete(key)
    #  37.3s

ดังที่คุณเห็นในการทดสอบครั้งที่สองต้องแยกวิเคราะห์เฉพาะค่า "ข้อมูล" เนื่องจาก hgetall (คีย์) ส่งคืนคำสั่งอยู่แล้ว แต่ไม่ใช่ค่าที่ซ้อนกัน

และแน่นอนว่าตัวอย่างที่ดีที่สุดของการใช้ Redis เป็นคำสั่งของ python คือการทดสอบครั้งแรก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.