Redis学习10–Java Drivers

Java驱动实践
  1. Redis的客户端有主要有三种:JDBC-Redis, JRedis和Jedis,推荐Jedis的方式;
  2. Jedis API Online Help:http://www.jarvana.com/jarvana/view/redis/clients/jedis/2.0.0/jedis-2.0.0-javadoc.jar!/index.html;
  3. Jredis使用总结:
    1. pipeline:starts a pipeline,which is a very efficient way to send lots of command and read all the responses when you finish sending them;即pipeline适用于批处理,当有大量的操作需要一次性执行的时候,可以用管道;
    2. 分布式的id生成器:因为redis-server是单线程处理client端的请求的,所以可以使用jedis.incr(“id_key”)来生成序列;
    3. 分布式锁watch/multi:可以用来实现跨jvm的同步问题;
      1. 方法1:Jedis.setnx(key, value),推荐的方法;
      2. 方法2:事务multi;
      3. 方法3:事务+监听;
    4. redis分布式:jedis里面通过MD5,MURMUR Hash(默认)两种方式实现了分布式,也可以自己实现redis.clients.util.Hashing接口;
  4. Jedis中Pool的问题:It seems like server has closed the connection;
    1. 原因:redis-server关闭了此客户端的连接,server端设置了maxidletime(默认是5分钟),服务端会不断循环检测clinet的最后一次通信时间(lastinteraction),如果大于maxidletime,则关闭连接,并回收相关资源,client在向该连接中写数据后就会由于server端已经关闭而出现broken pipe的问题;
    2. 错误的配置:在spring初始化时获取一次实例化jedisCommands,而后每次的redis的调用时并未从pool中获取;
    3. 解决办法;

———————– Pipeline ———————–

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
 
public class Redis
{
     public static void main(String[] args)
     {
           // 连接Redis 服务器;
          Jedis jedis = new Jedis( “192.168.10.112”, 6379);
           // 生成Pipeline;
          Pipeline pipeline = jedis.pipelined();
           // 管道操作,会把所有的操作发送给服务器端,然后一次性执行;
           // ……
          pipeline.incr( “key”);
           // 获得所有的结果;
          pipeline.sync();
     }
}
———————– Pipeline ———————–
———————– Jedis.setnx(key, value) ———————–
import java.util.Random;
import org.apache.commons.pool.impl.GenericObjectPool.Config;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
 
public class RedisLock
{
     // 加锁标志
     public static final String    LOCKED                  = “TRUE”;
     public static final long      ONE_MILLI_NANOS         = 1000000L;
     
     // 默认超时时间(ms )
     public static final long      DEFAULT_TIME_OUT   = 3000;
     public static JedisPool       pool;
     public static final Random    r                       = new Random();
     
     // 锁的超时时间(s),过期删除
     public static final int       EXPIRE                  = 5 * 60;
     static
     {
           pool = new JedisPool( new Config(), “host”, 6379);
     }
     
     private Jedis                 jedis;
     private String                key;
     
     // 锁状态标志
     private boolean                    locked                  = false;
     
     public RedisLock(String key)
     {
           this. key = key;
           this. jedis = pool.getResource();
     }
     
     public boolean lock( long timeout)
     {
           long nano = System. nanoTime();
          timeout *= ONE_MILLI_NANOS;
           try
          {
               while ((System. nanoTime() – nano) < timeout)
              {
                    if ( jedis.setnx( key, LOCKED) == 1)
                   {
                         jedis.expire( key, EXPIRE);
                         this. locked = true ;
                         return locked;
                   }
                    // 短暂休眠,nano避免出现活锁
                   Thread. sleep(3, r.nextInt(500));
              }
          }
           catch (Exception e)
          {
              e.printStackTrace();
          }
           return false;
     }
     
     public boolean lock()
     {
           return lock( DEFAULT_TIME_OUT);
     }
     
     // 无论是否加锁成功,必须调用
     public void unlock()
     {
           try
          {
               if ( locked)
              {
                    jedis.del( key);
              }
          }
           finally
          {
               pool.returnResource( jedis);
          }
     }
}
———————– Jedis.setnx(key, value) ———————–
———————– 事务multi ———————–
     public boolean lock_2( long timeout)
     {
           long nano = System. nanoTime();
          timeout *= ONE_MILLI_NANOS;
           try
          {
               while ((System. nanoTime() – nano) < timeout)
              {
                   Transaction t = jedis.multi();
                    // 开启事务,当server端收到 multi指令;
                    // 会将该client的命令放入一个队列,然后依次执行,直到收到 exec指令;
                   t.getSet( key, LOCKED);
                   t.expire( key, EXPIRE);
                   String ret = (String) t.exec().get(0);
                    if (ret == null || ret.equals(“UNLOCK”))
                   {
                         return true;
                   }
                    // 短暂休眠,nano避免出现活锁;
                   Thread. sleep(3, r.nextInt(500));
              }
          }
           catch (Exception e)
          {
          }
           return false;
     }
———————– 事务multi ———————–
———————– 事务+监听 ———————–
     public boolean lock_3( long timeout)
     {
           long nano = System. nanoTime();
          timeout *= ONE_MILLI_NANOS;
           try
          {
               while ((System. nanoTime() – nano) < timeout)
              {
                    jedis.watch( key);
                    // 开启watch之后,如果key的值被修改,则事务失败, exec方法返回null;
                   String value = jedis.get( key);
                    if (value == null || value.equals(“UNLOCK”))
                   {
                        Transaction t = jedis.multi();
                        t.setex( key, EXPIRE, LOCKED);
                         if (t.exec() != null)
                        {
                              return true;
                        }
                   }
                    jedis.unwatch();
                    // 短暂休眠,nano避免出现活锁;
                   Thread. sleep(3, r.nextInt(500));
              }
          }
           catch (Exception e)
          {
          }
           return false;
     }
———————– 事务+监听 ———————–
———————– redis分布式 ———————–
List<JedisShardInfo> hosts = new ArrayList<JedisShardInfo>();
// server1
JedisShardInfo host1 = new JedisShardInfo( “”, 6380, 2000);
// server2
JedisShardInfo host2 = new JedisShardInfo( “”, 6381, 2000);
hosts.add(host1);
hosts.add(host2);
 
ShardedJedis jedis = new ShardedJedis(hosts);
jedis.set(“key”, “”);
———————– redis分布式 ———————–
———————– 错误的配置 ———————–
<bean id=”jedisPoolConfig” class=”redis.clients.jedis.JedisPoolConfig”>
<property name=”maxActive”  value=”20″ />
<property name=”maxIdle” value=”10″ />
<property name=”maxWait” value=”1000″ />
</bean>

<!– jedis shard信息配置 –>
<bean id=”jedis.shardInfo” class=”redis.clients.jedis.JedisShardInfo”>
<constructor-arg index=”0″ value=”*.*.*.*” />
<constructor-arg index=”1″ value=”6379″ />
</bean>

<!– jedis shard pool配置 –>
<bean id=”shardedJedisPool” class=”redis.clients.jedis.ShardedJedisPool”>
<constructor-arg index=”0″ ref=”jedisPoolConfig” />
<constructor-arg index=”1″>
<list>
<ref bean=”jedis.shardInfo” />
</list>
</constructor-arg>
</bean>

<bean id=”jedisCommands” factory-bean=”shardedJedisPool” factory-method=”getResource” />

———————– 错误的配置 ———————–
———————– 解决办法 ———————–
<!– POOL配置 –>
<bean id=”jedisPoolConfig” class=”redis.clients.jedis.JedisPoolConfig”>
<property name=”maxActive”  value=”20″ />
<property name=”maxIdle” value=”10″ />
<property name=”maxWait” value=”1000″ />
<property name=”testOnBorrow”  value=”true”/>
</bean>

<!– jedis shard信息配置 –>
<bean id=”jedis.shardInfo” class=”redis.clients.jedis.JedisShardInfo”>
<constructor-arg index=”0″ value=”*.*.*.*” />
<constructor-arg index=”1″ value=”6379″ />
</bean>

<!– jedis shard pool配置 –>
<bean id=”shardedJedisPool” class=”redis.clients.jedis.ShardedJedisPool”>
<constructor-arg index=”0″ ref=”jedisPoolConfig” />
<constructor-arg index=”1″>
<list>
<ref bean=”jedis.shardInfo” />
</list>
</constructor-arg>
</bean>

———————– 解决办法 ———————–

Redis学习09–虚拟内存

Redis中的虚拟内存
  1. Redis中虚拟内存的概念:
    1. redis的虚拟内存与os的虚拟内存不是同一个概念,但是实现的方法和目的是相同的,就是暂时把不经常访问的数据从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据;
    2. 对于redis这样的内存数据库,内存总是不够用的,除了可以将数据分割到多个redis server外,另外的能够提高数据库容量的办法就是使用vm把那些不经常访问的数据交换的磁盘上;
    3. 总是有少部分数据被经常访问,大部分数据很少被访问,对于网站来说确实总是只有少量用户经常活跃,当少量数据被经常访问时,使用vm不但能提高单台redis server数据库的容量,而且也不会对性能造成太多影响;
  2. Redis没有使用os提供的虚拟内存机制而是自己在用户态实现了自己的虚拟内存机制,主要原因为:
    1. os的虚拟内存是以4k页面为最小单位进行交换的,而redis的大多数对象都远小于4k,所以一个os页面上可能有多个redis对象;
    2. 另外redis的集合对象类型如list,set可能存在与多个os页面上,最终可能造成只有10%的key被经常访问,但是所有os页面都会被os认为是活跃的,这样只有内存真正耗尽时os才会交换页面;
    3. 相比于os的交换方式,redis可以将被交换到磁盘的对象进行压缩,保存到磁盘的对象可以去除指针和对象元数据信息,一般压缩后的对象会比内存中的对象小10倍,这样redis的vm会比osvm能少做很多io操作;
  3. VM的配置:
    1. vm-enabled yes                     # 开启vm功能;
    2. vm-swap-file /tmp/redis.swap       # 交换出来的value保存的文件路径/tmp/redis.swap;
    3. vm-max-memory 1000000              # redis使用的最大内存上限,超过上限后redis开始交换value到磁盘文件中;
    4. vm-page-size 32                    # 每个页面的大小32个字节;
    5. vm-pages 134217728                 # 最多使用在文件中使用多少页面,交换文件的大小 = vm-page-size * vm-pages
    6. vm-max-threads 4                   # 用于执行value对象换入换出的工作线程数量,推荐设置为cpu的核心数,0表示不使用工作线程;
  4. 参数的解释:
    1. redis的vm在设计上为了保证key的查找速度,只会将value交换到swap文件中,所以如果是内存问题是由于太多value很小的key造成的,那么vm并不能解决;
    2. 和os一样redis也是按页面来交换对象的,redis规定同一个页面只能保存一个对象,但是一个对象可以保存在多个页面中;
    3. 在redis使用的内存没超过vm-max-memory之前是不会交换任何value的,当超过最大内存限制后,redis会选择较老的对象,如果两个,对象一样老会优先交换比较大的对象,精确的公式swappability = age log(size_in_memory);
    4. 对于vm-page-size的设置应该根据自己的应用将页面的大小设置为可以容纳大多数对象的大小,太大了会浪费磁盘空间,太小了会造成交换文件出现碎片;
    5. 对于交换文件中的每个页面,redis会在内存中对应一个1bit值来记录页面的空闲状态,所以像上面配置中页面数量(vm-pages 134217728)会占用16M(134217728/8/1024/1024)内存用来记录页面空闲状态;
    6. vm-max-threads表示用做交换任务的线程数量,如果大于0推荐设为服务器的cpu core的数量,如果是0则交换过程在主线程进行;
  5. vm的工作原理:
    1. 当vm-max-threads设为0时(Blocking VM):
      1. 换出:主线程定期检查发现内存超出最大上限后,会直接已阻塞的方式,将选中的对象保存到swap文件中,并释放对象占用的内存,此过程会一直重复直到下面条件满足
        1. 内存使用降到最大限制以下;
        2. swap文件满了;
        3. 几乎全部的对象都被交换到磁盘了;
      2. 换入:当有client请求value被换出的key时,主线程会以阻塞的方式从文件中加载对应的value对象,加载时此时会阻塞所有的client,然后处理client的请求;
    2. 当vm-max-threads大于0(Threaded VM):
      1. 换出:当主线程检测到使用内存超过最大上限,会将选中的要交换的对象信息放到一个队列中交由工作线程后台处理,主线程会继续处理client请求;
      2. 换入:如果有client请求的key被换出了,主线程先阻塞发出命令的client,然后将加载对象的信息放到一个队列中,让工作线程去加载,加载完毕后工作线程通知主线程,主线程再执行client的命令,这种方式只阻塞请求value被换出key的client;
    3. 总的来说blocking vm的方式总的性能会好一些,因为不需要线程同步,创建线程和恢复被阻塞的client等开销,但是也相应的牺牲了响应性;threaded vm的方式主线程不会阻塞在磁盘io上,所以响应性更好;如果应用不太经常发生换入换出,而且也不太在意有点延迟的话则推荐使用blocking vm的方式;
  6. 相关链接:
    1. http://antirez.com/post/redis-virtual-memory-story.html;
    2. http://redis.io/topics/internals-vm;

Redis学习08–Master-Slave架构

Redis的主从架构
  1. redis主从复制的配置和使用都非常简单,通过主从复制可以允许多个slave servers和master server具有相同的数据库副本;
  2. Master-Salve的特点:
    1. master可以有多个slaves;
    2. 除了多个slave连到相同的master外,slave也可以连接其他slave形成图状结构;
    3. 主从复制不会阻塞master,也就是说当一个或多个slave与master进行初次同步数据时,master可以继续处理client发来的请求,相反slave在初次同步数据时则会阻塞不能处理client的请求;
    4. 从复制可以用来提高系统的可伸缩性,我们可以用多个slave专门用于client的读请求,比如sort操作可以使用slave来处理,也可以用来做简单的数据冗余;
    5. 可以在master禁用数据持久化,只需要注释掉master配置文件中的所有save配置,然后只在slave上配置数据持久化;
  3. Master-Slave的过程:
    1. 当设置好slave服务器后,slave会建立和master的连接,然后发送sync命令;
    2. 无论是第一次同步建立的连接还是连接断开后的重新连接,master都会启动一个后台进程,将数据库快照保存到文件中,同时master主进程会开始收集新的写命令并缓存起来;
    3. 后台进程完成写文件后,master就发送文件给slave,slave将文件保存到磁盘上,然后加载到内存恢复数据库快照到slave上;
    4. 接着master就会把缓存的命令转发给slave,而且后续master收到的写命令都会通过开始建立的连接发送给slave;
    5. 从master到slave的同步数据的命令和从client发送的命令使用相同的协议格式;
    6. 当master和slave的连接断开时slave可以自动重新建立连接,如果master同时收到多个slave发来的同步连接命令,只会使用启动一个进程来写数据库镜像,然后发送给所有slave;
  4. 配置slave服务器很简单,只需要在配置文件中加入如下配置:slaveof 192.168.1.1 6379  #指定master的ip和端口;

Redis学习07–持久化

Redis的数据持久化
  1. redis是一个支持持久化的内存数据库,也就是说redis需要经常将内存中的数据同步到磁盘来保证持久化,redis支持两种持久化方式:
    1. Snapshotting(快照)也是默认方式;
    2. Append-only file(缩写aof)的方式;
  2. Snapshotting:
    1. 快照是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb;
    2. 可以通过配置设置自动做快照持久化的方式,我们可以配置redis在n秒内如果超过m个key被修改就自动做快照,下面是默认的快照保存配置:
      1. save 900 1  #900s内如果超过1个key被修改,则发起快照保存;
      2. save 300 10 #300s内如果超过10个key被修改,则发起快照保存;
      3. save 60 10000 # 10s内,如果超过10000个key被修改,则发起快照保存;
    3. 快照保存过程:
      1. redis调用fork,现在有了子进程和父进程;
      2. 父进程继续处理client请求,子进程负责将内存内容写入到临时文件,由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面,所以子进程的地址空间内的数据是fork时刻整个数据库的一个快照;
      3. 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出;
    4. 快照持久化的注意事项:
      1. client也可以使用save或者bgsave命令通知redis做一次快照持久化,save操作是在主线程中保存快照的,由于redis是用一个主线程来处理所有client的请求,这种方式会阻塞所有client请求,所以不推荐使用;
      2. 需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步脏数据,如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能;
      3. 另外由于快照方式是在一定间隔时间做一次的,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改,如果应用要求不能丢失任何修改的话,可以采用aof持久化方式;
  3. Append-only file:
    1. aof比快照方式有更好的持久化性,是由于在使用aof持久化方式时,redis会将每一个收到的写命令都通过write函数追加到文件中(默认是appendonly.aof),当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容,当然由于os会在内核中缓存write做的修改,所以可能不是立即写到磁盘上,这样aof方式的持久化也还是有可能会丢失部分修改,不过我们可以通过配置文件告诉redis我们想要通过fsync函数强制os写入到磁盘的时机:
      1. appendonly yes:启用aof持久化方式;
      2. appendfsync always:每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用;
      3. appendfsync everysec:每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐,也是默认方式;
      4. appendfsync no:完全依赖os,性能最好,持久化没保证;
    2. aof的方式也同时带来了另一个问题,持久化文件会变的越来越大;例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的,因为要恢复数据库的状态其实文件中保存一条set test 100就够了;为了压缩aof的持久化文件,redis提供了bgrewriteaof命令,收到此命令redis将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的文件;
    3. bgrewriteaof命令原理:
      1. redis调用fork,现在有父子两个进程;
      2. 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令;
      3. 父进程继续处理client请求,除了把写命令写入到原来的aof文件中,同时把收到的写命令缓存起来,这样就能保证如果子进程重写失败的话并不会出问题;
      4. 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程,然后父进程把缓存的写命令也写入到临时文件;
      5. 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加;
    4. 需要注意到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似;

Redis学习06–发布及订阅

Redis的发布及订阅

  1. 发布订阅(pub/sub)是一种消息通信模式,主要的目的是解决消息发布者和消息订阅者之间的耦合,这点和设计模式中的观察者模式比较相似;pub/sub不仅仅解决发布者和订阅者直接代码级别耦合也解决两者在物理部署上的耦合;
  2. redis作为一个pub/sub server,在订阅者和发布者之间起到了消息路由的功能,订阅者可以通过subscribe和psubscribe命令向redis server订阅自己感兴趣的消息类型,redis将消息类型称为通道(channel);
  3. 当发布者通过publish命令向redis server发送特定类型的消息时,订阅该消息类型的全部client都会收到此消息;这里消息的传递是多对多的,一个client可以订阅多个channel,也可以向多个channel发送消息;
  4. 发布/订阅的命令:
    1. SUBSCRIBE channel [channel …]:Subscribes the client to the specified channels,Once the client enters the subscribed state it is not supposed to issue any other commands, except for additional SUBSCRIBE, PSUBSCRIBE, UNSUBSCRIBE and PUNSUBSCRIBE commands;
    2. PSUBSCRIBE pattern [pattern …]:Subscribes the client to the given patterns;
    3. UNSUBSCRIBE [channel [channel …]]:Unsubscribes the client from the given channels, or from all of them if none is given,When no channels are specified, the client is unsubscribed from all the previously subscribed channels. In this case, a message for every unsubscribed channel will be sent to the client;
    4. PUNSUBSCRIBE [pattern [pattern …]]:Unsubscribes the client from the given patterns;
    5. PUBLISH channel message:Posts a message to the given channel; Integer reply:the number of clients that received the message;
  5. 开启三个redis-cli测试发布订阅的例子;
  6. redis的协议是文本类型的,具体链接为:http://redis.io/topics/protocol;
  7. 使用java客户端来订阅消息;
———————— 发布/订阅测试 ————————
— 1.打开第一个redis-cli的客户端订阅news:snda和news:taobao两个channels,收到redis-server返回订阅成功的消息;
redis 127.0.0.1:6379> subscribe news:snda news:taobao
Reading messages… (press Ctrl-C to quit)
1) “subscribe”
2) “news:snda”
3) (integer) 1
1) “subscribe”
2) “news:taobao”
3) (integer) 2
— 2.打开第二个redis-cli客户端,使用psubscribe订阅news:*模式(*表示任意字符串)的channels;
redis 127.0.0.1:6379> psubscribe news:*
Reading messages… (press Ctrl-C to quit)
1) “psubscribe”
2) “news:*”
3) (integer) 1
— 3.打开第三个redis-cli客户端,通过publish命令发布消息,返回值表示接收到此订阅的个数,通过订阅的窗口查看消息;
redis 127.0.0.1:6379> publish news:snda “www.snda.com”
(integer) 2
redis 127.0.0.1:6379> publish news:taobao “www.taobao.com”
(integer) 2
———————— 发布/订阅测试 ————————
———————— 使用java客户端来订阅消息 ————————
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
 
public class PubSubTest
{
     private static Socket    socket;
     
     public static void main(String[] args)
     {
           // String cmd = args[0] + “\r\n”;
          String cmd = “subscribe news:snda news:taobao \r\n”;
           try
          {
               socket = new Socket( “192.168.10.112”, 6379);
              InputStream in = socket.getInputStream();
              OutputStream out = socket.getOutputStream();
              out.write(cmd.getBytes());
               // 发送订阅命令         
               byte[] buffer = new byte[1024];
               while ( true)
              {
                    int readCount = in.read(buffer);
                   System. out.write(buffer, 0, readCount);
                   System. out.println( “————————————–“ );
              }
          }
           catch (Exception e)
          {
              e.printStackTrace();
          }
     }
}
 
———————— 使用java客户端来订阅消息 ————————

Redis学习05–Pipeline

Redis中的Pipeline
  1. Pipeline是打包多条命令发送给服务端,服务端处理完多条命令后将结果打包一起返回的方式;
  2. redis是一个cs模式的tcp server,使用和http类似的请求响应协议,一个client可以通过一个socket连接发起多个请求命令;每个请求命令发出后client通常会阻塞并等待redis服务处理,redis处理完后请求命令后会将结果通过响应报文返回给client;
  3. 模拟的通信过程;                                                
  4. 基本上四个命令需要8个tcp报文才能完成,由于通信会有网络延迟,假如从client和server之间的包传输时间需要0.125秒,那么上面的四个命令8个报文至少会需要1秒才能完成,这样即使redis每秒能处理100个命令,而我们的client也只能一秒钟发出四个命令,这显示没有充分利用redis的处理能力;
  5. 可以利用mget,mset之类的单条命令处理多个key的命令外,还可以利用pipeline的方式从client打包多条命令一起发出,不需要等待单条命令的响应返回,而redis服务端会处理完多条命令后会将多条命令的处理结果打包到一起返回给客户端;
  6. 假设不会因为tcp报文过长而被拆分,可能两个tcp报文就能完成四条命令,client可以将四个incr命令放到一个tcp报文一起发送,server则可以将四条命令的处理结果放到一个tcp报文返回;
    通过pipeline方式当有大批量的操作时候,我们可以节省很多原来浪费在网络延迟的时间,需要注意到是用pipeline方式打包命令发送,redis必须在处理完所有命令前先缓存起所有命令的处理结果,打包的命令越多,缓存消耗内存也越多;所以并是不是打包的命令越多越好;
  7. 使用Jedis测试Pipeline的过程,发现使用pipeline节省很多时间;                        
package com.snda.study.redis;
 
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
 
public class PipeLineTest
{
     public static void main(String[] args)
     {
           // 不使用pipeline的时间消耗;
           long start = System. currentTimeMillis();
           withoutPipeline();
           long end = System. currentTimeMillis();
          System. out.println( “WithoutPipeline:” + (end – start));
          
           // 使用pipeline的时间消耗;
          start = System. currentTimeMillis();
           usePipeline();
          end = System. currentTimeMillis();
          System. out.println( “UsePipeline:” + (end – start));
     }
     
     /**
      * 不使用pipeline的方式,循环N次,需要与服务器端交互2N次;
      */
     private static void withoutPipeline()
     {
           try
          {
              Jedis jedis = new Jedis( “192.168.10.112”, 6379);
               for ( int i = 0; i < 100000; i++)
              {
                   jedis.incr( “withoutPipeline”);
              }
              jedis.quit();
          }
           catch (Exception e)
          {
              e.printStackTrace();
          }
     }
     
     /**
      * 使用pipeline,循环N次,会把操作打包发送给服务端,然后一次性返回;
      */
     private static void usePipeline()
     {
           try
          {
              Jedis jedis = new Jedis( “192.168.10.112”, 6379);
              Pipeline pipeline = jedis.pipelined();
               for ( int i = 0; i < 100000; i++)
              {
                   pipeline.incr( “usePipeline”);
              }
              pipeline.sync();
              jedis.quit();
          }
           catch (Exception e)
          {
              e.printStackTrace();
          }
     }
}

Redis学习04–对事务的处理

Redis中的事务
  1. redis对事务的支持目前还比较简单,redis只能保证一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令,由于redis是单线程来处理所有client的请求的所以做到这点是很容易的;
  2. 一般情况下redis在接受到一个client发来的命令后会立即处理并返回处理结果,但是当一个client在一个连接中发出multi命令时,这个连接会进入一个事务上下文,该连接后续的命令并不是立即执行,而是先放到一个队列中,当此连接收到exec命令后,redis会顺序的执行队列中的所有命令,并将所有命令的运行结果打包到一起返回给client,然后此连接就结束事务上下文;
  3. 命令解释:
    1. MULTI:Marks the start of a transaction block. Subsequent commands will be queued for atomic execution using EXEC,开启一个事务,相当于begin transaction;
    2. EXEC:Executes all previously queued commands in a transaction and restores the connection state to normal,When using WATCH, EXEC will execute commands only if the watched keys were not modified,执行事务队列里面的命令,相当于commit;
    3. DISCARD:Flushes all previously queued commands in a transaction and restores the connection state to normal,If WATCH was used, DISCARD unwatches all keys,取消事务队列里面的命令,相当于rollback;
    4. WATCH:Marks the given keys to be watched for conditional execution of a transaction,命令会监视给定的key,当exec时候如果监视的key从调用watch后发生过变化,则整个事务会失败;也可以调用watch多次监视多个key,这样就可以对指定的key加乐观锁了;
    5. 注意watch的key是对整个连接有效的,事务也一样,如果连接断开,监视和事务都会被自动清除,当然了exec, discard, unwatch命令都会清除连接中的所有监视;
  4. 使用multi命令的例子;
  5. 使用discard命令的例子;
  6. 使用watch命令的例子;
  7. Redis使用事务的bug:
    1. redis只能保证事务的每个命令连续执行,但是如果事务中的一个命令失败了,并不回滚其他命令,命令类型不匹配的例子;
    2. 当事务的执行过程中,如果redis服务宕机了,只有部分命令执行了,后面的也就被丢弃了;当然如果我们使用的append-only file方式持久化,redis会用单个write操作写入整个事务内容,即使是这种方式还是有可能只部分写入了事务到磁盘;发生部分写入事务的情况下,redis重启时会检测到这种情况,然后失败退出,可以使用redis-check-aof工具进行修复,修复会删除部分写入的事务内容,修复完后就能够重新启动了;

————————- multi命令 ————————-

— 1.可以看到incr a ,incr b命令发出后并没执行而是被放到了队列中;调用exec后两个命令被连续的执行,最后返回的是两条命令执行后的结果;
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> incr a
QUEUED
redis 127.0.0.1:6379> incr b
QUEUED
redis 127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 1

————————- multi命令 ————————-
————————- discard命令 ————————-
— 1.可以发现incr a incr b都没被执行;discard命令其实就是清空事务的命令队列并退出事务上下文;
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> incr a
QUEUED
redis 127.0.0.1:6379> incr b
QUEUED
redis 127.0.0.1:6379> discard
OK
redis 127.0.0.1:6379> exec
(error) ERR EXEC without MULTI
redis 127.0.0.1:6379> get a
“1”
redis 127.0.0.1:6379> get b
“1”
————————- discard命令 ————————-
————————- watch命令 ————————-
— 1.常规情况,设置a的值;如果是一个incr a的操作,使用以下命令去实现,由于get a和set a两个命令并不能保证是连续执行的(get操作不在事务上下文中),很可能有两个client同时做这个操作,结果期望是加两次a从原来的1变成3,但是很有可能两个client的get a,取到都是1,造成最终加两次结果却是2,主要问题我们没有对共享资源a的访问进行任何的同步;
也就是说redis没提供任何的加锁机制来同步对a的访问;
redis 127.0.0.1:6379> get a
“1”
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> set a 2
QUEUED
redis 127.0.0.1:6379> exec
1) OK
redis 127.0.0.1:6379> get a
“2”
— 2.使用watch命令对变量加锁;
redis 127.0.0.1:6379> watch a
OK
redis 127.0.0.1:6379> get a
“2”
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> set a 3
QUEUED
redis 127.0.0.1:6379> exec
1) OK
redis 127.0.0.1:6379> get a
“3”
— 3.被watch的key如果在事务在exec之前被修改了,则返回错误;
————————- watch命令 ————————-
————————- Redis中事务的bugs ————————-
— 1.数据类型不一致导致事务失败;
redis 127.0.0.1:6379> set a 1
OK
redis 127.0.0.1:6379> lpush list1 5
(integer) 1
redis 127.0.0.1:6379> set c 1
OK
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> incr a
QUEUED
redis 127.0.0.1:6379> incr list1
QUEUED
redis 127.0.0.1:6379> incr c
QUEUED
redis 127.0.0.1:6379> exec
1) (integer) 2
2) (error) ERR Operation against a key holding the wrong kind of value
3) (integer) 2
— 2.当事务的执行过程中,如果redis服务宕机了,只有部分命令执行了,后面的也就被丢弃了;
————————- Redis中事务的bugs ————————-

Redis学习03–排序操作

Redis的排序操作

  1. redis支持对list, set和sorted set元素的排序;
  2. 排序命令SORT的语法:SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern …]] [ASC|DESC] [ALPHA] [STORE destination];
  3. sort key [ASC|DESC] [ALPHA]:
    1. sort key:不加任何选项时就是简单的对列表自身元素排序,并返回排序结果,列表中的值只能是数字;
    2. ASC:sort默认的排序方式,即升序排列;
    3. DESC:是逆序排列;
    4. ALPHA:如果列表中是字符串或者数字的话,需要使用alpha选项按照字母顺序排列;ASC|DESC可以和ALPHA连用;
  4. [LIMIT offset count]:用于分页显示,表示从offset的位置(从0开始),显示count个记录;
  5. [BY nosort]:不排序,直接返回list, set或者sorted set中元素的物理顺序;
  6. [BY pattern]:使用外部的keys作为权重来对list, set, sorted set中的元素排序,但是权重的后缀必须与几何中的元素一一对应;
  7. [GET pattern …]:以通过get选项去获取指定pattern作为新key对应的值;
  8. [STORE destination]:如果对集合经常按照固定的模式去排序,那么把排序结果缓存起来会减少cpu的开销,使用store选项可以将排序内容保存到指定key中,保存的类型是list;
  9. 关于排序的两个问题及解决方案:
    1. 如果我们有多个redis server的话,不同的key可能存在于不同的server上,比如weight_3,weight_9, weight_12, weight_22很有可能分别在四个不同的server上存放着,这种情况会对排序性能造成很大的影响;
      1. 可以通过key tag将需要排序的key都放到同一个server上,由于具体决定哪个key存在哪个服务器上一般都是在client端hash的办法来做的,我们可以通过只对key的部分进行hash;
      2. 举个例子假如我们的client如果发现key中包含[],那么只对key中[]包含的内容进行hash,我们将weight_四个相关的key,[weight_]3,[weight_]9, [weight_]12, [weight_]22都这样命名,于是client程序就会把他们都放到同一server上;
    2. 如果要sort的集合非常大的话排序就会消耗很长时间,由于redis单线程的,所以长时间的排序操作会阻塞其他client的请求;
      1. 通过主从复制机制将数据复制到多个slave上,然后我们只在slave上做排序操作,并进可能的对排序结果缓存;
      2. 或者是采用sorted set对需要按某个顺序访问的集合建立索引;
  10. 实际使用的场景及例子;
——————————— sort key [ASC|DESC] [ALPHA] ———————————
— 1.sort key;
redis 127.0.0.1:6379> lpush l1 12
(integer) 1
redis 127.0.0.1:6379> lpush l1 3
(integer) 2
redis 127.0.0.1:6379> lpush l1 22
(integer) 3
redis 127.0.0.1:6379> lpush l1 9
(integer) 4
redis 127.0.0.1:6379> sort l1
1) “3”
2) “9”
3) “12”
4) “22”
redis 127.0.0.1:6379> sort l1 desc
1) “22”
2) “12”
3) “9”
4) “3”
— 2.alpha;
1.对数字按照字母表顺序排序;
2.字符串按照字母表顺序排序;
redis 127.0.0.1:6379> lpush l2 snda
(integer) 1
redis 127.0.0.1:6379> lpush l2 tencent
(integer) 2
redis 127.0.0.1:6379> lpush l2 baidu
(integer) 3
redis 127.0.0.1:6379> lpush l2 taobao
(integer) 4
redis 127.0.0.1:6379> sort l2 alpha
1) “baidu”
2) “snda”
3) “taobao”
4) “tencent”
redis 127.0.0.1:6379> sort l2 desc alpha
1) “tencent”
2) “taobao”
3) “snda”
4) “baidu”
redis 127.0.0.1:6379> sort l2
(error) ERR One or more scores can’t be converted into double
——————————— sort key [ASC|DESC] [ALPHA] ———————————
——————————— [LIMIT offset count] ———————————
获得l1集合中第二个字符开始的后两个值;
redis 127.0.0.1:6379> sort l1
1) “3”
2) “9”
3) “12”
4) “22”
redis 127.0.0.1:6379> sort l1 limit 1 2
1) “9”
2) “12”
redis 127.0.0.1:6379> sort l1 limit 1 2 desc
1) “12”
2) “9”
——————————— [LIMIT offset count] ———————————
——————————— [BY nosort] ———————————
li列表插入的时候是按照12, 3, 22, 9的顺序插入的,但是实际的物理顺序是反序的,即后插入的元素在前面;
redis 127.0.0.1:6379> sort l1 by nosort
1) “9”
2) “22”
3) “3”
4) “12”
——————————— [BY nosort] ———————————
——————————— [BY pattern] ———————————
redis 127.0.0.1:6379> sort l1 by nosort
1) “9”
2) “22”
3) “3”
4) “12”
redis 127.0.0.1:6379> set weight_3 1
OK
redis 127.0.0.1:6379> set weight_12 2
OK
redis 127.0.0.1:6379> set weight_22 3
OK
redis 127.0.0.1:6379> set weight_9 4
OK
redis 127.0.0.1:6379> sort l1 by weight_*
1) “3”
2) “12”
3) “22”
4) “9”
redis 127.0.0.1:6379> sort l1 by weight_* desc
1) “9”
2) “22”
3) “12”
4) “3”
redis 127.0.0.1:6379> set weight_22 5
OK
redis 127.0.0.1:6379> sort l1 by weight_*
1) “3”
2) “12”
3) “9”
4) “22”
——————————— [BY pattern] ———————————
——————————— [GET pattern …] ———————————
— 1.返回pattern的值,按照pattern的顺序;
redis 127.0.0.1:6379> sort l1 by weight_*  get weight_*
1) “1”
2) “2”
3) “4”
4) “5”
— 2.通过#(#特殊符号引用的是原始集合)返回原来列表中的值;
redis 127.0.0.1:6379> sort l1 by weight_* get weight_* get #
1) “1”
2) “3”
3) “2”
4) “12”
5) “4”
6) “9”
7) “5”
8) “22”
— 3.引用hash类型字段的话需要使用->符号,当值不存在就返回nil;
redis 127.0.0.1:6379> hset h_3 id 1
(integer) 1
redis 127.0.0.1:6379> hset h_22 id 2
(integer) 1
redis 127.0.0.1:6379> hset h_9 id 3
(integer) 1
redis 127.0.0.1:6379> hset h_10 id 4
(integer) 1
redis 127.0.0.1:6379> sort l1 get h_*->id
1) “1”
2) “3”
3) (nil)
4) “2”
——————————— [GET pattern …] ———————————
——————————— [STORE destination] ———————————
把排序的结果保存在一个list中;
redis 127.0.0.1:6379> sort l1
1) “3”
2) “9”
3) “12”
4) “22”
redis 127.0.0.1:6379> sort l1 limit 1 2 store sl1
(integer) 2
redis 127.0.0.1:6379> type sl1
list
redis 127.0.0.1:6379> lrange sl1 0 -1
1) “9”
2) “12”
——————————— [STORE destination] ———————————
——————————— 实际使用的场景及例子 ———————————
— 1.kobe的队友列表,存放队友的号码uid;
redis 127.0.0.1:6379> sadd kobe:team:list 10
(integer) 1
redis 127.0.0.1:6379> sadd kobe:team:list 12
(integer) 1
redis 127.0.0.1:6379> sadd kobe:team:list 15
(integer) 1
redis 127.0.0.1:6379> sadd kobe:team:list 16
(integer) 1
— 2.球员的排序规则,存放球员的得分;
redis 127.0.0.1:6379> set uid:sort:10 1000
OK
redis 127.0.0.1:6379> set uid:sort:12 2000
OK
redis 127.0.0.1:6379> set uid:sort:15 300
OK
redis 127.0.0.1:6379> set uid:sort:16 2500
OK
— 3.队友的信息;
redis 127.0.0.1:6379> set uid:10 “{‘uid’:10,’name’:’Steve Nash’}”
OK
redis 127.0.0.1:6379> set uid:12 “{‘uid’:12,’name’:’Dwight Howard’}”
OK
redis 127.0.0.1:6379> set uid:15 “{‘uid’:15,’name’:’Ron Artest’}”
OK
redis 127.0.0.1:6379> set uid:16 “{‘uid’:16,’name’:’Pau Gasol’}”
OK
— 4.按照得分对球员排序;
redis 127.0.0.1:6379> sort kobe:team:list by uid:sort:* get uid:*
1) “{‘uid’:15,’name’:’Ron Artest’}”
2) “{‘uid’:10,’name’:’Steve Nash’}”
3) “{‘uid’:12,’name’:’Dwight Howard’}”
4) “{‘uid’:16,’name’:’Pau Gasol’}”
redis 127.0.0.1:6379> sort kobe:team:list by uid:sort:* get uid:* get uid:sort:*
1) “{‘uid’:15,’name’:’Ron Artest’}”
2) “300”
3) “{‘uid’:10,’name’:’Steve Nash’}”
4) “1000”
5) “{‘uid’:12,’name’:’Dwight Howard’}”
6) “2000”
7) “{‘uid’:16,’name’:’Pau Gasol’}”
8) “2500”
——————————— 实际使用的场景及例子 ———————————

Redis学习02–数据类型介绍

Redis中的数据类型

  1. Redis中支持的数据类型:
    1. string;
    2. list;
    3. set;
    4. sorted set;
    5. hash;
  2. Redis中的Key:
    1. redis本质上一个key-value数据库,它的key是字符串类型,但是key中不能包括边界字符,由于key不是binary safe的字符串,所以像”my key”和”mykey\n”这样包含空格和换行的key是不允许的(之后的版本是可以包含任字符的);
    2. 在redis内部并不限制使用binary字符,这是redis协议限制的,”\r\n”在协议格式中会作为特殊字符;redis 1.2以后的协议中部分命令已经开始使用新的协议格式了(比如MSET),推荐把包含边界字符当成非法的key,免得被bug纠缠;
    3. key的一个格式约定,object-type:id:field;比如user:1000:password,blog:xxidxx:title;key的长度最好不要太长,首先占内存啊,而且查找时候相对短key也更慢,不过也不推荐过短的key,可读性不好;
    4. 与key相关的命令:
      1. exists key:测试指定key是否存在,返回1表示存在,0不存在;
      2. del key1 key2 ….keyN:删除给定key,返回删除key的数目,0表示给定key都不存在;
      3. type key:返回给定key的value类型;返回none表示不存在key,string字符类型,list链表类型,set无序集合类型;
      4. keys pattern:返回匹配指定模式的所有key;
        1. keys *:获得所有的keys;
        2. key t*:获得t开头的keys;
        3. key t[ab]x:获得以t开头x结尾中间是a或者b的keys;
        4. key t?x:获得以t开头x结尾中间之后一个字符的keys;
      5. randomkey:返回从当前数据库中随机选择的一个key,如果当前数据库是空的,返回空串;
      6. rename oldkey newkey:原子的重命名一个key,如果newkey存在,将会被覆盖,返回1表示成功,0失败;可能是oldkey不存在或者和newkey相同;
      7. renamenx oldkey newkey:同上,但是如果newkey存在返回失败;
      8. dbsize:返回当前数据库的key数量;
      9. expire key seconds:为key指定过期时间,单位是秒;返回1成功,0表示key已经设置过过期时间或者不存在;
      10. ttl key:返回设置过过期时间的key的剩余过期秒数,-1表示key不存在或者没有设置过过期时间;
      11. select db-index:通过索引选择数据库,默认连接的数据库所有是0,默认数据库数是16个;返回1表示成功,0失败;
      12. move key db-index:将key从当前数据库移动到指定数据库,返回1成功,0如果key不存在,或者已经在指定数据库中;
      13. flushdb:删除当前数据库中所有key,此方法不会失败,慎用;
      14. flushall:删除所有数据库中的所有key,此方法不会失败,更加慎用;
  3. string类型:
    1. string是redis最基本的类型,而且string类型是二进制安全的,即redis的string可以包含任何数据;比如jpg图片或者序列化的对象;
    2. 从内部实现来看其实string可以看作byte数组,最大上限是1G字节,string类型的定义为:struct sdshdr {long len; long free; char buf[]; };
      1. buf是个char数组用于存贮实际的字符串内容,其实char和高级语言中的byte是等价的,都是一个字节;
      2. len是buf数组的长度;
      3. free是数组中剩余可用字节数;
    3. string类型可以被部分命令按int处理,比如incr等命令;
    4. redis的其它类型像list, set, sorted set, hash它们包含的元素与都只能是string类型;
    5. 如果只用string类型,redis就可以被看作加上持久化特性的memcached,当然redis对string类型的操作比memcached多很多;
    6. 与string相关的操作:
      1. set key value:设置key对应的值为string类型的value,返回1表示成功,0失败;
      2. setnx key value:同上,如果key已经存在,返回0;nx是not exist的意思;
      3. get key:获取key对应的string值,如果key不存在返回nil;
      4. getset key value:原子的设置key的值,并返回key的旧值;如果key不存在返回nil;
      5. mget key1 key2 … keyN:一次获取多个key的值,如果对应key不存在,则对应返回nil;
      6. mset key1 value1 … keyN valueN:一次设置多个key的值,成功返回1表示所有的值都设置了,失败返回0表示没有任何值被设置;
      7. msetnx key1 value1 … keyN valueN:同上,但是不会覆盖已经存在的key;
      8. incr key:对key的值做加加操作,并返回新的值;注意incr一个不是int的value会返回错误,incr一个不存在的key,则设置key为1;
      9. decr key:同上,但是做的是减减操作,decr一个不存在key,则设置key为-1;
      10. incrby key integer:同incr,加指定值,key不存在时候会设置key,并认为原来的value是0;
      11. decrby key integer:同decr,减指定值;decrby完全是为了可读性,我们完全可以通过incrby一个负值来实现同样效果,反之一样;
      12. substr key start end:返回截取过的key的字符串值,注意并不修改key的值,下标是从0开始的(redis在2.0版本以后不包括2.0,使用的方法是getrange参数相同);
      13. append key value:给指定key的字符串值追加value,返回新字符串值的长度;
  4. list类型:
    1. redis的list类型其实就是一个每个子元素都是string类型的双向链表,所以[lr]push和[lr]pop命令的算法时间复杂度都是O(1),另外list会记录链表的长度,所以llen操作也是O(1);
    2. list插入的元素,默认是按照时间的逆序排列的,最新的数据都是插入到头部,可以通过lrange list 0 -1/sort list by nosort查看;
    3. 链表的最大长度是(2的32次方-1),我们可以通过push,pop操作从链表的头部或者尾部添加删除元素;这使得list既可以用作栈,也可以用作队列;
    4. list的pop操作还有阻塞版本的,当我们[lr]pop一个list对象时,如果list是空,或者不存在,会立即返回nil,但是阻塞版本的b[lr]pop则可以阻塞,当然可以加超时时间,超时后也会返回nil;
    5. 为什么要阻塞版本的pop呢:主要是为了避免轮询;举个简单的例子如果我们用list来实现一个工作队列,执行任务的thread可以调用阻塞版本的pop去获取任务这样就可以避免轮询去检查是否有任务存在,当任务来时候工作线程可以立即返回,也可以避免轮询带来的延迟;
    6. 与list相关的操作:
      1. lpush key string:在key对应list的头部添加字符串元素,返回1表示成功,0表示key存在且不是list类型;
      2. rpush key string:同上,在尾部添加;
      3. llen key:返回key对应list的长度,key不存在返回0,如果key对应类型不是list返回错误;
      4. lrange key start end:返回指定区间内的元素,下标从0开始,负值表示从后面计算,-1表示倒数第一个元素,key不存在返回空列表;
      5. ltrim key start end:截取list,保留指定区间内元素,成功返回1,key不存在返回错误;
      6. lset key index value:设置list中指定下标的元素值,成功返回1,key或者下标不存在返回错误;
      7. lrem key count value:从key对应list中删除count个和value相同的元素,count为0时候删除全部;
      8. lpop key:从list的头部删除元素,并返回删除元素,如果key对应list不存在或者是空返回nil,如果key对应值不是list返回错误;
      9. rpop:同上,但是从尾部删除;
      10. blpop key1…keyN timeout:从左到右扫描返回对第一个非空list进行lpop操作并返回;
        1. 比如blpop list1 list2 list3 0,如果list1不存在list2,list3都是非空则对list2做lpop并返回从list2中删除的元素;如果所有的list都是空或不存在,则会阻塞timeout秒,timeout为0表示一直阻塞;
        2. 当阻塞时,如果有client对key1…keyN中的任意key进行push操作,则第一在这个key上被阻塞的client会立即返回;如果超时发生,则返回nil;有点像unix的select或者poll
      11. brpop:同blpop,一个是从头部删除一个是从尾部删除;
      12. rpoplpush srckey destkey:从srckey对应list的尾部移除元素并添加到destkey对应list的头部,最后返回被移除的元素值,整个操作是原子的.如果srckey是空或者不存在返回nil;
  5. set类型:
    1. redis的set是string类型的无序集合,set元素最大可以包含(2的32次方-1)个元素;
    2. set的是通过hash table实现的,所以添加,删除,查找的复杂度都是O(1);hash table会随着添加或者删除自动的调整大小,需要注意的是调整hash table大小时候需要同步(获取写锁)会阻塞其他读写操作,可能不久后就会改用跳表(skip list)来实现;
    3. 跳表已经在sorted set中使用了,关于set集合类型除了基本的添加删除操作,其它有用的操作还包含集合的取并集(union),交集(intersection),差集(difference);通过这些操作可以很容易的实现sns中的好友推荐和blog的tag功能;
    4. 与set相关的操作:
      1. sadd key member:添加一个string元素到key对应的set集合中,成功返回1,如果元素以及在集合中返回0,key对应的set不存在返回错误;
      2. srem key member:从key对应set中移除给定元素,成功返回1,如果member在集合中不存在或者key不存在返回0,如果key对应的不是set类型的值返回错误;
      3. spop key:删除并返回key对应set中随机的一个元素,如果set是空或者key不存在返回nil;
      4. srandmember key:同spop,随机取set中的一个元素,但是不删除元素;
      5. smove srckey dstkey member:从srckey对应set中移除member并添加到dstkey对应set中,整个操作是原子的;成功返回1,如果member在srckey中不存在返回0,如果key不是set类型返回错误;
      6. scard key:返回set的元素个数,如果set是空或者key不存在返回0;
      7. sismember key member:判断member是否在set中,存在返回1,0表示不存在或者key不存在;
      8. sinter key1 key2…keyN:返回所有给定key的交集;
      9. sinterstore dstkey key1…keyN:同sinter,但是会同时将交集存到dstkey下;
      10. sunion key1 key2…keyN:返回所有给定key的并集;
      11. sunionstore dstkey key1…keyN:同sunion,并同时保存并集到dstkey下;
      12. sdiff key1 key2…keyN:返回所有给定key的差集;
      13. sdiffstore dstkey key1…keyN:同sdiff,并同时保存差集到dstkey下;
      14. smembers key:返回key对应set的所有元素,结果是无序的;
  6. sorted set类型:
    1. 和set一样sorted set也是string类型元素的集合,不同的是每个元素都会关联一个double类型的score;
    2. sorted set的实现是skip list和hash table的混合体当元素被添加到集合中时,一个元素到score的映射被添加到hash table中,所以给定一个元素获取score的开销是O(1),另一个score到元素的映射被添加到skip list并按照score排序,所以就可以有序的获取集合中的元素;添加,删除操作开销都是O(log(N))和skip list的开销一致,redis的skip list实现用的是双向链表,这样就可以逆序从尾部取元素;
    3. sorted set最经常的使用方式应该是作为索引来使用,我们可以把要排序的字段作为score存储,对象的id当元素存储;
    4. 与sorted set相关的操作:
      1. zadd key score member:添加元素到集合,元素在集合中存在则更新对应score;
      2. zrem key member:删除指定元素,1表示成功,如果元素不存在返回0;
      3. zincrby key incr member:增加对应member的score值,然后移动元素并保持skip list保持有序,返回更新后的score值;
      4. zrank key member:返回指定元素在集合中的排名(下标),集合中元素是按score从小到大排序的;
      5. zrevrank key member:同上,但是集合中元素是按score从大到小排序;
      6. zrange key start end:类似lrange操作从集合中去指定区间的元素;返回的是有序结果;
      7. zrevrange key start end:同上,返回结果是按score逆序的;
      8. zrangebyscore key min max:返回集合中score在给定区间的元素;
      9. zcount key min max:返回集合中score在给定区间的数量;
      10. zcard key:返回集合中元素个数;
      11. zscore key element:返回给定元素对应的score;
      12. zremrangebyrank key min max:删除集合中排名在给定区间的元素;
      13. zremrangebyscore key min max:删除集合中score在给定区间的元素;
  7. hash类型:
    1. redis的hash是一个string类型的field和value的映射表,它的添加,删除操作平均都是O(1),hash特别适合用于存储对象;
    2. 相较于将对象的每个字段存成单个string类型,将一个对象存储在hash类型中会占用更少的内存,并且可以更方便的存取整个对象,省内存的原因是新建一个hash对象时开始是用zipmap(又称为small hash)来存储的;
    3. zipmap其实并不是hash table,但是zipmap相比正常的hash实现可以节省不少hash本身需要的一些元数据存储开销,尽管zipmap的添加,删除,查找都是O(n),但是由于一般对象的field数量都不太多,所以使用zipmap也是很快的,也就是说添加删除平均还是O(1);
    4. 如果field或者value的大小超出一定限制后,redis会在内部自动将zipmap替换成正常的hash实现,这个限制可以在配置文件中指定;
      1. hash-max-zipmap-entries 64 #配置字段最多64个
      2. hash-max-zipmap-value 512 #配置value最大为512字节
    5. 与hash相关的操作:
      1. hset key field value:设置hash field为指定值,如果key不存在,则先创建;
      2. hget key field:获取指定的hash field;
      3. hmget key filed1….fieldN:获取全部指定的hash filed;
      4. hmset key filed1 value1 … filedN valueN:同时设置hash的多个field;
      5. hincrby key field integer:将指定的hash filed 加上给定值;
      6. hexists key field:测试指定field是否存在;
      7. hdel key field:删除指定的hash field;
      8. hlen key:返回指定hash的field数量;
      9. hkeys key:返回hash的所有field;
      10. hvals key:返回hash的所有value;
      11. hgetall:返回hash的所有filed和value;

Redis学习01–在Linux下安装Redis v2.6.13

安装Redis
  1. Redis的介绍:
    1. redis是一个开源的key-value数据库,它又经常被认为是一个数据结构服务器,因为它的value不仅包括基本的string类型还有list,set,sorted set和hash类型,当然这些类型的元素也都是string类型,也就是说list,set这些集合类型也只能包含string类型;
    2. 可以在这些类型上做很多原子性的操作,比如对一个字符value追加字符串(APPEND命令),加加或者减减一个数字字符串(INCR命令,当然是按整数处理的),可以对list类型进行push,或者pop元素操作(可以模拟栈和队列),对于set类型可以进行一些集合相关操作(intersection,union,difference);
    3. memcache也有类似与++,–的命令,不过memcache的value只包括string类型,远没有redis的value类型丰富;
    4. 和memcahe一样为了性能,redis的数据通常都是放到内存中的,当然redis可以每间隔一定时间将内存中数据写入到磁盘以防止数据丢失;
    5. redis也支持主从复制机制(master-slave replication),redis的其它特性包括简单的事务支持和发布订阅(pub/sub)通道功能,而且redis配置管理非常简单,还有各种语言版本的开源客户端类库;
  2. key-list类型内存数据引擎:
    1. 互联网数据目前基本使用两种方式来存储,关系数据库或者key value,但是这些互联网业务本身并不属于这两种数据类型,比如用户在社会化平台中的关系,它是一个list,如果要用关系数据库存储就需要转换成一种多行记录的形式,这种形式存在很多冗余数据,每一行需要存储一些重复信息;如果用key value存储则修改和删除比较麻烦,需要将全部数据读出再写入; — by timyang@weibo.com
    2. 如果用key-value中的value存储list,只能实现最简单的列表功能(按照id或时间先后排序,例如使用memcache的append或prepend协议),其他list操作只能靠客户端操作,性能很差,如果数据量较大,操作时间是无法接受的,并发也会遇到巨大挑战);
    3. key-list系统key对应的”value”是一个list(eg.set list),可以对list中的单个item进行操作,理想的key-list需要如下特点:
      1. list可以是海量的,且操作性能高效;
      2. list是可以是有序的,且可动态调整顺序;
    4. 使用场景:
      1. 论坛中的主题列表,回复列表;
      2. 微博中的用户关注列表,用户feed列表,用户关注feed列表;
      3. 最近访问列表;
      4. 集合操作:求交集,并集,差集(sdiff/sinter/sunion);
      5. 好友推荐;
      6. 排行榜;
  3. 下载:
    1. 官方地址: http://redis.io/;
    2. 下载地址: http://redis.io/download;
    3. 文档地址: http://redis.io/documentation;
    4. 作者blog: http://oldblog.antirez.com/;
  4. 安装:
    1. 解压缩到/usr/local目录下:tar -zxvf /tools/redis-2.6.13.tar.gz -C /usr/local/;                            
    2. 编译:cd /usr/local/redis-2.6.13/;make;                 
    3. 安装时报错:undefined reference to `__sync_add_and_fetch_4′;  
    4. 是因为CPU信息判断失误引起的,所以需要确定CPU的信息,然后把CPU的信息设置到环境变量中,清除旧的编译文件,最后重新编译即可:uname -a(uname -m);export CFLAGS=-march=i686;cd /usr/local/redis-2.6.13/;make distclean;make;              
    5. 如果需要运行make-test的话需要安装8.5版本以上的tcl;
    6. 此时就会在src目录下出现redis-server的服务端和redis-cli客户端,为了方便使用可以分别创建软连接:ln -s /usr/local/redis-2.6.13/src/redis-server /usr/local/redis-2.6.13/src/redisd;ln -s /usr/local/redis-2.6.13/src/redis-cli /usr/local/redis-2.6.13/src/redis;                                 
    7. 添加PATH变量:PATH=$PATH:/usr/local/redis-2.6.13/src;                                  
    8. 启动服务端:
      1. 默认配置启动:redisd;
      2. 使用自己的配置文件启动:redisd /etc/redis.conf;
      3. 在后台运行:redisd /etc/redis.conf &;                                     
    9. 启动客户端:redis;                                        
  5. 驱动下载:
    1. 官网提供的支持语言的下载地址: http://redis.io/clients;
    2. Jedis的github地址:https://github.com/xetorthio/jedis;
    3. jar包下载地址:https://github.com/xetorthio/jedis/downloads;
    4. 提供的方法:
      1. Sorting
      2. Connection handling
      3. Commands operating on any kind of values
      4. Commands operating on string values
      5. Commands operating on hashes
      6. Commands operating on lists
      7. Commands operating on sets
      8. Commands operating on sorted sets
      9. Transactions
      10. Pipelining
      11. Publish/Subscribe
      12. Persistence control commands
      13. Remote server control commands
      14. Connection pooling
      15. Sharding (MD5, MurmureHash)
      16. Key-tags for sharding
      17. Sharding with pipelining
  6. 配置文件;
# Redis configuration file example
 
# Note on units: when memory size is needed, it is possible to specify
# it in the usual form of 1k 5GB 4M and so forth:
#
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#
# units are case insensitive so 1GB 1Gb 1gB are all the same.
 
# By default Redis does not run as a daemon. Use ‘yes’ if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
daemonize yes
 
# When running daemonized, Redis writes a pid file in /var/run/redis.pid by
# default. You can specify a custom pid file location here.
pidfile /var/run/redis.pid
 
# Accept connections on the specified port, default is 6379.
# If port 0 is specified Redis will not listen on a TCP socket.
port 6379
 
# If you want you can bind a single interface, if the bind option is not
# specified all the interfaces will listen for incoming connections.
#
# bind 127.0.0.1
 
# Specify the path for the unix socket that will be used to listen for
# incoming connections. There is no default, so Redis will not listen
# on a unix socket when not specified.
#
# unixsocket /tmp/redis.sock
# unixsocketperm 755
 
# Close the connection after a client is idle for N seconds (0 to disable)
timeout 0
 
# TCP keepalive.
#
# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence
# of communication. This is useful for two reasons:
#
# 1) Detect dead peers.
# 2) Take the connection alive from the point of view of network
#    equipment in the middle.
#
# On Linux, the specified value (in seconds) is the period used to send ACKs.
# Note that to close the connection the double of the time is needed.
# On other kernels the period depends on the kernel configuration.
#
# A reasonable value for this option is 60 seconds.
tcp-keepalive 0
 
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice
 
# Specify the log file name. Also ‘stdout’ can be used to force
# Redis to log on the standard output. Note that if you use standard
# output for logging but daemonize, logs will be sent to /dev/null
logfile stdout
 
# To enable logging to the system logger, just set ‘syslog-enabled’ to yes,
# and optionally update the other syslog parameters to suit your needs.
# syslog-enabled no
 
# Specify the syslog identity.
# syslog-ident redis
 
# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7.
# syslog-facility local0
 
# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and ‘databases’-1
databases 16
 
################################ SNAPSHOTTING  #################################
#
# Save the DB on disk:
#
#   save <seconds> <changes>
#
#   Will save the DB if both the given number of seconds and the given
#   number of write operations against the DB occurred.
#
#   In the example below the behaviour will be to save:
#   after 900 sec (15 min) if at least 1 key changed
#   after 300 sec (5 min) if at least 10 keys changed
#   after 60 sec if at least 10000 keys changed
#
#   Note: you can disable saving at all commenting all the “save” lines.
#
#   It is also possible to remove all the previously configured save
#   points by adding a save directive with a single empty string argument
#   like in the following example:
#
#   save “”
 
#save 900 1
#save 300 10
#save 60 10000
 
# By default Redis will stop accepting writes if RDB snapshots are enabled
# (at least one save point) and the latest background save failed.
# This will make the user aware (in an hard way) that data is not persisting
# on disk properly, otherwise chances are that no one will notice and some
# distater will happen.
#
# If the background saving process will start working again Redis will
# automatically allow writes again.
#
# However if you have setup your proper monitoring of the Redis server
# and persistence, you may want to disable this feature so that Redis will
# continue to work as usually even if there are problems with disk,
# permissions, and so forth.
stop-writes-on-bgsave-error yes
 
# Compress string objects using LZF when dump .rdb databases?
# For default that’s set to ‘yes’ as it’s almost always a win.
# If you want to save some CPU in the saving child set it to ‘no’ but
# the dataset will likely be bigger if you have compressible values or keys.
rdbcompression yes
 
# Since version 5 of RDB a CRC64 checksum is placed at the end of the file.
# This makes the format more resistant to corruption but there is a performance
# hit to pay (around 10%) when saving and loading RDB files, so you can disable it
# for maximum performances.
#
# RDB files created with checksum disabled have a checksum of zero that will
# tell the loading code to skip the check.
rdbchecksum yes
 
# The filename where to dump the DB
dbfilename dump.rdb
 
# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the ‘dbfilename’ configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir ./
 
################################# REPLICATION #################################
 
# Master-Slave replication. Use slaveof to make a Redis instance a copy of
# another Redis server. Note that the configuration is local to the slave
# so for example it is possible to configure the slave to save the DB with a
# different interval, or to listen to another port, and so on.
#
# slaveof <masterip> <masterport>
 
# If the master is password protected (using the “requirepass” configuration
# directive below) it is possible to tell the slave to authenticate before
# starting the replication synchronization process, otherwise the master will
# refuse the slave request.
#
# masterauth <master-password>
 
# When a slave loses its connection with the master, or when the replication
# is still in progress, the slave can act in two different ways:
#
# 1) if slave-serve-stale-data is set to ‘yes’ (the default) the slave will
#    still reply to client requests, possibly with out of date data, or the
#    data set may just be empty if this is the first synchronization.
#
# 2) if slave-serve-stale-data is set to ‘no’ the slave will reply with
#    an error “SYNC with master in progress” to all the kind of commands
#    but to INFO and SLAVEOF.
#
slave-serve-stale-data yes
 
# You can configure a slave instance to accept writes or not. Writing against
# a slave instance may be useful to store some ephemeral data (because data
# written on a slave will be easily deleted after resync with the master) but
# may also cause problems if clients are writing to it because of a
# misconfiguration.
#
# Since Redis 2.6 by default slaves are read-only.
#
# Note: read only slaves are not designed to be exposed to untrusted clients
# on the internet. It’s just a protection layer against misuse of the instance.
# Still a read only slave exports by default all the administrative commands
# such as CONFIG, DEBUG, and so forth. To a limited extend you can improve
# security of read only slaves using ‘rename-command’ to shadow all the
# administrative / dangerous commands.
slave-read-only yes
 
# Slaves send PINGs to server in a predefined interval. It’s possible to change
# this interval with the repl_ping_slave_period option. The default value is 10
# seconds.
#
# repl-ping-slave-period 10
 
# The following option sets a timeout for both Bulk transfer I/O timeout and
# master data or ping response timeout. The default value is 60 seconds.
#
# It is important to make sure that this value is greater than the value
# specified for repl-ping-slave-period otherwise a timeout will be detected
# every time there is low traffic between the master and the slave.
#
# repl-timeout 60
 
# Disable TCP_NODELAY on the slave socket after SYNC?
#
# If you select “yes” Redis will use a smaller number of TCP packets and
# less bandwidth to send data to slaves. But this can add a delay for
# the data to appear on the slave side, up to 40 milliseconds with
# Linux kernels using a default configuration.
#
# If you select “no” the delay for data to appear on the slave side will
# be reduced but more bandwidth will be used for replication.
#
# By default we optimize for low latency, but in very high traffic conditions
# or when the master and slaves are many hops away, turning this to “yes” may
# be a good idea.
repl-disable-tcp-nodelay no
 
# The slave priority is an integer number published by Redis in the INFO output.
# It is used by Redis Sentinel in order to select a slave to promote into a
# master if the master is no longer working correctly.
#
# A slave with a low priority number is considered better for promotion, so
# for instance if there are three slaves with priority 10, 100, 25 Sentinel will
# pick the one wtih priority 10, that is the lowest.
#
# However a special priority of 0 marks the slave as not able to perform the
# role of master, so a slave with priority of 0 will never be selected by
# Redis Sentinel for promotion.
#
# By default the priority is 100.
slave-priority 100
 
################################## SECURITY ###################################
 
# Require clients to issue AUTH <PASSWORD> before processing any other
# commands.  This might be useful in environments in which you do not trust
# others with access to the host running redis-server.
#
# This should stay commented out for backward compatibility and because most
# people do not need auth (e.g. they run their own servers).
#
# Warning: since Redis is pretty fast an outside user can try up to
# 150k passwords per second against a good box. This means that you should
# use a very strong password otherwise it will be very easy to break.
#
# requirepass foobared
 
# Command renaming.
#
# It is possible to change the name of dangerous commands in a shared
# environment. For instance the CONFIG command may be renamed into something
# hard to guess so that it will still be available for internal-use tools
# but not available for general clients.
#
# Example:
#
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
#
# It is also possible to completely kill a command by renaming it into
# an empty string:
#
# rename-command CONFIG “”
#
# Please note that changing the name of commands that are logged into the
# AOF file or transmitted to slaves may cause problems.
 
################################### LIMITS ####################################
 
# Set the max number of connected clients at the same time. By default
# this limit is set to 10000 clients, however if the Redis server is not
# able to configure the process file limit to allow for the specified limit
# the max number of allowed clients is set to the current file limit
# minus 32 (as Redis reserves a few file descriptors for internal uses).
#
# Once the limit is reached Redis will close all the new connections sending
# an error ‘max number of clients reached’.
#
# maxclients 10000
 
# Don’t use more memory than the specified amount of bytes.
# When the memory limit is reached Redis will try to remove keys
# accordingly to the eviction policy selected (see maxmemmory-policy).
#
# If Redis can’t remove keys according to the policy, or if the policy is
# set to ‘noeviction’, Redis will start to reply with errors to commands
# that would use more memory, like SET, LPUSH, and so on, and will continue
# to reply to read-only commands like GET.
#
# This option is usually useful when using Redis as an LRU cache, or to set
# an hard memory limit for an instance (using the ‘noeviction’ policy).
#
# WARNING: If you have slaves attached to an instance with maxmemory on,
# the size of the output buffers needed to feed the slaves are subtracted
# from the used memory count, so that network problems / resyncs will
# not trigger a loop where keys are evicted, and in turn the output
# buffer of slaves is full with DELs of keys evicted triggering the deletion
# of more keys, and so forth until the database is completely emptied.
#
# In short… if you have slaves attached it is suggested that you set a lower
# limit for maxmemory so that there is some free RAM on the system for slave
# output buffers (but this is not needed if the policy is ‘noeviction’).
#
# maxmemory <bytes>
 
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#
# volatile-lru -> remove the key with an expire set using an LRU algorithm
# allkeys-lru -> remove any key accordingly to the LRU algorithm
# volatile-random -> remove a random key with an expire set
# allkeys-random -> remove a random key, any key
# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# noeviction -> don’t expire at all, just return an error on write operations
#
# Note: with any of the above policies, Redis will return an error on write
#       operations, when there are not suitable keys for eviction.
#
#       At the date of writing this commands are: set setnx setex append
#       incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
#       sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
#       zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
#       getset mset msetnx exec sort
#
# The default is:
#
# maxmemory-policy volatile-lru
 
# LRU and minimal TTL algorithms are not precise algorithms but approximated
# algorithms (in order to save memory), so you can select as well the sample
# size to check. For instance for default Redis will check three keys and
# pick the one that was used less recently, you can change the sample size
# using the following configuration directive.
#
# maxmemory-samples 3
 
############################## APPEND ONLY MODE ###############################
 
# By default Redis asynchronously dumps the dataset on disk. This mode is
# good enough in many applications, but an issue with the Redis process or
# a power outage may result into a few minutes of writes lost (depending on
# the configured save points).
#
# The Append Only File is an alternative persistence mode that provides
# much better durability. For instance using the default data fsync policy
# (see later in the config file) Redis can lose just one second of writes in a
# dramatic event like a server power outage, or a single write if something
# wrong with the Redis process itself happens, but the operating system is
# still running correctly.
#
# AOF and RDB persistence can be enabled at the same time without problems.
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.
#
# Please check http://redis.io/topics/persistence for more information.
 
appendonly yes
 
# The name of the append only file (default: “appendonly.aof”)
# appendfilename appendonly.aof
 
# The fsync() call tells the Operating System to actually write data on disk
# instead to wait for more data in the output buffer. Some OS will really flush
# data on disk, some other OS will just try to do it ASAP.
#
# Redis supports three different modes:
#
# no: don’t fsync, just let the OS flush the data when it wants. Faster.
# always: fsync after every write to the append only log . Slow, Safest.
# everysec: fsync only one time every second. Compromise.
#
# The default is “everysec”, as that’s usually the right compromise between
# speed and data safety. It’s up to you to understand if you can relax this to
# “no” that will let the operating system flush the output buffer when
# it wants, for better performances (but if you can live with the idea of
# some data loss consider the default persistence mode that’s snapshotting),
# or on the contrary, use “always” that’s very slow but a bit safer than
# everysec.
#
# More details please check the following article:
# http://antirez.com/post/redis-persistence-demystified.html
#
# If unsure, use “everysec”.
 
# appendfsync always
appendfsync everysec
# appendfsync no
 
# When the AOF fsync policy is set to always or everysec, and a background
# saving process (a background save or AOF log background rewriting) is
# performing a lot of I/O against the disk, in some Linux configurations
# Redis may block too long on the fsync() call. Note that there is no fix for
# this currently, as even performing fsync in a different thread will block
# our synchronous write(2) call.
#
# In order to mitigate this problem it’s possible to use the following option
# that will prevent fsync() from being called in the main process while a
# BGSAVE or BGREWRITEAOF is in progress.
#
# This means that while another child is saving, the durability of Redis is
# the same as “appendfsync none”. In practical terms, this means that it is
# possible to lose up to 30 seconds of log in the worst scenario (with the
# default Linux settings).
#
# If you have latency problems turn this to “yes”. Otherwise leave it as
# “no” that is the safest pick from the point of view of durability.
no-appendfsync-on-rewrite no
 
# Automatic rewrite of the append only file.
# Redis is able to automatically rewrite the log file implicitly calling
# BGREWRITEAOF when the AOF log size grows by the specified percentage.
#
# This is how it works: Redis remembers the size of the AOF file after the
# latest rewrite (if no rewrite has happened since the restart, the size of
# the AOF at startup is used).
#
# This base size is compared to the current size. If the current size is
# bigger than the specified percentage, the rewrite is triggered. Also
# you need to specify a minimal size for the AOF file to be rewritten, this
# is useful to avoid rewriting the AOF file even if the percentage increase
# is reached but it is still pretty small.
#
# Specify a percentage of zero in order to disable the automatic AOF
# rewrite feature.
 
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
 
################################ LUA SCRIPTING  ###############################
 
# Max execution time of a Lua script in milliseconds.
#
# If the maximum execution time is reached Redis will log that a script is
# still in execution after the maximum allowed time and will start to
# reply to queries with an error.
#
# When a long running script exceed the maximum execution time only the
# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be
# used to stop a script that did not yet called write commands. The second
# is the only way to shut down the server in the case a write commands was
# already issue by the script but the user don’t want to wait for the natural
# termination of the script.
#
# Set it to 0 or a negative value for unlimited execution without warnings.
lua-time-limit 5000
 
################################## SLOW LOG ###################################
 
# The Redis Slow Log is a system to log queries that exceeded a specified
# execution time. The execution time does not include the I/O operations
# like talking with the client, sending the reply and so forth,
# but just the time needed to actually execute the command (this is the only
# stage of command execution where the thread is blocked and can not serve
# other requests in the meantime).
#
# You can configure the slow log with two parameters: one tells Redis
# what is the execution time, in microseconds, to exceed in order for the
# command to get logged, and the other parameter is the length of the
# slow log. When a new command is logged the oldest one is removed from the
# queue of logged commands.
 
# The following time is expressed in microseconds, so 1000000 is equivalent
# to one second. Note that a negative number disables the slow log, while
# a value of zero forces the logging of every command.
slowlog-log-slower-than 10000
 
# There is no limit to this length. Just be aware that it will consume memory.
# You can reclaim memory used by the slow log with SLOWLOG RESET.
slowlog-max-len 128
 
############################### ADVANCED CONFIG ###############################
 
# Hashes are encoded using a memory efficient data structure when they have a
# small number of entries, and the biggest entry does not exceed a given
# threshold. These thresholds can be configured using the following directives.
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
 
# Similarly to hashes, small lists are also encoded in a special way in order
# to save a lot of space. The special representation is only used when
# you are under the following limits:
list-max-ziplist-entries 512
list-max-ziplist-value 64
 
# Sets have a special encoding in just one case: when a set is composed
# of just strings that happens to be integers in radix 10 in the range
# of 64 bit signed integers.
# The following configuration setting sets the limit in the size of the
# set in order to use this special memory saving encoding.
set-max-intset-entries 512
 
# Similarly to hashes and lists, sorted sets are also specially encoded in
# order to save a lot of space. This encoding is only used when the length and
# elements of a sorted set are below the following limits:
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
 
# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in
# order to help rehashing the main Redis hash table (the one mapping top-level
# keys to values). The hash table implementation Redis uses (see dict.c)
# performs a lazy rehashing: the more operation you run into an hash table
# that is rehashing, the more rehashing “steps” are performed, so if the
# server is idle the rehashing is never complete and some more memory is used
# by the hash table.
#
# The default is to use this millisecond 10 times every second in order to
# active rehashing the main dictionaries, freeing memory when possible.
#
# If unsure:
# use “activerehashing no” if you have hard latency requirements and it is
# not a good thing in your environment that Redis can reply form time to time
# to queries with 2 milliseconds delay.
#
# use “activerehashing yes” if you don’t have such hard requirements but
# want to free memory asap when possible.
activerehashing yes
 
# The client output buffer limits can be used to force disconnection of clients
# that are not reading data from the server fast enough for some reason (a
# common reason is that a Pub/Sub client can’t consume messages as fast as the
# publisher can produce them).
#
# The limit can be set differently for the three different classes of clients:
#
# normal -> normal clients
# slave  -> slave clients and MONITOR clients
# pubsub -> clients subcribed to at least one pubsub channel or pattern
#
# The syntax of every client-output-buffer-limit directive is the following:
#
# client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
#
# A client is immediately disconnected once the hard limit is reached, or if
# the soft limit is reached and remains reached for the specified number of
# seconds (continuously).
# So for instance if the hard limit is 32 megabytes and the soft limit is
# 16 megabytes / 10 seconds, the client will get disconnected immediately
# if the size of the output buffers reach 32 megabytes, but will also get
# disconnected if the client reaches 16 megabytes and continuously overcomes
# the limit for 10 seconds.
#
# By default normal clients are not limited because they don’t receive data
# without asking (in a push way), but just after a request, so only
# asynchronous clients may create a scenario where data is requested faster
# than it can read.
#
# Instead there is a default limit for pubsub and slave clients, since
# subscribers and slaves receive data in a push fashion.
#
# Both the hard or the soft limit can be disabled by setting them to zero.
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
 
# Redis calls an internal function to perform many background tasks, like
# closing connections of clients in timeot, purging expired keys that are
# never requested, and so forth.
#
# Not all tasks are perforemd with the same frequency, but Redis checks for
# tasks to perform accordingly to the specified “hz” value.
#
# By default “hz” is set to 10. Raising the value will use more CPU when
# Redis is idle, but at the same time will make Redis more responsive when
# there are many keys expiring at the same time, and timeouts may be
# handled with more precision.
#
# The range is between 1 and 500, however a value over 100 is usually not
# a good idea. Most users should use the default of 10 and raise this up to
# 100 only in environments where very low latency is required.
hz 10
 
# When a child rewrites the AOF file, if the following option is enabled
# the file will be fsync-ed every 32 MB of data generated. This is useful
# in order to commit the file to the disk more incrementally and avoid
# big latency spikes.
aof-rewrite-incremental-fsync yes
 
################################## INCLUDES ###################################
 
# Include one or more other config files here.  This is useful if you
# have a standard template that goes to all Redis server but also need
# to customize a few per-server settings.  Include files can include
# other files, so use this wisely.
#
# include /path/to/local.conf
# include /path/to/other.conf