9个提升逼格的redis命令(keys,scan,slowlog,rename-command,bigkeys,monitor,info,config,set)

 redis  9个提升逼格的redis命令(keys,scan,slowlog,rename-command,bigkeys,monitor,info,config,set)已关闭评论
9月 082020
 

9个提升逼格的redis命令

非常好的文章,特别是使用bigkeys查找占大内存的key真是方便,推荐https://www.jianshu.com/p/4df5f2356de9

keys

我把这个命令放在第一位,是因为笔者曾经做过的项目,以及一些朋友的项目,都因为使用keys这个命令,导致出现性能毛刺。这个命令的时间复杂度是O(N),而且redis又是单线程执行,在执行keys时即使是时间复杂度只有O(1)例如SET或者GET这种简单命令也会堵塞,从而导致这个时间点性能抖动,甚至可能出现timeout。

强烈建议生产环境屏蔽keys命令(后面会介绍如何屏蔽)。

scan

既然keys命令不允许使用,那么有什么代替方案呢?有!那就是scan命令。如果把keys命令比作类似select * from users where username like '%afei%'这种SQL,那么scan应该是select * from users where id>? limit 10这种命令。

官方文档用法如下:

SCAN cursor [MATCH pattern] [COUNT count]

初始执行scan命令例如scan 0。SCAN命令是一个基于游标的迭代器。这意味着命令每次被调用都需要使用上一次这个调用返回的游标作为该次调用的游标参数,以此来延续之前的迭代过程。当SCAN命令的游标参数被设置为0时,服务器将开始一次新的迭代,而当redis服务器向用户返回值为0的游标时,表示迭代已结束,这是唯一迭代结束的判定方式,而不能通过返回结果集是否为空判断迭代结束。

使用方式:

127.0.0.1:6380> scan 0
1) "22"
2)  1) "23"
    2) "20"
    3) "14"
    4) "2"
    5) "19"
    6) "9"
    7) "3"
    8) "21"
    9) "12"
   10) "25"
   11) "7"

返回结果分为两个部分:第一部分即1)就是下一次迭代游标,第二部分即2)就是本次迭代结果集。

slowlog

上面提到不能使用keys命令,如果就有开发这么做了呢,我们如何得知?与其他任意存储系统例如mysql,mongodb可以查看慢日志一样,redis也可以,即通过命令slowlog。用法如下:

SLOWLOG subcommand [argument]

subcommand主要有:

  • get,用法:slowlog get [argument],获取argument参数指定数量的慢日志。
  • len,用法:slowlog len,总慢日志数量。
  • reset,用法:slowlog reset,清空慢日志。

执行结果如下:

127.0.0.1:6380> slowlog get 5
1) 1) (integer) 2
   2) (integer) 1532656201
   3) (integer) 2033
   4) 1) "flushddbb"
2) 1) (integer) 1  ----  慢日志编码,一般不用care
   2) (integer) 1532646897  ----  导致慢日志的命令执行的时间点,如果api有timeout,可以通过对比这个时间,判断可能是慢日志命令执行导致的
   3) (integer) 26424  ----  导致慢日志执行的redis命令,通过4)可知,执行config rewrite导致慢日志,总耗时26ms+
   4) 1) "config"
      2) "rewrite"

命令耗时超过多少才会保存到slowlog中,可以通过命令config set slowlog-log-slower-than 2000配置并且不需要重启redis。注意:单位是微妙,2000微妙即2毫秒。

rename-command

为了防止把问题带到生产环境,我们可以通过配置文件重命名一些危险命令,例如keys等一些高危命令。操作非常简单,只需要在conf配置文件增加如下所示配置即可:

rename-command flushdb flushddbb
rename-command flushall flushallall
rename-command keys keysys

bigkeys

随着项目越做越大,缓存使用越来越不规范。我们如何检查生产环境上一些有问题的数据。bigkeys就派上用场了,用法如下:

redis-cli -p 6380 --bigkeys

执行结果如下:

... ...
-------- summary -------

Sampled 526 keys in the keyspace!
Total key length in bytes is 1524 (avg len 2.90)

Biggest string found 'test' has 10005 bytes
Biggest   list found 'commentlist' has 13 items

524 strings with 15181 bytes (99.62% of keys, avg size 28.97)
2 lists with 19 items (00.38% of keys, avg size 9.50)
0 sets with 0 members (00.00% of keys, avg size 0.00)
0 hashs with 0 fields (00.00% of keys, avg size 0.00)
0 zsets with 0 members (00.00% of keys, avg size 0.00)

最后5行可知,没有set,hash,zset几种数据结构的数据。string类型有524个,list类型有两个;通过Biggest ... ...可知,最大string结构的key是test,最大list结构的key是commentlist

需要注意的是,这个bigkeys得到的最大,不一定是最大。说明原因前,首先说明bigkeys的原理,非常简单,通过scan命令遍历,各种不同数据结构的key,分别通过不同的命令得到最大的key:

  • 如果是string结构,通过strlen判断;
  • 如果是list结构,通过llen判断;
  • 如果是hash结构,通过hlen判断;
  • 如果是set结构,通过scard判断;
  • 如果是sorted set结构,通过zcard判断。

正因为这样的判断方式,虽然string结构肯定可以正确的筛选出最占用缓存,也可以说最大的key。但是list不一定,例如,现在有两个list类型的key,分别是:numberlist–[0,1,2],stringlist–[“123456789123456789”],由于通过llen判断,所以numberlist要大于stringlist。而事实上stringlist更占用内存。其他三种数据结构hash,set,sorted set都会存在这个问题。使用bigkeys一定要注意这一点。

monitor

假设生产环境没有屏蔽keys等一些高危命令,并且slowlog中还不断有新的keys导致慢日志。那我们如何揪出这些命令是由谁执行的呢?这就是monitor的用处,用法如下:

redis-cli -p 6380 monitor

如果当前redis环境OPS比较高,那么建议结合linux管道命令优化,只输出keys命令的执行情况:

[[email protected] ~]# redis-cli -p 6380 monitor | grep keys 
1532645266.656525 [0 10.0.0.1:43544] "keyss" "*"
1532645287.257657 [0 10.0.0.1:43544] "keyss" "44*"

执行结果中很清楚的看到keys命名执行来源。通过输出的IP和端口信息,就能在目标服务器上找到执行这条命令的进程,揪出元凶,勒令整改。

info

如果说哪个命令能最全面反映当前redis运行情况,那么非info莫属。用法如下:

INFO [section]

section可选值有:

  • Server:运行的redis实例一些信息,包括:redis版本,操作系统信息,端口,GCC版本,配置文件路径等;
  • Clients:redis客户端信息,包括:已连接客户端数量,阻塞客户端数量等;
  • Memory:使用内存,峰值内存,内存碎片率,内存分配方式。这几个参数都非常重要;
  • Persistence:AOF和RDB持久化信息;
  • Stats:一些统计信息,最重要三个参数:OPS(instantaneous_ops_per_sec),keyspace_hitskeyspace_misses两个参数反应缓存命中率;
  • Replication:redis集群信息;
  • CPU:CPU相关信息;
  • Keyspace:redis中各个DB里key的信息;

config

config是一个非常有价值的命令,主要体现在对redis的运维。因为生产环境一般是不允许随意重启的,不能因为需要调优一些参数就修改conf配置文件并重启。redis作者早就想到了这一点,通过config命令能热修改一些配置,不需要重启redis实例,可以通过如下命令查看哪些参数可以热修改:

config get *

热修改就比较容易了,执行如下命令即可:

config set 

例如:config set slowlog-max-len 100config set maxclients 1024

这样修改的话,如果以后由于某些原因redis实例故障需要重启,那通过config热修改的参数就会被配置文件中的参数覆盖,所以我们需要通过一个命令将config热修改的参数刷到redis配置文件中持久化,通过执行如下命令即可:

config rewrite

执行该命令后,我们能在config文件中看到类似这种信息:

# 如果conf中本来就有这个参数,通过执行config set,那么redis直接原地修改配置文件
maxclients 1024
# 如果conf中没有这个参数,通过执行config set,那么redis会追加在Generated by CONFIG REWRITE字样后面
# Generated by CONFIG REWRITE
save 600 60
slowlog-max-len 100

set

set命令也能提升逼格?是的,我本不打算写这个命令,但是我见过太多人没有完全掌握这个命令,官方文档介绍的用法如下:

SET key value [EX seconds] [PX milliseconds] [NX|XX]

你可能用的比较多的就是set key value,或者SETEX key seconds value,所以很多同学用redis实现分布式锁分为两步:首先执行SETNX key value,然后执行EXPIRE key seconds。很明显,这种实现有很严重的问题,因为两步执行不具备原子性,如果执行第一个命令后出现某些未知异常导致无法执行EXPIRE key seconds,那么分布式锁就会一直无法得到释放。

通过SET命令实现分布式锁的正式姿势应该是SET key value EX seconds NX(EX和PX任选,取决于对过期时间精度要求)。另外,value也有要求,最好是一个类似UUID这种具备唯一性的字符串。当然如果问你redis是否还有其他实现分布式锁的方案。你能说出redlock,那对方一定眼前一亮,心里对你竖起大拇指,但嘴上不会说。

redis数据迁移的3个方法

 redis  redis数据迁移的3个方法已关闭评论
1月 172020
 

1. rdb数据备份恢复方法
redis 127.0.0.1:6379> SAVE
OK
或者
redis-cli -h 127.0.0.1 -p 6379 -a pwd bgsave
该命令将在 redis 安装目录中创建dump.rdb文件。

查找dump.rdb文件位置
redis 127.0.0.1:6379> CONFIG GET dir
1) dir
2) /usr/local/redis/bin
以上命令 CONFIG GET dir 输出的 redis 安装目录为 /usr/local/redis/bin
bgsave
创建 redis 备份文件也可以使用命令 BGSAVE,该命令在后台执行。
127.0.0.1:6379> BGSAVE
Background saving started

2. AOF数据备份恢复方法
另一种持久化方式AOF,在配置文件中打开[appendonly yes]。
AOF刷新日志到disk的规则:
appendfsync always #always 表示每次有写操作都进行同步,非常慢,非常安全。
appendfsync everysec #everysec表示对写操作进行累积,每秒同步一次
官方的建议的everysec,安全,就是速度不够快,如果是机器出现问题可能会丢失1秒的数据。

也可以手动执行bgrewriteaof进行AOF备份:
redis-cli -h 127.0.0.1 -p 6379 -a pwd bgrewriteaof

迁移数据恢复
迁移到另外一台恢复数据,需先检查配置文件,将按照以下优先级恢复数据到内存:
如果只配置AOF,重启时加载AOF文件恢复数据;
如果同时 配置了RBDAOF,启动是只加载AOF文件恢复数据;
如果只配置RBD,启动是讲加载dump文件恢复数据;

dump.rdb或者AOF文件迁移到另外一台恢复数据
恢复数据,只需将备份文件 (dump.rdb或者AOF文件) 移动到 redis 安装目录并启动服务即可。

3. redis从库复制数据方法
Redis提供了复制(replication)功能可以自动实现同步的过程。
配置方法
通过配置文件 从数据库的配置文件中加入slaveof master-ip master-port,主数据库无需配置
通过命令行参数 启动redis-server的时候,使用命令行参数–slaveof master-ip master port
redis-server –port 6380 –slaveof 127.0.0.1 6379
通过命令SLAVEOF master-ip master-port
redis>SLAVEOF 127.0.0.1 6379
SLAVEOF NO ONE可以是当前数据库停止接收其他数据库的同步,转成主Redis数据库,程序连接地址都改为新的redisIP地址和端口。

转自:https://my.oschina.net/ppabvc/blog/819448

mac 下 redis 安装、redis.conf位置、redis-server后台运行

 redis  mac 下 redis 安装、redis.conf位置、redis-server后台运行已关闭评论
11月 122019
 

mac下安装redis命令:

brew install redis

 

如果没有安装homebrew,可以打开一个终端,输入下面命令安装:

/usr/bin/ruby -e “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)”

 

启动:

redis-server 命令直接启动会运行在一个终端里,关闭终端服务就停止了,也没有运行参数可以后台运行,但可以通过修改配置文件参数实现。

mac下brew安装的redis的配置文件默认所在位置: /usr/local/etc/redis.conf

修改: 将 daemonize no 修改为 daemonize yes

 

再启动

redis-server /usr/local/etc/redis.conf

redis就会后台运行了

 

如果要开机启动,使用下面命令:

ln -sfv /usr/local/opt/redis/*.plist ~/Library/LaunchAgents

launchctl load ~/Library/LaunchAgents/homebrew.mxcl.redis.plist

brew services start redis

8月 222014
 

非常好的一篇关于jedis各类调用的文章,强烈推荐!

redis是一个著名的key-value存储系统,而作为其官方推荐的java版客户端jedis也非常强大和稳定,支持事务、管道及有jedis自身实现的分布式。

在这里对jedis关于事务、管道和分布式的调用方式做一个简单的介绍和对比:

一、普通同步方式

最简单和基础的调用方式,

@Test
public void test1Normal() {
    Jedis jedis = new Jedis("localhost");
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        String result = jedis.set("n" + i, "n" + i);
    }
    long end = System.currentTimeMillis();
    System.out.println("Simple SET: " + ((end - start)/1000.0) + " seconds");
    jedis.disconnect();
}

很简单吧,每次set之后都可以返回结果,标记是否成功。

二、事务方式(Transactions)

redis的事务很简单,他主要目的是保障,一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令。

看下面例子:

@Test
public void test2Trans() {
    Jedis jedis = new Jedis("localhost");
    long start = System.currentTimeMillis();
    Transaction tx = jedis.multi();
    for (int i = 0; i < 100000; i++) {
        tx.set("t" + i, "t" + i);
    }
    List<Object> results = tx.exec();
    long end = System.currentTimeMillis();
    System.out.println("Transaction SET: " + ((end - start)/1000.0) + " seconds");
    jedis.disconnect();
}

我们调用jedis.watch(…)方法来监控key,如果调用后key值发生变化,则整个事务会执行失败。另外,事务中某个操作失败,并不会回滚其他操作。这一点需要注意。还有,我们可以使用discard()方法来取消事务。

三、管道(Pipelining)

有时,我们需要采用异步方式,一次发送多个指令,不同步等待其返回结果。这样可以取得非常好的执行效率。这就是管道,调用方法如下:

@Test
public void test3Pipelined() {
    Jedis jedis = new Jedis("localhost");
    Pipeline pipeline = jedis.pipelined();
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        pipeline.set("p" + i, "p" + i);
    }
    List<Object> results = pipeline.syncAndReturnAll();
    long end = System.currentTimeMillis();
    System.out.println("Pipelined SET: " + ((end - start)/1000.0) + " seconds");
    jedis.disconnect();
}

四、管道中调用事务

就Jedis提供的方法而言,是可以做到在管道中使用事务,其代码如下:

@Test
public void test4combPipelineTrans() {
    jedis = new Jedis("localhost");
    long start = System.currentTimeMillis();
    Pipeline pipeline = jedis.pipelined();
    pipeline.multi();
    for (int i = 0; i < 100000; i++) {
        pipeline.set("" + i, "" + i);
    }
    pipeline.exec();
    List<Object> results = pipeline.syncAndReturnAll();
    long end = System.currentTimeMillis();
    System.out.println("Pipelined transaction: " + ((end - start)/1000.0) + " seconds");
    jedis.disconnect();
}

但是经测试(见本文后续部分),发现其效率和单独使用事务差不多,甚至还略微差点。

五、分布式直连同步调用

@Test
public void test5shardNormal() {
    List<JedisShardInfo> shards = Arrays.asList(
            new JedisShardInfo("localhost",6379),
            new JedisShardInfo("localhost",6380));

    ShardedJedis sharding = new ShardedJedis(shards);

    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        String result = sharding.set("sn" + i, "n" + i);
    }
    long end = System.currentTimeMillis();
    System.out.println("[email protected] SET: " + ((end - start)/1000.0) + " seconds");

    sharding.disconnect();
}

这个是分布式直接连接,并且是同步调用,每步执行都返回执行结果。类似地,还有异步管道调用。

六、分布式直连异步调用

@Test
public void test6shardpipelined() {
    List<JedisShardInfo> shards = Arrays.asList(
            new JedisShardInfo("localhost",6379),
            new JedisShardInfo("localhost",6380));

    ShardedJedis sharding = new ShardedJedis(shards);

    ShardedJedisPipeline pipeline = sharding.pipelined();
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        pipeline.set("sp" + i, "p" + i);
    }
    List<Object> results = pipeline.syncAndReturnAll();
    long end = System.currentTimeMillis();
    System.out.println("[email protected] SET: " + ((end - start)/1000.0) + " seconds");

    sharding.disconnect();
}

七、分布式连接池同步调用

如果,你的分布式调用代码是运行在线程中,那么上面两个直连调用方式就不合适了,因为直连方式是非线程安全的,这个时候,你就必须选择连接池调用。

@Test
public void test7shardSimplePool() {
    List<JedisShardInfo> shards = Arrays.asList(
            new JedisShardInfo("localhost",6379),
            new JedisShardInfo("localhost",6380));

    ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards);

    ShardedJedis one = pool.getResource();

    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        String result = one.set("spn" + i, "n" + i);
    }
    long end = System.currentTimeMillis();
    pool.returnResource(one);
    System.out.println("[email protected] SET: " + ((end - start)/1000.0) + " seconds");

    pool.destroy();
}

上面是同步方式,当然还有异步方式。

八、分布式连接池异步调用

@Test
public void test8shardPipelinedPool() {
    List<JedisShardInfo> shards = Arrays.asList(
            new JedisShardInfo("localhost",6379),
            new JedisShardInfo("localhost",6380));

    ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards);

    ShardedJedis one = pool.getResource();

    ShardedJedisPipeline pipeline = one.pipelined();

    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        pipeline.set("sppn" + i, "n" + i);
    }
    List<Object> results = pipeline.syncAndReturnAll();
    long end = System.currentTimeMillis();
    pool.returnResource(one);
    System.out.println("[email protected] SET: " + ((end - start)/1000.0) + " seconds");
    pool.destroy();
}

九、需要注意的地方

  1. 事务和管道都是异步模式。在事务和管道中不能同步查询结果。比如下面两个调用,都是不允许的:

    Transaction tx = jedis.multi();
     for (int i = 0; i < 100000; i++) {
         tx.set("t" + i, "t" + i);
     }
     System.out.println(tx.get("t1000").get());  //不允许
    
     List<Object> results = tx.exec();
    
     …
     …
    
     Pipeline pipeline = jedis.pipelined();
     long start = System.currentTimeMillis();
     for (int i = 0; i < 100000; i++) {
         pipeline.set("p" + i, "p" + i);
     }
     System.out.println(pipeline.get("p1000").get()); //不允许
    
     List<Object> results = pipeline.syncAndReturnAll();
  2. 事务和管道都是异步的,个人感觉,在管道中再进行事务调用,没有必要,不如直接进行事务模式。

  3. 分布式中,连接池的性能比直连的性能略好(见后续测试部分)。

  4. 分布式调用中不支持事务。

    因为事务是在服务器端实现,而在分布式中,每批次的调用对象都可能访问不同的机器,所以,没法进行事务。

十、测试

运行上面的代码,进行测试,其结果如下:

Simple SET: 5.227 seconds

Transaction SET: 0.5 seconds
Pipelined SET: 0.353 seconds
Pipelined transaction: 0.509 seconds

[email protected] SET: 5.289 seconds
[email protected] SET: 0.348 seconds

[email protected] SET: 5.039 seconds
[email protected] SET: 0.401 seconds

另外,经测试分布式中用到的机器越多,调用会越慢。上面是2片,下面是5片:

[email protected] SET: 5.494 seconds
[email protected] SET: 0.51 seconds
[email protected] SET: 5.223 seconds
[email protected] SET: 0.518 seconds

下面是10片:

[email protected] SET: 5.9 seconds
[email protected] SET: 0.794 seconds
[email protected] SET: 5.624 seconds
[email protected] SET: 0.762 seconds

下面是100片:

[email protected] SET: 14.055 seconds
[email protected] SET: 8.185 seconds
[email protected] SET: 13.29 seconds
[email protected] SET: 7.767 seconds

分布式中,连接池方式调用不但线程安全外,根据上面的测试数据,也可以看出连接池比直连的效率更好。

十一、完整的测试代码

package com.example.nosqlclient;

import java.util.Arrays;
import java.util.List;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPipeline;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.Transaction;

import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestJedis {

    private static Jedis jedis;
    private static ShardedJedis sharding;
    private static ShardedJedisPool pool;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        List<JedisShardInfo> shards = Arrays.asList(
                new JedisShardInfo("localhost",6379),
                new JedisShardInfo("localhost",6379)); //使用相同的ip:port,仅作测试


        jedis = new Jedis("localhost");
        sharding = new ShardedJedis(shards);

        pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
        jedis.disconnect();
        sharding.disconnect();
        pool.destroy();
    }

    @Test
    public void test1Normal() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            String result = jedis.set("n" + i, "n" + i);
        }
        long end = System.currentTimeMillis();
        System.out.println("Simple SET: " + ((end - start)/1000.0) + " seconds");
    }

    @Test
    public void test2Trans() {
        long start = System.currentTimeMillis();
        Transaction tx = jedis.multi();
        for (int i = 0; i < 100000; i++) {
            tx.set("t" + i, "t" + i);
        }
        //System.out.println(tx.get("t1000").get());

        List<Object> results = tx.exec();
        long end = System.currentTimeMillis();
        System.out.println("Transaction SET: " + ((end - start)/1000.0) + " seconds");
    }

    @Test
    public void test3Pipelined() {
        Pipeline pipeline = jedis.pipelined();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            pipeline.set("p" + i, "p" + i);
        }
        //System.out.println(pipeline.get("p1000").get());
        List<Object> results = pipeline.syncAndReturnAll();
        long end = System.currentTimeMillis();
        System.out.println("Pipelined SET: " + ((end - start)/1000.0) + " seconds");
    }

    @Test
    public void test4combPipelineTrans() {
        long start = System.currentTimeMillis();
        Pipeline pipeline = jedis.pipelined();
        pipeline.multi();
        for (int i = 0; i < 100000; i++) {
            pipeline.set("" + i, "" + i);
        }
        pipeline.exec();
        List<Object> results = pipeline.syncAndReturnAll();
        long end = System.currentTimeMillis();
        System.out.println("Pipelined transaction: " + ((end - start)/1000.0) + " seconds");
    }

    @Test
    public void test5shardNormal() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            String result = sharding.set("sn" + i, "n" + i);
        }
        long end = System.currentTimeMillis();
        System.out.println("[email protected] SET: " + ((end - start)/1000.0) + " seconds");
    }

    @Test
    public void test6shardpipelined() {
        ShardedJedisPipeline pipeline = sharding.pipelined();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            pipeline.set("sp" + i, "p" + i);
        }
        List<Object> results = pipeline.syncAndReturnAll();
        long end = System.currentTimeMillis();
        System.out.println("[email protected] SET: " + ((end - start)/1000.0) + " seconds");
    }

    @Test
    public void test7shardSimplePool() {
        ShardedJedis one = pool.getResource();

        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            String result = one.set("spn" + i, "n" + i);
        }
        long end = System.currentTimeMillis();
        pool.returnResource(one);
        System.out.println("[email protected] SET: " + ((end - start)/1000.0) + " seconds");
    }

    @Test
    public void test8shardPipelinedPool() {
        ShardedJedis one = pool.getResource();

        ShardedJedisPipeline pipeline = one.pipelined();

        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            pipeline.set("sppn" + i, "n" + i);
        }
        List<Object> results = pipeline.syncAndReturnAll();
        long end = System.currentTimeMillis();
        pool.returnResource(one);
        System.out.println("[email protected] SET: " + ((end - start)/1000.0) + " seconds");
    }
}
转自:http://www.blogways.net/blog/2013/06/02/jedis-demo.html
5月 042014
 

毫无疑问,Redis开创了一种新的数据存储思路,使用Redis,我们不用在面对功能单调的数据库时,把精力放在如何把大象放进冰箱这样的问题上,而是利用Redis灵活多变的数据结构和数据操作,为不同的大象构建不同的冰箱。希望你喜欢这个比喻。

下面是一篇新鲜出炉的文章,其作者是Redis作者@antirez,他描述了Redis比较适合的一些应用场景。

1.取最新N个数据的操作

比如典型的取你网站的最新文章,通过下面方式,我们可以将最新的5000条评论的ID放在Redis的List集合中,并将超出集合部分从数据库获取

  • 使用LPUSH latest.comments<ID>命令,向list集合中插入数据
  • 插入完成后再用LTRIM latest.comments 0 5000命令使其永远只保存最近5000个ID
  • 然后我们在客户端获取某一页评论时可以用下面的逻辑(伪代码)
  1. FUNCTION get_latest_comments(start,num_items):  
  2.     id_list = redis.lrange(“latest.comments”,start,start+num_items-1)  
  3.     IF id_list.length < num_items  
  4.         id_list = SQL_DB(“SELECT … ORDER BY time LIMIT …”)  
  5.     END  
  6.     RETURN id_list  
  7. END  

如果你还有不同的筛选维度,比如某个分类的最新N条,那么你可以再建一个按此分类的List,只存ID的话,Redis是非常高效的。

2.排行榜应用,取TOP N操作

这个需求与上面需求的不同之处在于,前面操作以时间为权重,这个是以某个条件为权重,比如按顶的次数排序,这时候就需要我们的sorted set出马了,将你要排序的值设置成sorted set的score,将具体的数据设置成相应的value,每次只需要执行一条ZADD命令即可。

3.需要精准设定过期时间的应用

比如你可以把上面说到的sorted set的score值设置成过期时间的时间戳,那么就可以简单地通过过期时间排序,定时清除过期数据了,不仅是清除Redis中的过期数据,你完全可以把Redis里这个过期时间当成是对数据库中数据的索引,用Redis来找出哪些数据需要过期删除,然后再精准地从数据库中删除相应的记录。

4.计数器应用

Redis的命令都是原子性的,你可以轻松地利用INCR,DECR命令来构建计数器系统。

5.Uniq操作,获取某段时间所有数据排重值

这个使用Redis的set数据结构最合适了,只需要不断地将数据往set中扔就行了,set意为集合,所以会自动排重。

6.实时系统,反垃圾系统

通过上面说到的set功能,你可以知道一个终端用户是否进行了某个操作,可以找到其操作的集合并进行分析统计对比等。没有做不到,只有想不到。

7.Pub/Sub构建实时消息系统

Redis的Pub/Sub系统可以构建实时的消息系统,比如很多用Pub/Sub构建的实时聊天系统的例子。

8.构建队列系统

使用list可以构建队列系统,使用sorted set甚至可以构建有优先级的队列系统。

9.缓存

这个不必说了,性能优于Memcached,数据结构更多样化。

5月 042014
 

学习了,分享下:

其实微博是一个结构相对简单,但数据量却是很庞大的一种产品.标题所说的是千万级数据量也并不是一千万条微博信息而已,而是千万级订阅关系之间发布。在看 我这篇文章之前,大多数人都看过sina的杨卫华大牛的微博开发大会上的演讲.我这也不当复读机了,挑重点跟大家说一下。

大家都知道微博的难点在于明星会员问题,什么是明星会员问题了,就是刘德华来咱这开了微博,他有几百万的粉丝订阅者,他发一条微博信息,那得一下子把微博 信息发布到几百万的粉丝里去,如果黎明、郭富城等四大天王都来咱来开微博,那咱小站不是死翘翘了.所以这时消息队列上场了。在我的架构里 有一个异步publish集群,publish的任务都去zeromq队列读取队列.zeromq是目前已知开源的消息传递最快的一个。具体关于 zeromq可以自己google。zeromq有一个问题是不能持久化数据,这个自己做持久化存储.回过刚才那个话题, 把明星会员的粉丝按照”活跃度”进行分级。”活跃度”是根据登陆频度,时间,发布微博等因素大致分为铁杆粉丝、爱理不理、半死不活三大类分到不同的发布集 群中去. 铁杆粉丝类型的异步发布集群,发布速度肯定是最快的.微博的信息是用handler socket保存到mysql。这个信息ID,是用rdtsc+2位随机整数拼接而成的 64位整数唯一ID,防止出现自增ID出现的多服务器 id一致性的问题. 在publish的时候,集群只是把微博信息的ID发送给redis的订阅者。所以这个数据是很快的。而且订阅者的list里只保存的是ID.在内存的占 用率上也不是很高.

下面我给大家看一下我的mysql和redis数据结构

在我的结构中还有一个重要角色就是”Key GPS Server”(简称:KGS)简单来说,这个是分布式数据存储的中心索引服务器.一切数据的存储和获取,都通过KGS来定位. KGS支持多个服务器,多个机房多重备份存储。KGS是以Tokyo Cabinet的hash db为存储的socket server。记录key跟服务器之间的对应关系. KGS的任务就是告知key该存储在哪几台服务器上,或者告知该key存储在哪几台服务器上,并不做其他的服务.这样大大的减轻KGS的压力.

再说一下Redis集群,redis是以纯内存形式模式运行,关闭了热备的功能(redis的热备并不是那么好). 自己写了个backend server.在每台运行redis的机子上都运行着backend socket 进程, backend进程也是以tc的hash db为存储。备份着当前服务器的redis数据。当redis重启的时候,从本机的bakcend db 加载所有数据. Redis的集群是以用户水平切分法来分布的

现在该轮到mysql里, 在这个架构中,基本消除了这边缓存 那边缓存的问题。因为在这个集群中的每个服务都是高速运行的.唯一的一处的cache 就是在php端的eAccelerator local cache. eAccelerator是基于共享内存的,所有速度比基于socket类型的cache快多了. eAccelerator 缓存了用户top N条的微博信息还有从KGS查询的结果。 看到这里有人问了,你把用户信息和微博信息都放在mysql里,怎么能不用cache了.嘿嘿,因为我用了 handler socket。HS 是小日本写的一款mysql插件.HS避开了MySQL通讯协议,直接读取MySQL引擎。在多核、大内存、 InnoDB引擎环境,性能直超memcached.HS能以Key-Value方式直接读写mysql引擎

总结

Google首席科学家讲过一句话,就是一个大的复杂的系统,应该要分解成很多小的服务. 我的这个架构也是由一个个小的集群来共同处理大数据量发布数据。有的人为什么不用mongodb了,因为mongodb是一款大众性的分布式nosql db,我们有自己的key分布策略,不太适合用mongodb. 不理解redis的存储关系的同学,可以先参考一下 Retwis, Retwis是用纯redis实现的简单微博.

具体的架构图、流程图、ppt文件。请下载附件来阅读.   http://code.google.com/p/php-tokyocabinet/downloads/detail?name=micro-blog-qiye.tar.bz2&can=2&q=#makechanges

转自:http://www.oschina.net/question/12_36573

9月 262013
 

Redis RDB 文件格式

Redis *.rdb 文件是一个内存内存储的二进制表示法。这个二进制文件足以完全恢复Redis的状态。

rdb文件格式为快速读和写优化。LZF压缩可以用来减少文件大小。通常,对象前面有它们的长度, 这样,在读取对象之前,你可以准确地分配内存大小。

为快速读/写优化意味着磁盘上的格式应该尽可能接近于在内存里的表示法。这种方式正是rdb文件采用的。 导致的结果是,在不了解Redis在内存里表示数据的数据结构的情况下,你没法解析rdb文件。

解析RDB的高层算法

在高层层面看,RDB文件有下面的格式:


----------------------------# RDB 是一个二进制文件。文件里没有新行或空格。
52 45 44 49 53              # 魔术字符串 "REDIS"
00 00 00 03                 # RDB 版本号,高位优先。在这种情况下,版本是 0003 = 3
----------------------------
FE 00                       # FE = code 指出数据库选择器. 数据库号 = 00
----------------------------# 键值对开始
FD $unsigned int            # FD 指出 "有效期限时间是秒为单位". 在这之后,读取4字节无符号整数作为有效期限时间。
$value-type                 # 1 字节标记指出值的类型 - set,map,sorted set 等。
$string-encoded-key         # 键,编码为一个redis字符串。
$encoded-value              # 值,编码取决于 $value-type.
----------------------------
FC $unsigned long           # FC 指出 "有效期限时间是豪秒为单位". 在这之后,读取8字节无符号长整数作为有效期限时间。
$value-type                 # 1 字节标记指出值的类型 - set,map,sorted set 等。
$string-encoded-key         # 键,编码为一个redis字符串。
$encoded-value              # 值,编码取决于 $value-type.
----------------------------
$value-type                 # 这个键值对没有有效期限。$value_type 保证 != to FD, FC, FE and FF
$string-encoded-key
$encoded-value
----------------------------
FE $length-encoding         # 前一个数据库结束,下一个数据库开始。数据库号用长度编码读取。
----------------------------
...                         # 这个数据库的键值对,另外的数据库。
FF                          ## RDB 文件结束指示器
8 byte checksum             ## 整个文件的 CRC 32 校验和。

魔术数

文件开始于魔术字符串“REDIS”。这是一个快速明智的检查是否正在处理一个redis rdb文件。 52 45 44 49 53 # "REDIS"

RDB 版本号

接下来4个字节存储了rdb格式的版本号。这4个字节解释为ascii字符,然后使用字符串到整数的转换法转换为一个整数。 00 00 00 03 # Version = 3

数据库选择器

一个Redis实例可以有多个数据库。 单一字节0xFE标记数据库选择器的开始。在这个字节之后,一个可变长度的字段指出数据库序号。 见“长度编码”章节来了解如何读取数据库序号。

键值对

在数据库选择器之后,文件包含了一序列的键值对。

每个键值对有4部分:

  1. 键保存期限时间戳。这是可选的。
  2. 一个字节标记值的类型。
  3. 键编码为Redis 字符串。见“Redis 字符串编码”。
  4. 值根据值类型进行编码。见“Redis 值编码”。

键保存期限时间戳

这个区块开始于一字节标记。值FD指出保存期限是以秒为单位指定。值FC指出有效期限是以毫秒为单位指定。

如果时间指定为毫秒,接下来8个字节表示unix时间。这个数字是unix时间戳,精确到秒或毫秒,表示这个键的有效期限。

数字如何编码见“Redis 长度编码”章节。

在导入过程中,已经过期的键将必须丢弃。

值类型

一个字节标记指示用于保存值的编码。

  1. 0 = “String 编码”
  2. 1 = “ List 编码”
  3. 2 = “Set 编码”
  4. 3 = “Sorted Set 编码”
  5. 4 = “Hash 编码”
  6. 9 = “Zipmap 编码”
  7. 10 = “Ziplist 编码”
  8. 11 = “IntSet 编码”
  9. 12 = “以 Ziplist 编码的 Sorted Set”
  10. 13 = “以 Ziplist 编码的 Hashmap” (在rdb版本4中引入)


键简单地编码为Redis字符串。见“字符串编码”章节了解键如何被编码。


值的编码取决于值类型标记。

  • 当值类型 = 0,值是简单字符串。
  • 当值类型是 9, 10, 11 或 12 中的一个,值被包装为字符串。读取字符串后,它必须进一步解析。
  • 当值类型是1,2,3 或 4 中的一个,值是一序列字符串。这个序列字符串用于构造list,set,sorted set或hashmap。

长度编码

长度编码用于存储流中接下来对象的长度。长度编码是一个按的字节编码,为尽可能少用字节而设计。

这是长度编码如何工作:

  1. 从流中读取一个字节,最高两bit被读取
  2. 如果开始bit是 00 ,接下来6bit 表示长度
  3. 如果开始bit是 01,从流再读取额外一个字节。这组合的的14bit表示长度
  4. 如果开始bit是 10,那么剩余的6bit丢弃,从流中读取额外的4字节,这4个字节表示长度
  5. 如果开始bit是 11,那么接下来的对象是以特殊格式编码的。剩余6bit指示格式。这种编码通常用于把数字作为字符串存储 或存储编码后的字符串。见字符串编码

作为这种编码的结果:

  1. 数字 [0 – 63] 可以在1个字节里存储
  2. 数字 [0 – 16383] 可以在2个字节里存储
  3. 数字 [0 – (2^32 – 1)] 可以在4个字节里存储

字符串编码

Redis字符串是二进制安全的--这意味着你可以在这里存储任何东西。它们没有任何特殊的字符串结束记号。 最好认为Redis字符串是一个字节数组。

Redis里有三种类型的字符串:

  1. 长度前缀字符串
  2. 一个8,16或32bit整数
  3. LZF压缩的字符串

长度前缀字符串

长度前置字符串是很简单的。字符串字节的长度首先编码为“长度编码”,在这之后存储字符串的原始字节。

整数作为字符串

首先读取“长度编码”块,特别是第一个两bit是 11。在这种情况下,读取剩余的6bit。如果这6bit的值是:

  1. 0 表示接下来是8bit整数
  2. 1 表示接下来是16bit整数
  3. 2 表示接下来是32bit整数

压缩字符串

首先读取“长度编码”,特别是第一个两bit是 11. 在这种情况下,读取剩余6bit。如果这6bit值是4,它表示接下来是一个压缩字符串。

压缩字符串按如下读取:

  1. 从流中读取压缩后的长度clen,按“长度编码”
  2. 从流中读取未压缩长度,按“长度编码”
  3. 接下来从流中读取clen个字节
  4. 最后,这些字节按LZF算法解压

List 编码

一个Redis list 表示为一序列字符串。

  1. 首先,从流中读取list大小size,按“长度编码”
  2. 然后,size个字符串从流中读取,按“字符串编码”
  3. 使用这些字符串重新构建list

Set 编码

Set 编码与list完全类似。

Sorted Set 编码

  1. 首先,从流中读取sorted set大小size,按“长度编码”
  2. TODO

Hash 编码

  1. 首先,从流中读取hash大小size,按“长度编码”
  2. 下一步,从流中读取 2 * size 个字符串,按“字符串编码”
  3. 交替的字符串是键和值
  4. 例如,2 us washington india delhi 表示map {"us" => "washington", "india" => "dlhi"}

Zipmap 编码

注意:Zipmap编码从Redis 2.6开始已弃用。小的的hashmap编码为ziplist。

Zipmap是一个被序列化为一个字符串的hashmap。本质上,键值对按顺序存储。在这种结构里查找一个键的复杂度是O(N)。 当键值对数量很少时,这个结构用于替代dictionary。

为解析zipmap,首先用“字符串编码”从流读取一个字符串。这个字符串包装了zipmap。字符串的内容表示了zipmap。

字符串里的zipmap结构如下:


  "foo""bar""hello""world"

  1.  zmlen : 1字节长,保存zipmap的大小. 如果大于等于254,值不使用。将需要迭代整个zipmap来找出长度.
  2.  len : 后续字符串的长度,可以是键或值的。这个长度存储为1个或5个字节(与上面描述的“长度编码”不同)。
            如果第一个字节位于 0 到252,那么它是zipmap的长度。如果第一个字节是253,读取下4个字节作为无符号整数来表示zipmap的长度。
            254 和 255 对这个字段是非法的.
  3.  free : 总是1字节,指出值后面的空闲字节数。例如,如果键的值是“America”,更新为“USA”后,将有4个空闲的字节.
  4.  zmend : 总是 255. 指出zipmap结束.

*有效的例子*
  18 02 06 4d 4b 44 31 47 36 01 00 32 05 59 4e 4e 58 4b 04 00 46 37 54 49 ff ..

  1.  从使用“字符串编码”开始解码。你会注意到18是字符串的长度。因此,我们将读取下24个字节,直到ff。
  2.  现在,我们开始解析从  @02 06… @ 开始的字符串,使用 “Zipmap 编码”
  3.  02是hashmap里条目的数量.
  4.  06是下一个字符串的长度. 因为长度小于254, 我们不需要读取任何额外的字节
  5.  我们读取下6个字节  4d 4b 44 31 47 36 来得到键 “MKD1G6”
  6.  01是下一个字符串的长度,这个字符串应当是值
  7.  00是空闲字节的数量
  8.  读取下一个字节 0x32,得到值“2”
  9.   在这种情况下,空闲字节是0,所以不需要跳过任何东西
  10.  05是下一个字符串的长度,在这种情况下是键。
  11.  读取下5个字节 59 4e 4e 58 4b, 得到键 “YNNXK”
  12.  04是下一个字符串的长度,这是一个值
  13.  00是值后面的空闲字节数
  14.  读取下4个字节 46 37 54 49 来得到值 “F7TI”
  15.  最终,遇到 FF, 这表示这个zipmap的结束
  16. 因此,这个zipmap表示hash {"MKD1G6" => "2", "YNNXK" => "F7TI"}

Ziplist 编码

一个Ziplist是一个序列化为一个字符串的list。本质上,list的元素按顺序地存储,借助于标记(flag)和偏移(offset)来达到高校地双向遍历list。

为解析一个ziplist,首先从流中读取一个字符串,按”字符串编码“。这个字符串是ziplist的封装。这个字符串的内容表示了ziplist。

字符串里的ziplist的结构如下:

  1. zlbytes :这是一个4字节无符号整数,表示ziplist的总字节数。这4字节是little endian格式--最先出现的是最低有效位组
  2. zltail:这是一个4字节无符号整数,little endian格式。它表示到ziplist的尾条目(tail entry)的偏移。
  3. zllen:这是一个2字节无符号整数,little endian格式。它表示ziplist的条目的数量
  4. entry:一个条目表示ziplist的元素。细节在下面
  5. zlend:总是等于255.它表示ziplist的结束

ziplist的每个条目有下面的格式:

length-prev-enty: 这个字段存储上一个条目的长度,如果是第一个条目则是0。这允许容易地进行反向遍历list。这个长度存储为1或5个字节。 如果第一个字节小于等于253,它被认为是长度,如果第一个字节是254,接下来4个字节用于存储长度。4字节按无符号整数读取。

special-flag:这个标记指出条目是字符串还是整数。它也指示字符串长度或整数的大小。这个标记的可变编码如下:

  1. |00pppppp| - 1字节:字符串值长度小于等于63字节(6bit)
  2. |01pppppp|qqqqqqqq| - 2字节:字符串值长度小于等于16383字节(14bit)
  3. |10______|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5字节:字符串值长度大于等于16384字节
  4. |1100____| - 读取后面2个字节作为16bit有符号整数
  5. |1101____| - 读取后面4个字节作为32bit有符号整数
  6. |1110____| - 读取后面8个字节作为64bit有符号整数
  7. |11110000| - 读取后面3个字节作为24bit有符号整数
  8. |11111110| - 读取后面1个字节作为8bit有符号整数
  9. |1111xxxx| - (当xxxx位于 0000 到 1101)直接4bit整数。0 到 12 的无符号整数。被编码的实际值是从 1 到 13,因为 0000 和 1111 不能使用,所以应当从编码的4bit值里减去 1 来获得正确的值。

Raw Bytes:在special flag后,是原始字节。字节的数字由前面的special flag部分决定。

举例 23 23 00 00 00 1e 00 00 00 04 00 00 e0 ff ff ff ff ff ff ff 7f 0a d0 ff ff 00 00 06 c0 fc 3f 04 c0 3f 00 ff … | | | | | | | |

  1. 从使用“字符串编码”开始解码。23 是字符串的长度,然后读取35个字节直到 ff
  2. 使用“Ziplist 编码”解析开始于 23 00 00 … 的字符串
  3. 前4个字节 23 00 00 00 表示Ziplis长度的字节总数。注意,这是little endian 格式
  4. 接下来4个字节 1e 00 00 00 表示到尾条目的偏移。 0x1e = 30,这是一个基于0的偏移。 0th position = 23, 1st position = 00 and so on. It follows that the last entry starts at 04 c0 3f 00 .. 。
  5. 接下来2个字节 04 00 表示list里条目的数量。
  6. 从现在开始,读取条目。
  7. 00 表示前一个条目的长度。0表示这是第一个条目。
  8. e0 是特殊标记,因为它开始于位模式 1110____,读取下8个字节作为整数。这是list的第一个条目。
  9. 现在开始读取第二个条目。
  10. 0a 是前一个条目的长度。10 字节 = 1 字节prev长度 + 1 字节特殊标记长度 + 8 字节整数
  11. d0 是特殊标记,因为它开始于位模式 1101____,读取下4个字节作为整数。这是list的第二个条目。
  12. 现在开始第二个条目。
  13. 06 是前一个条目的长度。 6 字节 = 1 字节prev长度 + 1 字节特殊标记 + 4 字节整数。
  14. c0 是特殊标记,因为它开始于位模式 1100____,读取下2个字节作为整数。这是list的第三个条目。
  15. 现在开始读取第四个条目。
  16. 04 是前一个题目的长度。
  17. c0 指出是2字节整数。
  18. 读取下2个字节,作为第四个条目。
  19. 最终遇到 ff,这表明已经读取完list里的所有元素。
  20. 因此,ziplist存储了值 [0×7fffffffffffffff, 65535, 16380, 63]。

Intset 编码

一个Inset是一个整数的二叉搜索树。这个二叉树在一个整数数组里实现。inset用于当set的所有元素都是整数时。Inset支持达64位的整数。 作为一个优化,如果整数能用更少的字节表示,整数数组将由16位或32位整数构建。当一个新元素插入时,intset实现在需要时将进行一次升级。

因为Intset是二叉搜索树,set里的数字总是有序的。

一个Intset有一个Set的外部接口。

为了解析Inset,首先使用“字符串编码”从流中读取一个字符串。这个字符串包含了Intset。这个字符串的内容表示了Intset。

在字符串里,Intset有一个非常简单的布局:

 

  1. encoding:是一个32位无符号整数。它有3个可能的值 - 2, 4 或 8.它指出内容里存储的每个整数的字节大小。 嗯,是的,这是浪费的-可以在2bit里存储这些信息。
  2. length-of-contet:是一个32位无符号整数,指出内容数组的长度。
  3. contents:是一个 $length-of-content 个字节的数组。它包含了二叉搜索树。

举例 14 04 00 00 00 03 00 00 00 fc ff 00 00 fd ff 00 00 fe ff 00 00 …

  1. 使用“字符串编码”来开始。14 是字符串的长度,读取下20个字节直到 00.
  2. 现在,开始解析开始于 04 00 00 …. 的字符串。
  3. 前4个字节 04 00 00 00 是编码,因为它的值是4,我们知道我们正在处理32位整数。
  4. 下4个字节 03 00 00 00 是内容的长度。这样,我们知道我们正在处理3个整数,每个4字节长。
  5. 从现在开始,我们以4个字节为一组读取,再把它转换为一个无符号整数。
  6. 这样,我们的intset看起来是这样的 - 0x0000FFFC, 0x0000FFFD, 0x0000FFFE。注意,这些整数是little endian格式的。首先出现的是最低有效位。

以Ziplist 编码的 Sorted Set

以ziplist编码存储的sorted list跟上面描述的Ziplist很像。在ziplist里,sorted set的每个元素后跟它的score。

举例 [‘Manchester City’, 1, ‘Manchester United’, 2, ‘Totenham’, 3]

如你所见score跟在每个元素后面。

Ziplist编码的Hashmap

在这里,hashmap的键值对是作为连续的条目存储在ziplist里。

注意:这是在rdb版本4引入,它废弃了在先前版本里使用的zipmap。

举例 {"us" => “washington”, “india” => "delhi"} 存储在ziplist里是: [“us”, “washington”, “india”, “delhi”]

CRC32 校验和

从RDB版本5开始,一个8字节的CRC32校验和被加到文件结尾。可以通过redis.conf 文件的一个参数来作废这个校验和。

当校验和被作废时,这个字段将是0。

 

翻译自: https://github.com/sripathikrishnan/redis-rdb-tools/wiki/Redis-RDB-Dump-File-Format

转自:http://blog.csdn.net/jiazhen/article/details/3938313

8月 072013
 

[[email protected] ~]$ redis-cli –help

redis-cli 2.4.16 

Usage: redis-cli [OPTIONS] [cmd [arg [arg …]]] 

  -h <hostname>    Server hostname (default: 127.0.0.1) 

  -p <port>        Server port (default: 6379) 

  -s <socket>      Server socket (overrides hostname and port) 

  -a <password>    Password to use when connecting to the server 

  -r <repeat>      Execute specified command N times 

  -i <interval>    When -r is used, waits <interval> seconds per command. 

                   It is possible to specify sub-second times like -i 0.1. 

  -n <db>          Database number 

  -x               Read last argument from STDIN 

  -d <delimiter>   Multi-bulk delimiter in for raw formatting (default: n) 

  –raw            Use raw formatting for replies (default when STDOUT is not a tty) 

  –latency        Enter a special mode continuously sampling latency. 

  –slave          Simulate a slave showing commands received from the master. 

  –pipe           Transfer raw Redis protocol from stdin to server. 

  –bigkeys        Sample Redis keys looking for big keys. 

  –help           Output this help and exit 

  –version        Output version and exit

–raw 显示中文,而不是"xd6xd0"

redis 10.10.100.18:6379> set aaa 中

OK

redis 10.10.100.18:6379> get aaa

如果你是用的windows cmd,还是乱码,要设置窗口的编码

chcp 65001  就是换成UTF-8代码页,在命令行标题栏上点击右键,选择"属性"->"字体",将字体修改为True Type字体"Lucida Console",然后点击确定将属性应用到当前窗口

8月 072013
 

如何在命令行批量删除redis的key?

 
  1. rediscli -n 0 KEYS "topic*" | xargs rediscli -n 0 DEL
 

redis-cli已经设置为系统变量了可以直接使用上面的命令
如果没有,redis-cli应该替换为redis完整路径

 

 

 

附: Linux xargs命令  http://blog.csdn.net/ithomer/article/details/7303501

xargs是给命令传递参数的一个过滤器,也是组合多个命令的一个工具。它把一个数据流分割为一些足够小的块,以方便过滤器和命令进行处理。通常情况下,xargs从管道或者stdin中读取数据,但是它也能够从文件的输出中读取数据。xargs的默认命令是echo,这意味着通过管道传递给xargs的输入将会包含换行和空白,不过通过xargs的处理,换行和空白将被空格取代。

 

xargs 是一个强有力的命令,它能够捕获一个命令的输出,然后传递给另外一个命令,下面是一些如何有效使用xargs 的实用例子。

1. 当你尝试用rm 删除太多的文件,你可能得到一个错误信息:/bin/rm Argument list too long. 用xargs 去避免这个问题

find ~ -name ‘*.log’ -print0 | xargs -0 rm -f

 

2. 获得/etc/ 下所有*.conf 结尾的文件列表,有几种不同的方法能得到相同的结果,下面的例子仅仅是示范怎么实用xargs ,在这个例子中实用 xargs将find 命令的输出传递给ls -l

find /etc -name "*.conf" | xargs ls –l


3. 假如你有一个文件包含了很多你希望下载的URL, 你能够使用xargs 下载所有链接

# cat url-list.txt | xargs wget –c

 

4. 查找所有的jpg 文件,并且压缩它

# find / -name *.jpg -type f -print | xargs tar -cvzf images.tar.gz


5. 拷贝所有的图片文件到一个外部的硬盘驱动 

ls *.jpg | xargs -n1 -i cp {} /external-hard-drive/directory

 

redis info 命令

 redis  redis info 命令已关闭评论
5月 142013
 

来自redis官档对于 INFO命令的解析。

大家也可以在http://redis.io/commands#server中找到CLIENT LIST、SLOWLOG等命令的官方解释。

The INFO command returns information and statistics about the server in a format that is simple to parse by computers and easy to read by humans.

The optional parameter can be used to select a specific section of information:

  • server: General information about the Redis server
  • clients: Client connections section
  • memory: Memory consumption related information
  • persistence: RDB and AOF related information
  • stats: General statistics
  • replication: Master/slave replication information
  • cpu: CPU consumption statistics
  • commandstats: Redis command statistics
  • cluster: Redis Cluster section
  • keyspace: Database related statistics

It can also take the following values:

  • all: Return all sections
  • default: Return only the default set of sections

When no parameter is provided, the default option is assumed.

Return value

Bulk reply: as a collection of text lines.

Lines can contain a section name (starting with a # character) or a property. All the properties are in the form offield:value terminated by rn.

redis> INFO

# Server
redis_version:2.5.13
redis_git_sha1:2812b945
redis_git_dirty:0
os:Linux 2.6.32.16-linode28 i686
arch_bits:32
multiplexing_api:epoll
gcc_version:4.4.1
process_id:8107
run_id:2e0192d968d1d4a36a927413d3b4ae4aa46e7ccd
tcp_port:6379
uptime_in_seconds:10335010
uptime_in_days:119
lru_clock:537661

# Clients
connected_clients:8
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0

# Memory
used_memory:1478200
used_memory_human:1.41M
used_memory_rss:2007040
used_memory_peak:1737576
used_memory_peak_human:1.66M
used_memory_lua:20480
mem_fragmentation_ratio:1.36
mem_allocator:jemalloc-3.0.0

# Persistence
loading:0
rdb_changes_since_last_save:235
rdb_bgsave_in_progress:0
rdb_last_save_time:1368525256
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok

# Stats
total_connections_received:4673
total_commands_processed:26601043
instantaneous_ops_per_sec:0
rejected_connections:0
expired_keys:26239
evicted_keys:0
keyspace_hits:6439648
keyspace_misses:1588733
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:786

# Replication
role:master
connected_slaves:0

# CPU
used_cpu_sys:2999.93
used_cpu_user:1354.42
used_cpu_sys_children:165.93
used_cpu_user_children:401.32

# Keyspace
db0:keys=11071,expires=1
redis>  

Notes

Please note depending on the version of Redis some of the fields have been added or removed. A robust client application should therefore parse the result of this command by skipping unknown properties, and gracefully handle missing fields.

Here is the description of fields for Redis >= 2.4.

Here is the meaning of all fields in the server section:

  • redis_version: Version of the Redis server
  • redis_git_sha1: Git SHA1
  • redis_git_dirty: Git dirty flag
  • os: Operating system hosting the Redis server
  • arch_bits: Architecture (32 or 64 bits)
  • multiplexing_api: event loop mechanism used by Redis
  • gcc_version: Version of the GCC compiler used to compile the Redis server
  • process_id: PID of the server process
  • run_id: Random value identifying the Redis server (to be used by Sentinel and Cluster)
  • tcp_port: TCP/IP listen port
  • uptime_in_seconds: Number of seconds since Redis server start
  • uptime_in_days: Same value expressed in days
  • lru_clock: Clock incrementing every minute, for LRU management

Here is the meaning of all fields in the clients section:

  • connected_clients: Number of client connections (excluding connections from slaves)
  • client_longest_output_list: longest output list among current client connections
  • client_biggest_input_buf: biggest input buffer among current client connections
  • blocked_clients: Number of clients pending on a blocking call (BLPOP, BRPOP, BRPOPLPUSH)

Here is the meaning of all fields in the memory section:

  • used_memory: total number of bytes allocated by Redis using its allocator (either standard libcjemalloc, or an alternative allocator such astcmalloc
  • used_memory_human: Human readable representation of previous value
  • used_memory_rss: Number of bytes that Redis allocated as seen by the operating system (a.k.a resident set size). This is the number reported by tools such as top and ps.
  • used_memory_peak: Peak memory consumed by Redis (in bytes)
  • used_memory_peak_human: Human readable representation of previous value
  • used_memory_lua: Number of bytes used by the Lua engine
  • mem_fragmentation_ratio: Ratio between used_memory_rss and used_memory
  • mem_allocator: Memory allocator, chosen at compile time.

Ideally, the used_memory_rss value should be only slightly higher than used_memory. When rss >> used, a large difference means there is memory fragmentation (internal or external), which can be evaluated by checking mem_fragmentation_ratio. When used >> rss, it means part of Redis memory has been swapped off by the operating system: expect some significant latencies.

Because Redis does not have control over how its allocations are mapped to memory pages, high used_memory_rss is often the result of a spike in memory usage.

When Redis frees memory, the memory is given back to the allocator, and the allocator may or may not give the memory back to the system. There may be a discrepancy between the used_memory value and memory consumption as reported by the operating system. It may be due to the fact memory has been used and released by Redis, but not given back to the system. The used_memory_peak value is generally useful to check this point.

Here is the meaning of all fields in the persistence section:

  • loading: Flag indicating if the load of a dump file is on-going
  • rdb_changes_since_last_save: Number of changes since the last dump
  • rdb_bgsave_in_progress: Flag indicating a RDB save is on-going
  • rdb_last_save_time: Epoch-based timestamp of last successful RDB save
  • rdb_last_bgsave_status: Status of the last RDB save operation
  • rdb_last_bgsave_time_sec: Duration of the last RDB save operation in seconds
  • rdb_current_bgsave_time_sec: Duration of the on-going RDB save operation if any
  • aof_enabled: Flag indicating AOF logging is activated
  • aof_rewrite_in_progress: Flag indicating a AOF rewrite operation is on-going
  • aof_rewrite_scheduled: Flag indicating an AOF rewrite operation will be scheduled once the on-going RDB save is complete.
  • aof_last_rewrite_time_sec: Duration of the last AOF rewrite operation in seconds
  • aof_current_rewrite_time_sec: Duration of the on-going AOF rewrite operation if any
  • aof_last_bgrewrite_status: Status of the last AOF rewrite operation

changes_since_last_save refers to the number of operations that produced some kind of changes in the dataset since the last time eitherSAVE or BGSAVE was called.

If AOF is activated, these additional fields will be added:

  • aof_current_size: AOF current file size
  • aof_base_size: AOF file size on latest startup or rewrite
  • aof_pending_rewrite: Flag indicating an AOF rewrite operation will be scheduled once the on-going RDB save is complete.
  • aof_buffer_length: Size of the AOF buffer
  • aof_rewrite_buffer_length: Size of the AOF rewrite buffer
  • aof_pending_bio_fsync: Number of fsync pending jobs in background I/O queue
  • aof_delayed_fsync: Delayed fsync counter

If a load operation is on-going, these additional fields will be added:

  • loading_start_time: Epoch-based timestamp of the start of the load operation
  • loading_total_bytes: Total file size
  • loading_loaded_bytes: Number of bytes already loaded
  • loading_loaded_perc: Same value expressed as a percentage
  • loading_eta_seconds: ETA in seconds for the load to be complete

Here is the meaning of all fields in the stats section:

  • total_connections_received: Total number of connections accepted by the server
  • total_commands_processed: Total number of commands processed by the server
  • instantaneous_ops_per_sec: Number of commands processed per second
  • rejected_connections: Number of connections rejected because of maxclients limit
  • expired_keys: Total number of key expiration events
  • evicted_keys: Number of evicted keys due to maxmemory limit
  • keyspace_hits: Number of successful lookup of keys in the main dictionary
  • keyspace_misses: Number of failed lookup of keys in the main dictionary
  • pubsub_channels: Global number of pub/sub channels with client subscriptions
  • pubsub_patterns: Global number of pub/sub pattern with client subscriptions
  • latest_fork_usec: Duration of the latest fork operation in microseconds

Here is the meaning of all fields in the replication section:

  • role: Value is "master" if the instance is slave of no one, or "slave" if the instance is enslaved to a master. Note that a slave can be master of another slave (daisy chaining).

If the instance is a slave, these additional fields are provided:

  • master_host: Host or IP address of the master
  • master_port: Master listening TCP port
  • master_link_status: Status of the link (up/down)
  • master_last_io_seconds_ago: Number of seconds since the last interaction with master
  • master_sync_in_progress: Indicate the master is SYNCing to the slave

If a SYNC operation is on-going, these additional fields are provided:

  • master_sync_left_bytes: Number of bytes left before SYNCing is complete
  • master_sync_last_io_seconds_ago: Number of seconds since last transfer I/O during a SYNC operation

If the link between master and slave is down, an additional field is provided:

  • master_link_down_since_seconds: Number of seconds since the link is down

The following field is always provided:

  • connected_slaves: Number of connected slaves

For each slave, the following line is added:

  • slaveXXX: id, ip address, port, state

Here is the meaning of all fields in the cpu section:

  • used_cpu_sys: System CPU consumed by the Redis server
  • used_cpu_user:User CPU consumed by the Redis server
  • used_cpu_sys_children: System CPU consumed by the background processes
  • used_cpu_user_children: User CPU consumed by the background processes

The commandstats section provides statistics based on the command type, including the number of calls, the total CPU time consumed by these commands, and the average CPU consumed per command execution.

For each command type, the following line is added:

  • cmdstat_XXX:calls=XXX,usec=XXX,usecpercall=XXX

The cluster section currently only contains a unique field:

  • cluster_enabled: Indicate Redis cluster is enabled

The keyspace section provides statistics on the main dictionary of each database. The statistics are the number of keys, and the number of keys with an expiration.

For each database, the following line is added:

  • dbXXX:keys=XXX,expires=XXX
 Posted by at 下午6:01  Tagged with:

Redis服务端状态与性能监控命令

 redis  Redis服务端状态与性能监控命令已关闭评论
4月 052013
 

1、redis-benchmark 

redis基准信息,redis服务器性能检测 



redis-benchmark -h localhost -p 6379 -c 100 -n 100000 

100个并发连接,100000个请求,检测host为localhost 端口为6379的redis服务器性能 

 

  1. [root@Architect redis-1.2.6]# redis-benchmark -h localhost -p 6379 -c 100 -n 100000  
  2. ====== PING ======  
  3.   10001 requests completed in 0.41 seconds  
  4.   50 parallel clients  
  5.   3 bytes payload  
  6.   keep alive: 1  
  7.   
  8. 0.01<= 0 milliseconds  
  9. 23.09<= 1 milliseconds  
  10. 85.82<= 2 milliseconds  
  11. 95.60<= 3 milliseconds  
  12. 97.20<= 4 milliseconds  
  13. 97.96<= 5 milliseconds  
  14. 98.83<= 6 milliseconds  
  15. 99.41<= 7 milliseconds  
  16. 99.70<= 8 milliseconds  
  17. 99.99<= 9 milliseconds  
  18. 100.00<= 12 milliseconds  
  19. 24274.27 requests per second  





2、redis-cli 



redis-cli -h localhost -p 6380 monitor 

Dump all the received requests in real time; 
监控host为localhost,端口为6380,redis的连接及读写操作
 

 

  1. [root@Architect redis-1.2.6]# redis-cli -h localhost -p 6380 monitor  
  2. +OK  
  3. +1289800615.808225 "monitor"  
  4. +1289800615.839079 "GET" "name"  
  5. +1289800615.853694 "PING"  
  6. +1289800615.853783 "PING"  
  7. +1289800615.854646 "PING"  
  8. +1289800615.854974 "PING"  
  9. +1289800615.857693 "PING"  
  10. +1289800615.866862 "PING"  
  11. +1289800615.871944 "PING"  





redis-cli -h localhost -p 6380 info 

Provide information and statistics about the server ; 
提供host为localhost,端口为6380,redis服务的统计信息
 

 

  1. [root@Architect redis-1.2.6]# redis-cli -h localhost -p 6380 info  
  2. redis_version:2.0.4  
  3. redis_git_sha1:00000000  
  4. redis_git_dirty:0  
  5. arch_bits:32  
  6. multiplexing_api:epoll  
  7. process_id:21990  
  8. uptime_in_seconds:490580  
  9. uptime_in_days:5  
  10. connected_clients:103  
  11. connected_slaves:0  
  12. blocked_clients:0  
  13. used_memory:4453240  
  14. used_memory_human:4.25M  
  15. changes_since_last_save:200  
  16. bgsave_in_progress:0  
  17. last_save_time:1290394640  
  18. bgrewriteaof_in_progress:0  
  19. total_connections_received:809  
  20. total_commands_processed:44094018  
  21. expired_keys:0  
  22. hash_max_zipmap_entries:64  
  23. hash_max_zipmap_value:512  
  24. pubsub_channels:0  
  25. pubsub_patterns:0  
  26. vm_enabled:0  
  27. role:slave  
  28. master_host:localhost  
  29. master_port:6379  
  30. master_link_status:up  
  31. master_last_io_seconds_ago:18  
  32. db0:keys=1319,expires=0  





3、redis-stat 



redis-stat host localhost port 6380 overview 

Print general information about a Redis instance; 
实时打印出host为localhost,端口为6380,redis实例的总体信息
 

 

  1. [root@Architect redis-1.2.6]# redis-stat port 6380 overview  
  2.  ——- data —— ———— load —————————– – childs –  
  3.  keys      used-mem  clients   requests            connections  
  4.  1319      5.37M     103       44108021 (+44108021810                 
  5.  1319      5.38M     103       44108124 (+103    810                 
  6.  1319      5.38M     103       44108225 (+101    810                 
  7.  1319      5.39M     103       44108326 (+101    810                 
  8.  1319      5.40M     103       44108427 (+101    810                 
  9.  1319      5.41M     103       44108528 (+101    810                 





redis-stat host localhost port 6380 overview 

Measure Redis server latency; 
输出host为localhost,端口为6380,redis服务中每个请求的响应时长
 

 

  1. [root@Architect redis-1.2.6]# redis-stat port 6380 latency  
  2. 10.16 ms  
  3. 20.11 ms  
  4. 30.15 ms  
  5. 40.11 ms  
  6. 50.18 ms  
  7. 60.14 ms  

配置redis replication

 redis  配置redis replication已关闭评论
3月 062013
 

一、Redis的Replication:

这里首先需要说明的是,在Redis中配置Master-Slave模式真是太简单了。相信在阅读完这篇Blog之后你也可以轻松做到。这里我们还是先列出一些理论性的知识,后面给出实际操作的案例。
下面的列表清楚的解释了Redis Replication的特点和优势。
1). 同一个Master可以同步多个Slaves。
2). Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力。因此我们可以将Redis的Replication架构视为图结构。
3). Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求。
4). Slave Server同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据。
5). 为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务仍然必须由Master来完成。即便如此,系统的伸缩性还是得到了很大的提高。
6). Master可以将数据保存操作交给Slaves完成,从而避免了在Master中要有独立的进程来完成此操作。

二、Replication的工作原理:

在Slave启动并连接到Master之后,它将主动发送一个SYNC命令。此后Master将启动后台存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕后,Master将传送整个数据库文件到Slave,以完成一次完全同步。而Slave服务器在接收到数据库文件数据之后将其存盘并加载到内存中。此后,Master继续将所有已经收集到的修改命令,和新的修改命令依次传送给Slaves,Slave将在本次执行这些数据修改命令,从而达到最终的数据同步。
如果Master和Slave之间的链接出现断连现象,Slave可以自动重连Master,但是在连接成功之后,一次完全同步将被自动执行。

三、如何配置Replication:

见如下步骤:
1). 同时启动两个Redis服务器,可以考虑在同一台机器上启动两个Redis服务器,分别监听不同的端口,如6379和6380。
2). 在Slave服务器上执行一下命令:
 /> redis-cli -p 6380   #这里我们假设Slave的端口号是6380
redis 127.0.0.1:6380> slaveof 127.0.0.1 6379 #我们假设Master和Slave在同一台主机,Master的端口为6379
OK
上面的方式只是保证了在执行slaveof命令之后,redis_6380成为了redis_6379的slave,一旦服务(redis_6380)重新启动之后,他们之间的复制关系将终止。
如果希望长期保证这两个服务器之间的Replication关系,可以在redis_6380的配置文件中做如下修改:
/> cd /etc/redis  #切换Redis服务器配置文件所在的目录。
/> ls
6379.conf  6380.conf
/> vi 6380.conf

    # slaveof <masterip> <masterport>
改为
    slaveof 127.0.0.1 6379
保存退出。
这样就可以保证Redis_6380服务程序在每次启动后都会主动建立与Redis_6379的Replication连接了。

四、应用示例:

这里我们假设Master-Slave已经建立。
    #启动master服务器。
[[email protected] redis]# redis-cli -p 6379
redis 127.0.0.1:6379>
    #情况Master当前数据库中的所有Keys。
redis 127.0.0.1:6379> flushdb
OK
    #在Master中创建新的Keys作为测试数据。
redis 127.0.0.1:6379> set mykey hello
OK
redis 127.0.0.1:6379> set mykey2 world
OK
    #查看Master中存在哪些Keys。
redis 127.0.0.1:6379> keys *
1) “mykey”
2) “mykey2”

    #启动slave服务器。
[[email protected] redis]# redis-cli -p 6380
    #查看Slave中的Keys是否和Master中一致,从结果看,他们是相等的。
redis 127.0.0.1:6380> keys *
1) “mykey”
2) “mykey2”

    #在Master中删除其中一个测试Key,并查看删除后的结果。
redis 127.0.0.1:6379> del mykey2
(integer) 1
redis 127.0.0.1:6379> keys *
1) “mykey”

    #在Slave中查看是否mykey2也已经在Slave中被删除。
redis 127.0.0.1:6380> keys *
1) “mykey”

在多台服务器上简单实现Redis的数据主从复制

 redis  在多台服务器上简单实现Redis的数据主从复制已关闭评论
3月 062013
 

Redis的主从复制功能非常强大,一个master可以拥有多个slave,而一个slave又可以拥有多个slave,如此下去,形成了强大的多级服务器集群架构。下面我演示下怎样在多台服务器上进行Redis数据主从复制。这里我假设有两台服务器,一台是Windows操作系统(局域网IP:192.168.3.82),一台是Linux操作系统(局域网IP:192.168.3.90),在两个操作系统都安装redis,Windows操作系统使用cygwin工具进行安装,命令为:

1
2
3
$ tar xzf redis-2.2.2.tar.gz
$ cd redis-2.2.2
$ make

可以通过”make test”命令判断是否安装成功。

 

这里我使用1个master以及2个slave(master在Windows下,一个slave在Windows下,一个slave在Linux下),基本流程是:

image

 

1. 在Windows服务器上创建两个目录,Demo1,Demo2,其中Demo1用来存放Master服务,Demo2用来存放Slave服务,

在Master服务中的配置文件修改:

1
bind 192.168.3.82

 

在Slave服务中的配置文件修改:

1
2
3
port 6381(服务端口号要分开)
bind 192.168.3.82
slaveof 192.168.3.82 6379 (设置master的Host以及Port)

 

2. 在Linux服务器上创建一个目录,Demo,Demo存放Slave服务,在服务中的配置文件修改:

1
2
bind 192.168.3.90
slaveof 192.168.3.82 6379(设置master的Host以及Port)

 

这样就完成了所有的配置。

 

3. 现在运行这3个服务,通过命令:

1
./redis-server redis.conf

来启动redis服务。

 

注意到,当我启动master,然后启动一个slave的时候,可以发现slave上:

image

会发送一个SYNC请求,从Master上面进行相应,而且它支持自动重连,即当master掉线的情况下,它会处于等待请求的状态。

而Master上:

image

能够接受Slave的应答,并且开始持久化操作,说明在Slave每次去连接Master的时候,都会去持久化磁盘。

 

4. 现在开始写一个客户端程序,使用到ServiceStack.Redis.dll的.NET组件:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using ServiceStack.Redis;
static void Main(string[] args)
{
    IRedisClientFactory factory = new RedisCacheClientFactory();
    IRedisClient client = factory.CreateRedisClient("192.168.3.82", 6379);
    client.Set<string>("username", "leepy");
    string username = client.Get<string>("username");
    client.Save();
    Console.WriteLine("username: {0}", username);
    Console.ReadLine();
}</string></string>

 

运行结果:

image

数据Set的时候,数据保存在内存中,当调用Save方法时候,将数据保存在磁盘中。

其中你会发现在3个服务目录中,都出现了dump.rdb,说明Master的文件都同步到Slave中去了。

image

image

用UE编辑器打开文件查看:

image
从Redis源码中,可以发现rdb文件采用的是lzf压缩算法进行实现,默认lzf压缩算法是开启的。

 

这样你可以通过其他的客户端程序或者Web平台去读取Slave磁盘数据库的数据,真正达到了读写分离的目的。


作者:Leepy
邮箱:sunleepy(AT)gmail.com

Redis RDB文件格式全解析

 redis  Redis RDB文件格式全解析已关闭评论
3月 062013
 

RDB文件是Redis持久化的一种方式,Redis通过制定好的策略,按期将内存中的数据以镜像的形式转存到RDB文件中。那么RDB文件内部格式是什么样的呢,Redis又做了哪些工作让RDB能够更快的dump和加载呢,下面我们深入RDB文件,来看一看其内部结构。

首先我们来看一个RDB文件的概况图:

----------------------------# RDB文件是二进制的,所以并不存在回车换行来分隔一行一行.
52 45 44 49 53              # 以字符串 "REDIS" 开头
30 30 30 33                 # RDB 的版本号,大端存储,比如左边这个表示版本号为0003
----------------------------
FE 00                       # FE = FE表示数据库编号,Redis支持多个库,以数字编号,这里00表示第0个数据库
----------------------------# Key-Value 对存储开始了
FD $length-encoding         # FD 表示过期时间,过期时间是用 length encoding 编码存储的,后面会讲到
$value-type                 # 1 个字节用于表示value的类型,比如set,hash,list,zset等
$string-encoded-key         # Key 值,通过string encoding 编码,同样后面会讲到
$encoded-value              # Value值,根据不同的Value类型采用不同的编码方式
----------------------------
FC $length-encoding         # FC 表示毫秒级的过期时间,后面的具体时间用length encoding编码存储
$value-type                 # 同上,也是一个字节的value类型
$string-encoded-key         # 同样是以 string encoding 编码的 Key值
$encoded-value              # 同样是以对应的数据类型编码的 Value 值
----------------------------
$value-type                 # 下面是没有过期时间设置的 Key-Value对,为防止冲突,数据类型不会以 FD, FC, FE, FF 开头
$string-encoded-key
$encoded-value
----------------------------
FE $length-encoding         # 下一个库开始,库的编号用 length encoding 编码
----------------------------
...                         # 继续存储这个数据库的 Key-Value 对
FF                          ## FF:RDB文件结束的标志

下面我们对上面的内容进行详细讲解

Magic Number

第一行就不用讲了,REDIS字符串用于标识是Redis的RDB文件

版本号

用了4个字节存储版本号,以大端(big endian)方式存储和读取

数据库编号

以一个字节的0xFE开头,后面存储数据库的具体编号,数据库的编号是一个数字,通过 “Length Encoding” 方式编码存储,“Length Encoding” 我们后面会讲到。

Key-Value值对

值对包括下面四个部分

  1. Key 过期时间,这一项是可有可无的
  2. 一个字节表示value的类型
  3. Key的值,Key都是字符串,通过 “Redis String Encoding” 来保存
  4. Value的值,通过 “Redis Value Encoding” 来根据不同的数据类型做不同的存储

Key过期时间

过期时间由 0xFD 或 0xFC开头用于标识,分别表示秒级的过期时间和毫秒级的过期时间,后面的具体时间是一个UNIX时间戳,秒级或毫秒级的。具体时间戳的值通过“Redis Length Encoding” 编码存储。在导入RDB文件的过程中,会通过过期时间判断是否已过期并需要忽略。

Value类型

Value类型用一个字节进行存储,目前包括以下一些值:

  • 0 = “String Encoding”
  • 1 = “List Encoding”
  • 2 = “Set Encoding”
  • 3 = “Sorted Set Encoding”
  • 4 = “Hash Encoding”
  • 9 = “Zipmap Encoding”
  • 10 = “Ziplist Encoding”
  • 11 = “Intset Encoding”
  • 12 = “Sorted Set in Ziplist Encoding”

Key

Key值就是简单的 “String Encoding” 编码,具体可以看后面的描述

Value

上面列举了Value的9种类型,实际上可以分为三大类

  • type = 0, 简单字符串
  • type 为  9, 10, 11 或 12, value字符串在读取出来后需要先解压
  • type 为 1, 2, 3 或 4, value是字符串序列,这一系列的字符串用于构建list,set,hash 和 zset 结构

Length Encoding

上面说了很多 Length Encoding ,现在就为大家讲解。可能你会说,长度用一个int存储不就行了吗?但是,通常我们使用到的长度可能都并不大,一个int 4个字节是否有点浪费呢。所以Redis采用了变长编码的方法,将不同大小的数字编码成不同的长度。

  1. 首先在读取长度时,会读一个字节的数据,其中前两位用于进行变长编码的判断
  2. 如果前两位是 0 0,那么下面剩下的 6位就表示具体长度
  3. 如果前两位是 0 1,那么会再读取一个字节的数据,加上前面剩下的6位,共14位用于表示具体长度
  4. 如果前两位是 1 0,那么剩下的 6位就被废弃了,取而代之的是再读取后面的4 个字节用于表示具体长度
  5. 如果前两位是 1 1,那么下面的应该是一个特殊编码,剩下的 6位用于标识特殊编码的种类。特殊编码主要用于将数字存成字符串,或者编码后的字符串。具体见 “String Encoding”

这样做有什么好处呢,实际就是节约空间:

  1. 0 – 63的数字只需要一个字节进行存储
  2. 而64 – 16383 的数字只需要两个字节进行存储
  3. 16383 – 2^32 -1 的数字只需要用5个字节(1个字节的标识加4个字节的值)进行存储

String Encoding

Redis的 String Encoding 是二进制安全的,也就是说他没有任何特殊分隔符用于分隔各个值,你可以在里面存储任何东西。它就是一串字节码。

下面是 String Encoding 的三种类型

  1. 长度编码的字符串
  2. 数字替代字符串:8位,16位或者32位的数字
  3. LZF 压缩的字符串

长度编码字符串

长度编码字符串是最简单的一种类型,它由两部分组成,一部分是用 “Length Encoding” 编码的字符串长度,第二部分是具体的字节码。

数字替代字符串

上面说到过 Length Encoding 的特殊编码,就在这里用上了。所以数字替代字符串是以 1 1 开头的,然后读取这个字节剩下的6 位,根据不同的值标识不同的数字类型:

  • 0 表示下面是一个8 位的数字
  • 1 表示下面是一个16 位的数字
  • 2 表示下面是一个32 位的数字

LZF压缩字符串

和数据替代字符串一样,它也是以1 1 开头的,然后剩下的6 位如果值为4,那么就表示它是一个压缩字符串。压缩字符串解析规则如下:

  1. 首先按 Length Encoding 规则读取压缩长度 clen
  2. 然后按 Length Encoding 规则读取非压缩长度
  3. 再读取第二个 clen
  4. 获取到上面的三个信息后,再通过LZF算法解码后面clen长度的字节码

List Encoding

Redis List 结构在RDB文件中的存储,是依次存储List中的各个元素的。其结构如下:

  1. 首先按 Length Encoding 读取这个List 的长度 size
  2. 然后读取 size个 String Encoding的值
  3. 然后再用这些读到的 size 个值重新构建 List就完成了

Set Encoding

Set结构和List结构一样,也是依次存储各个元素的

Sorted Set Encoding

todo

Hash Encoding

  1. 首先按 Length Encoding 读出hash 结构的大小 size
  2. 然后读取2×size 个 String Encoding的字符串(因为一个hash项包括key和value两项)
  3. 将上面读取到的2×size 个字符串解析为hash 和key 和 value
  4. 然后将上面的key value对存储到hash结构中

 

转自:http://blog.nosqlfan.com/html/3734.html

 Posted by at 上午10:19  Tagged with:

ubuntu下redis安装配置

 redis, ubuntu  ubuntu下redis安装配置已关闭评论
2月 052013
 

wget http://redis.googlecode.com/files/redis-2.2.4.tar.gz
也可以到redis.io官网下载最新的包

tar -zxf redis-2.2.4.tar.gz
cd redis-2.2.4
make
sudo make install
2. 配置init脚本:

wget https://github.com/ijonas/dotfiles/raw/master/etc/init.d/redis-server
wget https://github.com/ijonas/dotfiles/raw/master/etc/redis.conf
如果错误认证,添加参数 –no-check-certificate

sudo mv redis-server /etc/init.d/redis-server
sudo chmod +x /etc/init.d/redis-server
sudo mv redis.conf /etc/redis.conf

3. 初始化用户和日志路径
第一次启动Redis前,建议为Redis单独建立一个用户,并新建data和日志文件夹
sudo useradd redis
sudo mkdir -p /var/lib/redis
sudo mkdir -p /var/log/redis
sudo chown redis.redis /var/lib/redis
sudo chown redis.redis /var/log/redis

4、设置开机自动启动,关机自动关闭
sudo update-rc.d redis-server defaults

5. 启动Redis:
sudo /etc/init.d/redis-server start

6.启动客户端连接
/usr/local/bin/redis-cli

添加一个服务
#sudo update-rc.d 服务名 defaults 99
删除一个服务
#sudo update-rc.d 服务名 remove
临时重启一个服务
#/etc/init.d/服务名 restart
临时关闭一个服务
#/etc/init.d/服务名 stop
临时启动一个服务
#/etc/init.d/服务名 start

要在虚拟机下使用redis,需要在redis.conf配置文件中加入really-use-vm true

转自: http://who0168.blog.51cto.com/253401/626345