Memcache Key 设计技巧及注意事项

简介: 刚刚开始研究Memcache,觉得Memcache Key的设计其实是十分重要的,搜了搜,感觉资料不是很多,下面这些资料主要是从官网上获得的,有些地方可能不太精确,仅供参考。 初始化Memcache Client # perlmy $memclient = Cache::Memcached->new({ servers => [ '10.0.0.10:112
刚刚开始研究Memcache,觉得Memcache Key的设计其实是十分重要的,搜了搜,感觉资料不是很多,下面这些资料主要是从官网上获得的,有些地方可能不太精确,仅供参考。

初始化Memcache Client

# perl
my $memclient = Cache::Memcached->new({ servers => [ '10.0.0.10:11211', '10.0.0.11:11211' ]});
# pseudocode
memcli = new Memcache
memcli:add_server('10.0.0.10:11211')
     某些客户端的实现可能允许重复添加同一个Server,但最好每个Server是有一个实例,而且也要注意他们的顺序最好不要经常变化,防止key的重新映射;在初始化MemcacheClient的时候要注意实例化的次数,不要太过频繁的实例化,最好通过连接池来管理。

封装一个SQL作为key

     Memcache在减轻数据库负载方面发挥着重要的作用,我们最常使用的场景也是用它来减少数据库查询次数,下面这个例子就是通过Cache缓存查询数据库结果的场景,把sql+userId作为一个key,相同用户第二次查询时就可以从缓存中直接提取:
# Don't load little bobby tables
sql = "SELECT * FROM user WHERE user_id = ?"
key = 'SQL:' . user_id . ':' . md5sum(sql)
# We check if the value is 'defined', since '0' or 'FALSE' # can be 
# legitimate values!
if (defined result = memcli:get(key)) {
        return result
} else {
        handler = run_sql(sql, user_id)
        # Often what you get back when executing SQL is a special handler
        # object. You can't directly cache this. Stick to strings, arrays,
        # and hashes/dictionaries/tables
        rows_array = handler:turn_into_an_array
        # Cache it for five minutes
        memcli:set(key, rows_array, 5 * 60)
        return rows_array
}
     注意:在通过set方法缓存数据时,我们可以指定数据过期时间,上面例子中设置的是5分钟,这样的话,五分钟内用户看到的都是同样的信息,即5分钟内的数据变化用户是感觉不到的,在开发时一定要注意根据实际情况对过期时间进行合理设置。

封装多个查询SQL作为Key

     有些处理过程是复杂的,可能会用到多个sql,如果我们能把这些复杂的处理最后封装成一个key,这是最理想的结果。比如下面这个例子,我们把sql1+sql2+userId作为一个key,这样,我们两次查询的结果便封装成了一个CacheItem:
sql1 = "SELECT * FROM user WHERE user_id = ?"
sql2 = "SELECT * FROM user_preferences WHERE user_id = ?"
key  = 'SQL:' . user_id . ':' . md5sum(sql1 . sql2)
if (defined result = memcli:get(key)) {
        return result
} else {
        # Remember to add error handling, kids ;)
        handler = run_sql(sql1, user_id)
        t[info] = handler:turn_into_an_array
        handler = run_sql(sql2, user_id)
        t[pref] = handler:turn_into_an_array
        # Client will magically take this hash/table/dict/etc
        # and serialize it for us.
        memcli:set(key, t, 5 * 60)
        return t
}
     
缓存Object类型的数据    

     某些语言比如Java可以把对象进行序列化,进行序列化的对象就可以像普通字符串数据一样进行缓存了,但要注意的是,序列化和反序列化会耗费cpu时间,在缓存时仅仅序列化需要缓存对象会提高系统效率。

缓存一个片段(网页)

     Memcache不仅仅可以用来减少数据库查询,任何可以提高我们应用速度的地方我们都可以用它来解决,比如下面这个例子,加载一个用户模板是非常耗时的,但是我们可以把一个封装好的模板(甚至是整个网页)缓存起来:     
 
 
key  =  'FRAG-BIO:'  . user_id 
if (result = memcli:get(key)) {
        return result
} else {
        user         = fetch_user_info(user_id)
        bio_template = fetch_biotheme_for(user_id)
        bio_fragment = apply_template(bio_template, user)
        memcli:set(key, bio_fragment, 5 * 15)
        return bio_fragment
}

Get-By-Group-Key

      在某些情况下,我们希望一系列的key只存储到一台服务器缓存中,比如我们在显示一个用户首页的时候,需要缓存他的姓名、年龄、简历、好友、日志等信息,而通常情况下客户端Hash算法会把它们的key映射到集群中的每一台机器的缓存中,如果这样的话,我们在查询一个人的主页时会从多台机器上取数据,会耗掉很多网络传输、Hash映射等时间,这是完全没有必要的,我们需要一种机制,在客户端内部把这些key组成一个group-key(可以是userId)。

其它需要注意的地方
  • 数据有效性:某些时候我们需要保证对Cache中的数据进行同步,不要让用户感觉到自己用到的是过期的脏数据。
  • 数据过期:我们可以设置缓存中的数据过期时间,最大过期时间是30天,如果参数值为0,则表示永不过期(LRU算法会自动删除需要删除的数据)。
  • 删除Cache中数据:最直接的让Cache数据过期的办法就是delete。
  • Key的值越短越好:key的大小被限制在250个字节(将来可能达到6K),key的长度会影响Hash算法寻找value值的效率,也会浪费更多内存空间(期间有对key的copy)。

使用伪命名空间
     
     Memcache并不支持命名空间(其设计哲学就是简单高效),但是我们可以模拟实现它。比如,我们可以把一个用户的所有key都归档在一个命名空间下,比如可以把前缀设计为:user_namespace+userId。
user_prefix = memcli:get('user_namespace:' . user_id)
bio_data    = memcli:get(user_prefix . user_id . 'bio')
缓存Set 或 List

     存储集合到缓存的意思是把一个集合的所有数据作为一个CacheItem保存到缓存中,当然,具体的实现方式有多种。不过需要注意一点的是,Memcache的每个Item有不能超过1MB的限制,那么,我们该如何存储一个超大数据量的集合呢?

方案一:分二个阶段把数据缓存到Cache
     第一个阶段存取的不是所有集合的数据,而是集合数据每一项的Id,然后第二阶段再把每个Id对应的数据对象存到缓存中。当然,当一个集合特别大的时候,所有的数据的Id有可能也会超过1MB的限制,那么我们在第一阶段存储时指定每个集合的数量即可。这么做还有个好处就是当我们在更新一个数据条目的时候仅更新指定Id的项目即可。

方案二:一个阶段分批次把数据存取到Cache
     比如有30000条数据,我们可以每批次100条分300批次缓存到Cache。








目录
相关文章
|
NoSQL Java Redis
介绍Redis的各种用途以及使用场景
介绍Redis的各种用途以及使用场景 Redis 一、为什么使用 解决应用服务器的cpu和内存压力 减少io的读操作,减轻io的压力 关系型数据库的扩展性不强,难以改变表结构 二、优点: nosql数据库没有关联关系,数据结构简单,拓展表比较容易 nosql读取速度快,对较大数据.
11483 1
|
4月前
|
NoSQL Go Redis
Go语言中如何扫描Redis中大量的key
在Redis中,遍历大量键时直接使用`KEYS`命令会导致性能瓶颈,因为它会一次性返回所有匹配的键,可能阻塞Redis并影响服务稳定性。为解决此问题,Redis提供了`SCAN`命令来分批迭代键,避免一次性加载过多数据。本文通过两个Go语言示例演示如何使用`SCAN`命令:第一个示例展示了基本的手动迭代方式;第二个示例则利用`Iterator`简化迭代过程。这两种方法均有效地避免了`KEYS`命令的性能问题,并提高了遍历Redis键的效率。
64 0
|
6月前
|
存储 NoSQL 安全
Redis系列学习文章分享---第十五篇(Redis最佳实践--设计优雅的key+合适的数据结构+持久化如何配置+慢查询问题解决)
Redis系列学习文章分享---第十五篇(Redis最佳实践--设计优雅的key+合适的数据结构+持久化如何配置+慢查询问题解决)
99 1
|
6月前
|
NoSQL Redis 容器
Redis大Key问题 - 标准、原因、查找
【6月更文挑战第13天】**大Key标准**在不同场景各异,一般string超1MB或容器超10k元素视为大;高并发场景中,string超10KB,容器超5k或整体10MB。**阿里云Redis**中,大Key可能表现为String值5MB,ZSET成员10k,或Hash总值100MB。**大Key影响**包括高读取成本、操作阻塞、存储压力不均。**产生原因**多源于业务设计、动态增长管理和程序错误。**查找大Key**可通过云服务的实时/离线统计,`redis-cli --bigkeys`或使用Redis RDB Tools分析RDB文件。注意,某些特定需求可能需额外工具。
112 1
|
6月前
|
缓存 NoSQL 关系型数据库
Redis第二课,1.set key value(设置对应的key和value)2.get key(得到value值)Redis全局命令(支持很多的数据结构)3.keys(用来查询当前
Redis第二课,1.set key value(设置对应的key和value)2.get key(得到value值)Redis全局命令(支持很多的数据结构)3.keys(用来查询当前
|
7月前
|
NoSQL Shell Redis
批量迁移redis实例的key
批量迁移redis实例的key
|
7月前
|
存储 消息中间件 监控
Redis的数据类型以及如何解决大Key问题
在当今的数字化时代,数据成为了企业竞争力的核心要素之一。而Redis作为一种高效的内存数据结构,因其快速存取和丰富的数据类型特性被广泛应用于各类数据处理场景。在这篇文章中,我们将深入探讨Redis支持的数据类型以及如何解决大Key问题。通过了解Redis的数据类型以及相应的使用场景,我们可以更好地利用Redis的特性来满足各种数据存储需求。同时,对于大Key问题的解决策略,将帮助我们在处理大规模数据时提高性能和效率。让我们一起进入Redis的世界,探索其数据类型和大Key问题的解决方案。
254 1
|
7月前
|
存储 NoSQL Redis
②【Hash】Redis常用数据类型:Hash [使用手册]
②【Hash】Redis常用数据类型:Hash [使用手册]
78 0
|
存储 NoSQL Redis
15Redis - 存储set(使用场景)
15Redis - 存储set(使用场景)
55 0
|
NoSQL Redis Python
redis跨实例迁移key(Python 版)
测试区Codis集群之前废了,今天重新搭起来之后,测试程序发现很多key的数据都没了。 如果整个库迁移过来没有必要。 只需要把特定的几个key迁移过来即可。
197 0