2

注意: 这是一个详细的问题,询问如何使用 memcached 在我的 Web 应用程序中最好地实现和管理数据库缓存。这个问题使用 Python/Django 来说明数据模型和用法,但语言并不是那么相关。我真的对学习保持缓存一致性的最佳策略更感兴趣。Python/Django 恰好是我用来说明这个问题的语言。

我的申请规则:

  1. 我有一个 3 x 3 的整数单元格网格
  2. 这个网格的大小将来可能会增加或减少。我们的解决方案必须扩展。
  3. 它们是每行的累积分数,通过(value * Y-Coord)对该行中的每个单元格求和来计算。
  4. 它们是每列的累积分数,通过(value * X-Coord)对该列中的每个单元格求和来计算。
  5. 单元格中的值很少更改。但是这些值和分数分数经常被读取。
  6. 我想用它memcached来最小化我的数据库访问。
  7. 我想最小化/消除在我的数据库中存储重复或派生信息

下图显示了我的网格状态的示例。

在此处输入图像描述

我的代码:

import memcache
mc = memcache.Client(['127.0.0.1:11211'], debug=0)

class Cell(models.Model):
    x = models.IntegerField(editable=False)
    y = models.IntegerField(editable=False)

    # Whenever this value is updated, the keys for the row and column need to be 
    # invalidated. But not sure exactly how I should manage that.
    value = models.IntegerField()


class Row(models.Model):
    y = models.IntegerField()

    @property
    def cummulative_score(self):
        # I need to do some memcaching here.
        # But not sure the smartest way to do it.
        return sum(map(lambda p: p.x * p.value, Cell.objects.filter(y=self.y)))

class Column(models.Model):
    x = models.IntegerField()

    @property
    def cummulative_score(self):
        # I need to do some memcaching here.
        # But not sure the smartest way to do it.
        return sum(map(lambda p: p.y * p.value, Cell.objects.filter(x=self.x)))

所以这是我的问题:

你可以看到我已经设置了一个memcached实例。当然我知道如何在memcached. 但是鉴于我上面的代码,我应该如何正确命名键?如果键名是固定的,它将不起作用,因为每一行和每一列都必须存在单独的键。至关重要的是,当单元格中的值更新时,如何确保适当的键(并且只有适当的键)无效?

每当有人更新 Cell.values 以最小化数据库访问时,我如何管理缓存失效?没有一些 django 中间件可以为我处理这个簿记吗?我看到的文件没有这样做。

4

3 回答 3

1

如果要缓存单个行/列组合,则应将对象 ID 附加到键名。

给定 ax, y 变量:

key = 'x={}_y={}'.format(x, y)

我会使用表名并附加 id,行 id 可以只是表 PK,列 id 可以只是列名,就像这样

key = '{}_{}_{}'.format(table_name, obj.id, column_name)

无论如何,我建议考虑缓存整行而不是单个单元格

于 2014-11-25T11:56:24.700 回答
1

当保存模型对象时,对象Cell可以使其缓存值无效。RowColumn

Row这里Column是普通对象,不是 Django 模型,但是如果出于某种原因需要将它们存储在数据库中,当然可以更改它。)

import memcache
mc = memcache.Client(['127.0.0.1:11211'], debug=0)

class Cell(models.Model):
    x = models.IntegerField(editable=False)
    y = models.IntegerField(editable=False)

    # Whenever this value is updated, the keys for the row and column need to be 
    # invalidated. But not sure exactly how I should manage that.
    value = models.IntegerField()

    def invalidate_cache(self):
        Row(self.y).invalidate_cache()
        Column(self.x).invalidate_cache()

    def save(self, *args, **kwargs):
        super(Cell, self).save(*args, **kwargs)
        self.invalidate_cache()


class Row(object):
    def __init__(self, y):
        self.y = y

    @property
    def cache_key(self):
        return "row_{}".format(self.y)

    @property
    def cumulative_score(self):
        score = mc.get(self.cache_key)
        if not score:
            score = sum(map(lambda p: p.x * p.value, Cell.objects.filter(y=self.y)))
            mc.set(self.cache_key, score)
        return score

    def invalidate_cache(self):
        mc.delete(self.cache_key)


class Column(object):
    def __init__(self, x):
        self.x = x

    @property
    def cache_key(self):
        return "column_{}".format(self.x)

    @property
    def cumulative_score(self):
        score = mc.get(self.cache_key)
        if not score:
            score = sum(map(lambda p: p.y * p.value, Cell.objects.filter(x=self.x)))
            mc.set(self.cache_key, score)
        return score

    def invalidate_cache(self):
        mc.delete(self.cache_key)
于 2014-12-03T01:44:29.467 回答
1
# your client, be it memcache or redis, assign to client variable
# I think both of them use set without TTL for permanent values.

class Cell(models.Model):
    x = models.IntegerField(editable=False)
    y = models.IntegerField(editable=False)
    value = models.IntegerField()

    def save(self, *args, **kwargs):
        Cell.cache("row",self.y)
        Cell.cache("column",self.x)
        super(Cell, self).save(*args, **kwargs)

    @staticmethod
    def score(dimension, number):
        return client.get(dimension+str(number), False) or Cell.cache(number)

    @staticmethod
    def cache(dimension, number):
        if dimension == "row":
            val = sum([c.y * c.value for c in Cell.objects.filter(y=number)])
            client.set(dimension+str(self.y),val)
            return val

        if dimension == "column":
            val = sum([c.x * c.value for c in Cell.objects.filter(x=number)])
            client.set(dimension+str(self.x),val)
            return val

        raise Exception("No such dimension:"+str(dimension))
于 2014-11-29T17:10:51.927 回答