【问题标题】:IMemoryCache Guaranteed Unique new Keys .NET-CoreIMemoryCache 保证唯一的新键 .NET-Core
【发布时间】:2017-07-16 17:51:02
【问题描述】:

我正在尝试使用 Microsoft.Extensions.Caching.Memory.IMemoryCache 接口/类。

我需要向缓存中添加一个新项目,并确保我不会覆盖已保存的任何其他内容。目前,所有密钥都是自动生成和随机生成的(不是顺序的)。

如何针对我当前的缓存项测试随机键的唯一性?

或者,我怎样才能获得保证唯一的密钥? 获得可用于以后检索的自动生成的密钥是可以的。

https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory 有几个例子,但对我生成的每个键使用 .TryGetValue(object key, object out value) 来测试唯一性似乎有点过分了,在考虑多线程环境时可能是个问题。

博客https://wildermuth.com/2016/04/14/Using-Cache-in-ASP-NET-Core-1-0-RC1 使用相同的 TryGetValue(key, out value) 模式来获取特意的 Key。

我希望不必将生成的密钥列表保存在其他地方,即另一个列表分隔列表,因为这会让我回到当它变老时修剪列表的问题。

作为额外的,在这种特定情况下,我将键作为 url-querystring 参数传递,因此与 url 兼容的字符串将是超级的。提前致谢。

【问题讨论】:

  • 你的意思是 Guid.NewGuid()?
  • 我的理解是Guids 不是保证唯一的。我当前对 GetRandomKey() 的实现是 Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("=","").Replace("+","").Replace("/"," ");但如果我可以检查当前的 keyList,我会睡得更好。
  • @Guid 他们是。这就是全球唯一的意思。
  • 另外,所有这些字符串操作的意义何在?既然缓存接受objects作为键,为什么不直接使用Guid呢?
  • 在这种特殊情况下,我将密钥作为 url-querystring 参数传递

标签: c# asp.net-mvc caching .net-core


【解决方案1】:

正如您已经发现的那样,将 GUID 用于缓存键并不是一个好的解决方案。主要问题是生成 GUID 后,没有办法可靠地将其重新生成为相同的键,以便将数据从缓存中取出。


通常,当我创建缓存时,键基于被缓存的实体或缓存它的方法。但也可以基于使值唯一的值组合来制作缓存。

使用实体的示例

public class Employee
{
    int Id { get; set; }
    string Name { get; set; }
}

要从实体中获取键,我们只需在实体的主键之外使用一个常量值。

private const string KEY_PREFIX = "Employee_";
private object syncLock = new object();

// innerEmployeeRetriever and cache are populated through the constructor
public Employee GetEmployee(int id)
{
    string key = KEY_PREFIX + id.ToString();

    // Get the employee from the cache
    var employee = cache[key];
    if (employee == null)
    {
        lock (syncLock)
        {
            // Double-check that another thread didn't beat us
            // to populating the cache
            var employee = cache[key];
            if (employee == null)
            {
                employee = innerEmployeeRetriever.GetEmployee(id);
                cache[key] = employee;
            }
        }
    }
    return employee;
}

使用方法名称的示例

private object syncLock = new object();

// innerEmployeeRetriever and cache are populated through the constructor
public Employee GetEmployeeList()
{
    string key = "GetEmployeeList";

    // Get the employee from the cache
    var employees = cache[key];
    if (employees == null)
    {
        lock (syncLock)
        {
            // Double-check that another thread didn't beat us
            // to populating the cache
            var employees = cache[key];
            if (employees == null)
            {
                employees = innerEmployeeRetriever.GetEmployeeList();
                cache[key] = employees;
            }
        }
    }
    return employees;
}

使用值组合的示例

您还可以从几个不同的值构建键,这些值组合起来使实体独一无二。如果您没有可使用的主键,或者您有几个不同的上下文要分别缓存,这将很有帮助。这个例子取自MvcSiteMapProvider

protected string GetCacheKey(string memberName)
{
    // NOTE: We must include IsReadOnly in the request cache key 
    // because we may have a different 
    // result when the sitemap is being constructed than when 
    // it is being read by the presentation layer.
    return "__MVCSITEMAPNODE_" + this.SiteMap.CacheKey + "_" + this.Key 
        + "_" + memberName + "_" + this.IsReadOnly.ToString() + "_";
}

在这种情况下,我们根据节点所属的父 SiteMap 的唯一键、节点的唯一键、方法或属性名称以及当前是否设置了只读标志来构建键。这些值的每一个唯一集合都会产生一个单独的缓存键,从而为每个组合创建一个单独的缓存。

当然,要使其正常工作,值之间应该有某种“安全”分隔符,以防止通过连接不同的值来创建相同的键。例如,"1" + "23""12" + "3" 是相同的字符串,但您可以通过使用下划线、竖线字符、逗号或其他一些不在数据本身中的分隔符来分隔值来防止此类冲突("1" + "_" + "23""12" + "_" + "3"不是相同的字符串)。


底线是缓存键必须以某种方式表示缓存中的什么才能使其有用。您的应用程序应了解如何提供构成密钥的数据,以便在需要再次检索相同数据时重新生成密钥。

【讨论】:

  • 非常感谢,这篇文章让我思考了很多。
【解决方案2】:

感谢 cmets 加强 Guid 的独特性:

我在 subejct 上进行了更多搜索,发现 Eric Lippert 的帖子澄清了我的担忧:https://ericlippert.com/2012/05/07/guid-guide-part-three/

  • GUID 保证是唯一的,但不保证是随机的。不要将它们用作随机数。

  • 作为随机数的 GUID 不是加密强度随机数。

  • GUID只有在大家合作的情况下才是唯一的;如果有人想重新使用以前生成的 GUID 从而人为地创建碰撞,您无法阻止他们。 GUID 不是一种安全机制。

由于我正在创建自己的密钥,因此如果我停止删除字符,我可以信任它们。

【讨论】:

    【解决方案3】:

    您始终可以使用GUID 作为唯一键,尽管理论上它们当然不是唯一的,因为它们的空间有限,但实际上它们的可能性不会小于陨石摧毁地球。所以使用它们是相当安全的。

    另一个问题是为什么要使用随机 id 作为键?当然有这样的情况,但通常数据有一些你可以使用的键。例如。如果您在博客引擎中有用户和帖子,您总是可以使用$"user_{user.key}"$"post_{post.key}" 或一些类似的方法来获得您的唯一密钥。您可以在前面加上完整的类名作为替代。如果数据库中的项目具有唯一键,则此方法将起作用。由于您尚未完全描述您的情况,因此很难判断它是否可能,但我宁愿在使用 GUID 之前尝试找到“自然键”。

    附:在 URL 中以这种方式使用 GUID 时要小心。这可能是也可能不是安全问题。 IE。如果您将所有用户的所有数据都放在缓存中并允许通过 URL 访问它,那么任何人都可以“猜测” guid 并访问他们不应该访问的内容。这并不容易,但可以做到这一点,因为正如您自己编写的那样,它们不能保证在密码学上足够随机。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-11
      • 1970-01-01
      • 2019-04-02
      • 2020-05-29
      相关资源
      最近更新 更多