Redis总结(七)Redis运维常用命令

redis 服务器端命令

redis 127.0.0.1:6380> time  ,显示服务器时间 , 时间戳(秒), 微秒数

1) “1375270361”

2) “504511”

 

redis 127.0.0.1:6380> dbsize  // 当前数据库的key的数量

(integer) 2

redis 127.0.0.1:6380> select 2

OK

redis 127.0.0.1:6380[2]> dbsize

(integer) 0

redis 127.0.0.1:6380[2]>

 

 

BGREWRITEAOF 后台进程重写AOF

BGSAVE       后台保存rdb快照

SAVE         保存rdb快照

LASTSAVE     上次保存时间

 

Slaveof master-Host port  , 把当前实例设为master的slave

 

Flushall  清空所有库所有键

Flushdb  清空当前库所有键

Showdown [save/nosave]

 

注: 如果不小心运行了flushall, 立即 shutdown nosave ,关闭服务器

然后 手工编辑aof文件, 去掉文件中的 “flushall ”相关行, 然后开启服务器,就可以导入回原来数据.

 

如果,flushall之后,系统恰好bgrewriteaof了,那么aof就清空了,数据丢失.

 

Slowlog 显示慢查询

注:多慢才叫慢?

答: 由slowlog-log-slower-than 10000 ,来指定,(单位是微秒)

 

服务器储存多少条慢查询的记录?

答: 由 slowlog-max-len 128 ,来做限制

 

Info [Replication/CPU/Memory..]

查看redis服务器的信息

 

Config get 配置项

Config set 配置项 值 (特殊的选项,不允许用此命令设置,如slave-of, 需要用单独的slaveof命令来设置)

 

Redis运维时需要注意的参数

1: 内存

# Memory

used_memory:859192 数据结构的空间

used_memory_rss:7634944 实占空间

mem_fragmentation_ratio:8.89 前2者的比例,1.N为佳,如果此值过大,说明redis的内存的碎片化严重,可以导出再导入一次.

2: 主从复制

# Replication

role:slave

master_host:192.168.1.128

master_port:6379

master_link_status:up

 

3:持久化

# Persistence

rdb_changes_since_last_save:0

rdb_last_save_time:1375224063

 

4: fork耗时

#Status

latest_fork_usec:936  上次导出rdb快照,持久化花费微秒

注意: 如果某实例有10G内容,导出需要2分钟,

每分钟写入10000次,导致不断的rdb导出,磁盘始处于高IO状态.

 

 

5: 慢日志

config get/set slowlog-log-slower-than

CONFIG get/SET slowlog-max-len

slowlog get N 获取慢日志

 

运行时更改master-slave

修改一台slave(设为A)为new master

1) 命令该服务不做其他redis服务的slave

命令: slaveof no one

2) 修改其readonly为yes

 

其他的slave再指向new master A

1) 命令该服务为new master A的slave

命令格式 slaveof IP port

Redis总结(六)Redis配置文件全解

前面已经写了一些关于redis 的介绍,redis 的基本功能和用法,基本上都说了,有问题的可以去看看 http://www.cnblogs.com/zhangweizhong/category/771056.html。今天把redis 配置文件里面,用到的一些常用的配置项总结总结。

==基本配置

port 6379  # 监听端口号,默认为 6379,如果你设为 0 ,redis 将不在 socket 上监听任何客户端连接。

daemonize no #是否以后台进程启动

databases 16 #创建database的数量(默认选中的是database 0)

save 900 1 #刷新快照到硬盘中,必须满足两者要求才会触发,即900秒之后至少1个关键字发生变化。
save 300 10 #必须是300秒之后至少10个关键字发生变化。
save 60 10000 #必须是60秒之后至少10000个关键字发生变化。

stop-writes-on-bgsave-error yes #后台存储错误停止写。
rdbcompression yes #使用LZF压缩rdb文件。
rdbchecksum yes #存储和加载rdb文件时校验。
dbfilename dump.rdb #设置rdb文件名。
dir ./ #设置工作目录,rdb文件会写入该目录。

==主从配置
slaveof 设为某台机器的从服务器
masterauth 连接主服务器的密码
slave-serve-stale-data yes # 当主从断开或正在复制中,从服务器是否应答
slave-read-only yes #从服务器只读
repl-ping-slave-period 10 #从ping主的时间间隔,秒为单位
repl-timeout 60 #主从超时时间(超时认为断线了),要比period大
slave-priority 100 #如果master不能再正常工作,那么会在多个slave中,选择优先值最小的一个slave提升为master,优先值为0表示不能提升为master。

repl-disable-tcp-nodelay no #主端是否合并数据,大块发送给slave
slave-priority 100 从服务器的优先级,当主服挂了,会自动挑slave priority最小的为主服

===安全
requirepass foobared # 需要密码
rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 #如果公共环境,可以重命名部分敏感命令 如config

 

===限制
maxclients 10000 #最大连接数
maxmemory #最大使用内存

maxmemory-policy volatile-lru #内存到极限后的处理
volatile-lru -> LRU算法删除过期key
allkeys-lru -> LRU算法删除key(不区分过不过期)
volatile-random -> 随机删除过期key
allkeys-random -> 随机删除key(不区分过不过期)
volatile-ttl -> 删除快过期的key
noeviction -> 不删除,返回错误信息

#解释 LRU ttl都是近似算法,可以选N个,再比较最适宜T踢出的数据
maxmemory-samples 3

====日志模式
appendonly no #是否仅要日志
appendfsync no # 系统缓冲,统一写,速度快
appendfsync always # 系统不缓冲,直接写,慢,丢失数据少
appendfsync everysec #折衷,每秒写1次

no-appendfsync-on-rewrite no #为yes,则其他线程的数据放内存里,合并写入(速度快,容易丢失的多)
auto-AOF-rewrite-percentage 100 当前aof文件是上次重写是大N%时重写
auto-AOF-rewrite-min-size 64mb aof重写至少要达到的大小

====慢查询
slowlog-log-slower-than 10000 #记录响应时间大于10000微秒的慢查询
slowlog-max-len 128 # 最多记录128条

====服务端命令
time 返回时间戳+微秒
dbsize 返回key的数量
bgrewriteaof 重写aof
bgsave 后台开启子进程dump数据
save 阻塞进程dump数据
lastsave

slaveof host port 做host port的从服务器(数据清空,复制新主内容)
slaveof no one 变成主服务器(原数据不丢失,一般用于主服失败后)

flushdb 清空当前数据库的所有数据
flushall 清空所有数据库的所有数据(误用了怎么办?)

shutdown [save/nosave] 关闭服务器,保存数据,修改AOF(如果设置)

slowlog get 获取慢查询日志
slowlog len 获取慢查询日志条数
slowlog reset 清空慢查询

info []

config get 选项(支持*通配)
config set 选项 值
config rewrite 把值写到配置文件
config restart 更新info命令的信息

debug object key #调试选项,看一个key的情况
debug segfault #模拟段错误,让服务器崩溃
object key (refcount|encoding|idletime)
monitor #打开控制台,观察命令(调试用)
client list #列出所有连接
client kill #杀死某个连接 CLIENT KILL 127.0.0.1:43501
client getname #获取连接的名称 默认nil
client setname “名称” #设置连接名称,便于调试

 

====连接命令===
auth 密码 #密码登陆(如果有密码)
ping #测试服务器是否可用
echo “some content” #测试服务器是否正常交互
select 0/1/2… #选择数据库
quit #退出连接

 

Redis总结(五)缓存雪崩和缓存穿透等问题

  前面讲过一些redis 缓存的使用和数据持久化。感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhong/category/771056.html 。今天总结总结缓存使用过程中遇到的一些常见的问题。比如缓存雪崩,缓存穿透,缓存预热等等。

缓存雪崩

  缓存雪崩是由于原有缓存失效(过期),新缓存未到期间。所有请求都去查询数据库,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。

  1. 碰到这种情况,一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。

        public object GetProductListNew()
        {
            const int cacheTime = 30;
            const string cacheKey = "product_list";
            const string lockKey = cacheKey;
            
            var cacheValue = CacheHelper.Get(cacheKey);
            if (cacheValue != null)
            {
                return cacheValue;
            }
            else
            {
                lock (lockKey)
                {
                    cacheValue = CacheHelper.Get(cacheKey);
                    if (cacheValue != null)
                    {
                        return cacheValue;
                    }
                    else
                    {
                        cacheValue = GetProductListFromDB(); //这里一般是 sql查询数据。              
                        CacheHelper.Add(cacheKey, cacheValue, cacheTime);
                    }                    
                }
                return cacheValue;
            }
        }    
      

 

  2. 加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。假设在高并发下,缓存重建期间key是锁着的,这是过来1000个请求999个都在阻塞的。同样会导致用户等待超时,这是个治标不治本的方法。

  还有一个解决办法解决方案是:给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。

     public object GetProductListNew()
        {
            const int cacheTime = 30;
            const string cacheKey = "product_list";
            //缓存标记。
            const string cacheSign = cacheKey + "_sign";
            
            var sign = CacheHelper.Get(cacheSign);
            //获取缓存值
            var cacheValue = CacheHelper.Get(cacheKey);
            if (sign != null)
            {
                return cacheValue; //未过期,直接返回。
            }
            else
            {
                CacheHelper.Add(cacheSign, "1", cacheTime);
                ThreadPool.QueueUserWorkItem((arg) =>
                {
                    cacheValue = GetProductListFromDB(); //这里一般是 sql查询数据。
                    CacheHelper.Add(cacheKey, cacheValue, cacheTime*2); //日期设缓存时间的2倍,用于脏读。                
                });
                
                return cacheValue;
            }
        } 

  缓存标记:记录缓存数据是否过期,如果过期会触发通知另外的线程在后台去更新实际key的缓存。

  缓存数据:它的过期时间比缓存标记的时间延长1倍,例:标记缓存时间30分钟,数据缓存设置为60分钟。 这样,当缓存标记key过期后,实际缓存还能把旧数据返回给调用端,直到另外的线程在后台更新完成后,才会返回新缓存。

  这样做后,就可以一定程度上提高系统吞吐量。

缓存穿透

  缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。

  解决的办法就是:如果查询数据库也为空,直接设置一个默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。

        public object GetProductListNew()
        {
            const int cacheTime = 30;
            const string cacheKey = "product_list";

            var cacheValue = CacheHelper.Get(cacheKey);
            if (cacheValue != null)
                return cacheValue;
                
            cacheValue = CacheHelper.Get(cacheKey);
            if (cacheValue != null)
            {
                return cacheValue;
            }
            else
            {
                cacheValue = GetProductListFromDB(); //数据库查询不到,为空。
                
                if (cacheValue == null)
                {
                    cacheValue = string.Empty; //如果发现为空,设置个默认值,也缓存起来。                
                }
                CacheHelper.Add(cacheKey, cacheValue, cacheTime);
                
                return cacheValue;
            }
        }    

  把空结果,也给缓存起来,这样下次同样的请求就可以直接返回空了,即可以避免当查询的值为空时引起的缓存穿透。同时也可以单独设置个缓存区域存储空值,对要查询的key进行预先校验,然后再放行给后面的正常缓存处理逻辑。

缓存预热

  缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样避免,用户请求的时候,再去加载相关的数据。

  解决思路:

    1,直接写个缓存刷新页面,上线时手工操作下。

    2,数据量不大,可以在WEB系统启动的时候加载。

    3,定时刷新缓存,

缓存更新

  缓存淘汰的策略有两种:

    (1) 定时去清理过期的缓存。

    (2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。 

  两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂,具体用哪种方案,大家可以根据自己的应用场景来权衡。1. 预估失效时间 2. 版本号(必须单调递增,时间戳是最好的选择)3. 提供手动清理缓存的接口。

  我前面有篇文章,是介绍缓存系统的缓存更新的。感兴趣的朋友可以看看:http://www.cnblogs.com/zhangweizhong/p/5884761.html

总结

  这些都是实际项目中,可能碰到的一些问题。实际上还有很多很多各种各样的问题。缓存层框架的封装往往要复杂的多。应用场景不同,方法和解决方案也不同。具体要根据实际情况来取舍。  

 

redis总结(四)redis的持久化RDB和AOF

前面已经总结了Redis 的安装和使用今天讲下Redis 的持久化。

redis跟memcached类似,都是内存数据库,不过redis支持数据持久化,也就是说redis可以将内存中的数据同步到磁盘来持久化,以确保redis 的数据安全。

 

 redis持久化的两种方式

redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File)。

RDB,简而言之,就是将存储的数据快照的方式存储到磁盘上,

AOF,则是将redis执行过的所有写指令记录下来,通过write函数追加到AOF文件的末尾。在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。

 

其实RDB和AOF两种方式也可以同时使用,在这种情况下,如果redis重启的话,则会优先采用AOF方式来进行数据恢复,这是因为AOF方式的数据恢复完整度更高。

如果你没有数据持久化的需求,也完全可以关闭RDB和AOF方式,这样的话,redis将变成一个纯内存数据库,就像memcache一样。

 

 RDB

RDB(Redis DataBase),是将redis某一时刻的数据持久化到磁盘中,是一种快照式的持久化方法。默认的文件名为dump.rdb。

redis在进行数据持久化的过程中,会先将数据写入到一个临时文件中,待持久化过程都结束了,才会用这个临时文件替换上次持久化好的文件,以确保数据完整可用。

  save 300 10  #300秒内容如超过10个key被修改,则发起快照保存

  

不过,由于快照方式是在一定间隔时间做一次的,如果对数据的完整性非常敏感,那么RDB方式就不太适合你,因为即使你每2分钟都持久化一次,当redis故障时,仍然会有近2分钟的数据丢失。所以,redis还提供了另一种持久化方式,那就是AOF。


 

 AOF

AOF(Append Only File),即只允许追加不允许改写的文件。

Redis会将收到的每一个写操作(如SET等)通过write函数追加到AOF文件的末尾。默认的AOF持久化策略是每秒钟fsync一次(把缓存中的写指令记录到磁盘中)。

 

当Redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。

  appendonly yes           #启用aof持久化方式
  # appendfsync always    #每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用
  appendfsync everysec     #每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐
  # appendfsync no       #完全依赖os,性能最好,持久化没保证

但AOF方式是将所有的命令记录下来,所以AOF文件要比RDB文件的体积大。而且,恢复速度也要慢于RDB方式。

 

redis提供了bgrewriteaof命令,会重新生成一个全新的AOF文件,其中便包括了可以恢复现有数据的最少的命令集。

 

需要注意到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。

 

 如何选择RDB和AOF

对于我们应该选择RDB还是AOF,取决于具体的应用场景,官方的建议是两个同时使用。这样可以提供更可靠的持久化方案。

 

Redis总结(三)Redis 的主从复制

接着上一篇,前面两篇我总结了《Redis总结(一)Redis安装》和《Redis总结(二)C#中如何使用redis》 所以这一篇,会讲讲Redis 的主从复制以及C#中如何调用。

Redis跟MySQL一样,拥有非常强大的主从复制功能,而且还支持一个master可以拥有多个slave,而一个slave又可以拥有多个slave,从而形成强大的多级服务器集群架构。

redis的主从复制是异步进行的,它不会影响master的运行,所以不会降低redis的处理性能。主从架构中,可以考虑关闭Master的数据持久化功能,只让Slave进行持久化,这样可以提高主服务器的处理性能。同时Slave为只读模式,这样可以避免Slave缓存的数据被误修改。

 1.配置

实际生产中,主从架构是在几个不同服务器上安装相应的Redis服务。为了测试方便,我这边的主从备份的配置,都是在我Windows 本机上测试。

1. 安装两个Redis 实例,Master和Slave。将Master端口设置为6379,Slave 端口设置为6380 。bind 都设置为:127.0.0.1。 具体Redis安装步骤,请参考前一篇博文 《Redis总结(一)Redis安装》。

2. 在Slave 实例 ,增加:slaveof 127.0.0.1 6379 配置。如下图所示:

    

 

    配置完成之后,启动这两个实例,如果输出如下内容,说明主从复制的架构已经配置成功了。

 

注意:在同一台电脑上测试,Master和Slave的端口不要一样,否则是不能同时启动两个实例的。

 2.测试

在命令行,分别连接上Master服务器和Slave 服务器。然后在Master 写入缓存,然后在Slave 中读取。如下图所示:

 3.C#中调用

主从架构的Redis的读写其实和单台Redis 的读写差不多,只是部分配置和读取区分了主从,如果不清楚C#中如何使用redis,请参考我这篇文章 《Redis总结(二)C#中如何使用redis》。

需要注意的是:ServiceStack.Redis 中GetClient()方法,只能拿到Master redis中获取连接,而拿不到slave 的readonly连接。这样 slave起到了冗余备份的作用,读的功能没有发挥出来,如果并发请求太多的话,则Redis的性能会有影响。

    所以,我们需要的写入和读取的时候做一个区分,写入的时候,调用client.GetClient() 来获取writeHosts的Master的redis 链接。读取,则调用client.GetReadOnlyClient()来获取的readonlyHost的 Slave的redis链接。

或者可以直接使用client.GetCacheClient() 来获取一个连接,他会在写的时候调用GetClient获取连接,读的时候调用GetReadOnlyClient获取连接,这样可以做到读写分离,从而利用redis的主从复制功能。

1. 配置文件 app.config

    
    "SessionExpireMinutes" value="180" />
    "redis_server_master_session" value="127.0.0.1:6379" />
    "redis_server_slave_session" value="127.0.0.1:6380" />
    "redis_max_read_pool" value="300" />
    "redis_max_write_pool" value="100" />
    

 

2. Redis操作的公用类RedisCacheHelper

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Web;
using ServiceStack.Common.Extensions;
using ServiceStack.Redis;
using ServiceStack.Logging;

namespace Weiz.Redis.Common 
{
    public class RedisCacheHelper
    {
        private static readonly PooledRedisClientManager pool = null;
        private static readonly string[] writeHosts = null;
        private static readonly string[] readHosts = null;
        public static int RedisMaxReadPool = int.Parse(ConfigurationManager.AppSettings["redis_max_read_pool"]);
        public static int RedisMaxWritePool = int.Parse(ConfigurationManager.AppSettings["redis_max_write_pool"]);
        static RedisCacheHelper()
        {
            var redisMasterHost = ConfigurationManager.AppSettings["redis_server_master_session"];
            var redisSlaveHost = ConfigurationManager.AppSettings["redis_server_slave_session"];

            if (!string.IsNullOrEmpty(redisMasterHost))
            {
                writeHosts = redisMasterHost.Split(',');
                readHosts = redisSlaveHost.Split(',');

                if (readHosts.Length > 0)
                {
                    pool = new PooledRedisClientManager(writeHosts, readHosts,
                        new RedisClientManagerConfig()
                        {
                            MaxWritePoolSize = RedisMaxWritePool,
                            MaxReadPoolSize = RedisMaxReadPool,
                            
                            AutoStart = true
                        });
                }
            }
        }
        public static void Add(string key, T value, DateTime expiry)
        {
            if (value == null)
            {
                return;
            }

            if (expiry <= DateTime.Now)
            {
                Remove(key);

                return;
            }

            try
            {
                if (pool != null)
                {
                    using (var r = pool.GetClient())
                    {
                        if (r != null)
                        {
                            r.SendTimeout = 1000;
                            r.Set(key, value, expiry - DateTime.Now);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string msg = string.Format("{0}:{1}发生异常!{2}", "cache", "存储", key);
            }

        }

        public static void Add(string key, T value, TimeSpan slidingExpiration)
        {
            if (value == null)
            {
                return;
            }

            if (slidingExpiration.TotalSeconds <= 0)
            {
                Remove(key);

                return;
            }

            try
            {
                if (pool != null)
                {
                    using (var r = pool.GetClient())
                    {
                        if (r != null)
                        {
                            r.SendTimeout = 1000;
                            r.Set(key, value, slidingExpiration);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string msg = string.Format("{0}:{1}发生异常!{2}", "cache", "存储", key);
            }

        }



        public static T Get(string key)
        {
            if (string.IsNullOrEmpty(key))
            {
                return default(T);
            }

            T obj = default(T);

            try
            {
                if (pool != null)
                {
                    using (var r = pool.GetClient())
                    {
                        if (r != null)
                        {
                            r.SendTimeout = 1000;
                            obj = r.Get(key);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string msg = string.Format("{0}:{1}发生异常!{2}", "cache", "获取", key);
            }


            return obj;
        }

        public static void Remove(string key)
        {
            try
            {
                if (pool != null)
                {
                    using (var r = pool.GetClient())
                    {
                        if (r != null)
                        {
                            r.SendTimeout = 1000;
                            r.Remove(key);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string msg = string.Format("{0}:{1}发生异常!{2}", "cache", "删除", key);
            }

        }

        public static bool Exists(string key)
        {
            try
            {
                if (pool != null)
                {
                    using (var r = pool.GetClient())
                    {
                        if (r != null)
                        {
                            r.SendTimeout = 1000;
                            return r.ContainsKey(key);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string msg = string.Format("{0}:{1}发生异常!{2}", "cache", "是否存在", key);
            }

            return false;
        }

        public static IDictionary<string, T> GetAll(IEnumerable<string> keys) where T : class
        {
            if (keys == null)
            {
                return null;
            }

            keys = keys.Where(k => !string.IsNullOrWhiteSpace(k));

            if (keys.Count() == 1)
            {
                T obj = Get(keys.Single());

                if (obj != null)
                {
                    return new Dictionary<string, T>() { { keys.Single(), obj } };
                }

                return null;
            }

            if (!keys.Any())
            {
                return null;
            }

            IDictionary<string, T> dict = null;

            if (pool != null)
            {
                keys.Select(s => new
                {
                    Index = Math.Abs(s.GetHashCode()) % readHosts.Length,
                    KeyName = s
                })
                .GroupBy(p => p.Index)
                .Select(g =>
                {
                    try
                    {
                        using (var r = pool.GetClient(g.Key))
                        {
                            if (r != null)
                            {
                                r.SendTimeout = 1000;
                                return r.GetAll(g.Select(p => p.KeyName));
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        string msg = string.Format("{0}:{1}发生异常!{2}", "cache", "获取", keys.Aggregate((a, b) => a + "," + b));
                    }
                    return null;
                })
                .Where(x => x != null)
                .ForEach(d =>
                {
                    d.ForEach(x =>
                    {
                        if (dict == null || !dict.Keys.Contains(x.Key))
                        {
                            if (dict == null)
                            {
                                dict = new Dictionary<string, T>();
                            }
                            dict.Add(x);
                        }
                    });
                });
            }

            IEnumerable<Tuple<string, T>> result = null;

            if (dict != null)
            {
                result = dict.Select(d => new Tuple<string, T>(d.Key, d.Value));
            }
            else
            {
                result = keys.Select(key => new Tuple<string, T>(key, Get(key)));
            }

            return result
                .Select(d => new Tuple<string[], T>(d.Item1.Split('_'), d.Item2))
                .Where(d => d.Item1.Length >= 2)
                .ToDictionary(x => x.Item1[1], x => x.Item2);
        }
    }
}

View Code

 

Redis总结(二)C#中如何使用redis

  上一篇讲述了安装redis《Redis总结(一)Redis安装》,同时也大致介绍了redis的优势和应用场景。本篇着重讲解.NET中如何使用redis和C#。

  Redis官网提供了很多开源的C#客户端。例如,Nhiredis ,ServiceStack.Redis ,StackExchange.Redis等。其中ServiceStack.Redis应该算是比较流行的。它提供了一整套从Redis数据结构都强类型对象转换的机制并将对象json序列化。所以这里只介绍ServiceStack.Redis,它也是目前我们产品中所使用的客户端。

  ServiceStack.Redis地址https://github.com/ServiceStack/ServiceStack.Redis

  1. 建立一个控制台应用程序,并引用以下ServiceStack.Redis相关的四个类库。或者通过Nuget进行安装Redis常用组件ServiceStack.Redis。 下载示例代码

 

2. 创建一个Redis操作的公用类RedisCacheHelper,

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Web;
using ServiceStack.Common.Extensions;
using ServiceStack.Redis;
using ServiceStack.Logging;

namespace Weiz.Redis.RedisTest
{
    public class RedisCacheHelper
    {
        private static readonly PooledRedisClientManager pool = null;
        private static readonly string[] redisHosts = null;
        public static int RedisMaxReadPool = int.Parse(ConfigurationManager.AppSettings["redis_max_read_pool"]);
        public static int RedisMaxWritePool = int.Parse(ConfigurationManager.AppSettings["redis_max_write_pool"]);

        static RedisCacheHelper()
        {
            var redisHostStr = ConfigurationManager.AppSettings["redis_server_session"];

            if (!string.IsNullOrEmpty(redisHostStr))
            {
                redisHosts = redisHostStr.Split(',');

                if (redisHosts.Length > 0)
                {
                    pool = new PooledRedisClientManager(redisHosts, redisHosts,
                        new RedisClientManagerConfig()
                        {
                            MaxWritePoolSize = RedisMaxWritePool,
                            MaxReadPoolSize = RedisMaxReadPool,
                            AutoStart = true
                        });
                }
            }
        }
        public static void Add(string key, T value, DateTime expiry)
        {
            if (value == null)
            {
                return;
            }

            if (expiry <= DateTime.Now)
            {
                Remove(key);

                return;
            }

            try
            {
                if (pool != null)
                {
                    using (var r = pool.GetClient())
                    {
                        if (r != null)
                        {
                            r.SendTimeout = 1000;
                            r.Set(key, value, expiry - DateTime.Now);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string msg = string.Format("{0}:{1}发生异常!{2}", "cache", "存储", key);
            }

        }

        public static void Add(string key, T value, TimeSpan slidingExpiration)
        {
            if (value == null)
            {
                return;
            }

            if (slidingExpiration.TotalSeconds <= 0)
            {
                Remove(key);

                return;
            }

            try
            {
                if (pool != null)
                {
                    using (var r = pool.GetClient())
                    {
                        if (r != null)
                        {
                            r.SendTimeout = 1000;
                            r.Set(key, value, slidingExpiration);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string msg = string.Format("{0}:{1}发生异常!{2}", "cache", "存储", key);
            }

        }



        public static T Get(string key)
        {
            if (string.IsNullOrEmpty(key))
            {
                return default(T);
            }

            T obj = default(T);

            try
            {
                if (pool != null)
                {
                    using (var r = pool.GetClient())
                    {
                        if (r != null)
                        {
                            r.SendTimeout = 1000;
                            obj = r.Get(key);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string msg = string.Format("{0}:{1}发生异常!{2}", "cache", "获取", key);
            }


            return obj;
        }

        public static void Remove(string key)
        {
            try
            {
                if (pool != null)
                {
                    using (var r = pool.GetClient())
                    {
                        if (r != null)
                        {
                            r.SendTimeout = 1000;
                            r.Remove(key);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string msg = string.Format("{0}:{1}发生异常!{2}", "cache", "删除", key);
            }

        }

        public static bool Exists(string key)
        {
            try
            {
                if (pool != null)
                {
                    using (var r = pool.GetClient())
                    {
                        if (r != null)
                        {
                            r.SendTimeout = 1000;
                            return r.ContainsKey(key);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string msg = string.Format("{0}:{1}发生异常!{2}", "cache", "是否存在", key);
            }

            return false;
        }
    }
}

说明:RedisCacheHelper 使用的是客户端链接池模式,这样的存取效率应该是最高的。同时也更方便的支持读写分离,均衡负载。

 

3. 配置文件

    
    
    
    
    
    

4. 测试程序调用

class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Redis写入缓存:zhong");

            RedisCacheHelper.Add("zhong", "zhongzhongzhong", DateTime.Now.AddDays(1));

            Console.WriteLine("Redis获取缓存:zhong");

            string str3 = RedisCacheHelper.Get("zhong");

            Console.WriteLine(str3);

            Console.WriteLine("Redis获取缓存:nihao");
            string str = RedisCacheHelper.Get("nihao");
            Console.WriteLine(str);


            Console.WriteLine("Redis获取缓存:wei");
            string str1 = RedisCacheHelper.Get("wei");
            Console.WriteLine(str1);

            Console.ReadKey();
        }
    }

5. 输出结果

Redis总结(一)Redis安装

最近项目中需要使用Redis,刚好这两天有时间,便总结记录一下Redis的安装,以及如何在.NET中使用Redis。

 

Redis是一个用的比较广泛的Key/Value的内存数据库。目前新浪微博、Github、StackOverflow 等大型应用中都用其作为缓存,和Memcached类似,但是支持数据的持久化,解决了断电后数据完全丢失的情况。而且它支持更多的类型,除了string外,还支持lists(链表)、sets(集合)和zsets(有序集合)几种数据类型。

 

Redis的官网为: http://redis.io/

 

 1.Redis安装

redis的安装非常的简单,而且Redis并不依赖其他环境和标准库,很容易上手,这可能也是它流行的一个原因。这里为了测试方便,用的都是windows 环境下测试。下载Windows版本Redis

redis.windows.conf 是redis的配置文件。

redis-server.exe  服务器端。

redis-cli  命令行客户端。

redis-benchmark:Redis性能测试工具,测试Redis在你的系统及你的配置下的读写性能。

 

 2.启动服务

在命令行输入如下命令 :redis-server  redis.windows.conf。

同时也可以该命令保存为文件 startup.bat,下次就可以直接启动了。

如果提示redis-server 不是内部命令。将该目录加到环境变量里面即可。

 

3. redis相关配置

1. port 端口号,例如6379

2. bind 实例绑定的访问地址127.0.0.1

3. requirepass 访问的密码

4. maxheap 记得把这个配置节点打开,否者redis 服务无法启动。例如maxheap 1024000000

5. timeout:请求超时时间

6. logfile:log文件位置

7. databases:开启数据库的数量

8. dbfilename:数据快照文件名(只是文件名,不包括目录)

 

 4. 连接测试

在命令行输入如下命令:redis-cli –h 127.0.0.1 –p 6379

参数分别为host、port,如果设置了密码,则必须要加上-a 123456,123456为登录密码。否则会提示没有权限登录系统。

如下图所示。