散列

Redis 散列键 hash key 会将一个键和一个散列在数据库里关联起来, 散列中可以存任意多个字段 field. 与字符串一样, 散列字段和值既可以是文本数据, 也可以是二进制数据.

示例: 短网址生成

为了给用户提供更多空间, 并记录用户在网站上的链接点击行为, 大部分社交网站都会将用户输入的网址转换为短网址. 当用户点击段网址时, 后台就会进行数据统计, 并引导用户跳转到原地址.
创建短网址本质上就是, 要创建出短网址ID与目标网址之间的映射, 并让用户访问短网址时, 根据短网址的ID映射记录中找出与之相对应的目标网址.

短网址 ID目标网址
RqRRz8nhttp://redisdoc.com/geo/index.html
RUwtQBxhttp://item.jd.com/117910607.html

Redis 散列底层为无序存储的, 因此HKEYS, HVALS 和 HGETALL 可能会得到不同的结果, 因此不应该对其返回元素顺序做任何假设.

示例: 存储图数据

图是一直常用的数据结构, 这里使用 field=edge, value=weight 的表示法来存储图结构, 其中 edge 由 start->edge 构成

PYTHON
from redis import Redis

def make_edge_from_vertexs(start, end):
    return str(start) + "->" + str(end)

def decompose_vertexs_from_edge_name(name):
    return name.split("->")

class Graph:
    def __init__(self, client, key):
        self.client = client
        self.key = key

    def add_edge(self, start, end, weight):
        edge = make_edge_from_vertexs(start, end)
        self.client.hset(self.key, edge, weight)

    def remove_edge(self, start, end):
        edge = make_edge_from_vertexs(start, end)
        return self.client.hdel(self.key, edge)

    def get_edge_weight(self, start, end):
        edge = make_edge_from_vertexs(start, end)
        return self.client.hget(self.key, edge)

    def has_edge(self, start, end):
        edge = make_edge_from_vertexs(start, end)
        return self.client.hexists(self.key, edge)
    
    def add_multi_edges(self, *tuples):
        nodes_and_weights = {}
        for start, end, weight in tuples:
            edge = make_edge_from_vertexs(start, end)
            nodes_and_weights[edge] = weight
        self.client.hset(self.key, mapping=nodes_and_weights) # hmset 在 4.0 已抛弃, 使用 .hset(mapping={...})

    def get_multi_edge_weights(self, *tuples):
        edge_list = []
        for start, end in tuples:
            edge = make_edge_from_vertexs(start, end)
            edge_list.append(edge)
        return self.client.hmget(self.key, edge_list)

    def get_all_edges(self):
        edges = self.client.hkeys(self.key)
        result = set()
        for edge in edges:
            start, end = decompose_vertexs_from_edge_name(edge)
            result.add((start, end))
        return result

    def get_all_edges_with_weight(self):
        edges_and_weights = self.client.hgetall(self.key)
        result = set()
        for edge, weight in edges_and_weights.items():
            start, end = decompose_vertexs_from_edge_name(edge)
            result.add((start, end, weight))
        return result

client = Redis(decode_responses=True)
graph = Graph(client, "test-graph")
graph.add_edge("a", "b", 30)
graph.add_edge("c", "b", 25)
graph.add_multi_edges(("b", "d", 70), ("d", "e", 10))

print("edge a-> b weight:", graph.get_edge_weight("a", "b"))
print("a->b 是否存在:", graph.has_edge("a", "b"))
print("b->a 是否存在:", graph.has_edge("b", "a"))
print("所有边:", graph.get_all_edges())
print("所有边和权重", graph.get_all_edges_with_weight())
Click to expand and view more

这里的图数据结构提供了边和权重的功能, 可以快速检查边是否存在, 能够方便的添加和移除边, 适合存储结点较多但是边较少的稀疏图(sparse graph).

示例: 使用散列键重新实现文章存储程序

PYTHON
from redis import Redis
from time import time

class Article:
    def __init__(self, client, article_id):
        self.client = client
        self.article_id = str(article_id)
        self.article_hash = "article::" + self.article_hash
    def is_exists(self):
        return self.client.hexists(self.article_hash)
    def create(self, title, content, author):
        if self.is_exists():
            return False
        article_data = {
            "title": title,
            "content": content,
            "author": author,
            "created_at": time(),
        }
        return self.client.hset(self.article_hash, mapping=article_data)
    def get(self):
        article_data = self.client.hgetall(self.article_hash)
        article_data["id"] = self.article_id # 添加 id 到文章数据, 方便用户操作
        return article_data
    def update(self, title=None, content=None, author=None):
        if not self.is_exists():
            return False
        article_data = {}
        if title is not None:
            article_data["title"] = title
        if content is not None:
            article_data["content"] = content
        if author is not None:
            article_data["author"] = author
        return self.client.hset(self.article_hash, mapping=article_data)

client = Redis(decode_responses=True)
article = Article(client, 10086)
article.create("greeting", "hello world", "peter")
Click to expand and view more

Wrapping Up

string 和 hash 总结与对比

Start searching

Enter keywords to search articles

↑↓
ESC
⌘K Shortcut