第一 Redis 简介

1.1 什么是Redis

Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
Redis 与其他 key - value 缓存产品有以下三个特点:
Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
Redis支持数据的备份,即master-slave模式的数据备份。

1.2 Redis 应用场景

主要能够体现 解决数据库的访问压力。
   例如:短信验证码时间有效期、session共享解决方案

1.3 Redis 优势

性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

第二 Redis 基本数据类型之字符串(String)

Redis 字符串命令

命令

描述说明

SET key value

此命令设置指定键的值。

GET key

获取指定键的值。

GETRANGE key start end

获取存储在键上的字符串的子字符串。

GETSET key value

设置键的字符串值并返回其旧值。

GETBIT key offset

返回在键处存储的字符串值中偏移处的位值。

MGET key1 [key2..]

获取所有给定键的值

SETBIT key offset value

存储在键上的字符串值中设置或清除偏移处的位

SETEX key seconds value

使用键和到期时间来设置值

SETNX key value

设置键的值,仅当键不存在时

SETRANGE key offset value

在指定偏移处开始的键处覆盖字符串的一部分

STRLEN key

获取存储在键中的值的长度

MSET key value [key value …]

为多个键分别设置它们的值

MSETNX key value [key value …]

为多个键分别设置它们的值,仅当键不存在时

PSETEX key milliseconds value

设置键的值和到期时间(以毫秒为单位)

INCR key

将键的整数值增加1

INCRBY key increment

将键的整数值按给定的数值增加

INCRBYFLOAT key increment

将键的浮点值按给定的数值增加

DECR key

将键的整数值减1

DECRBY key decrement

按给定数值减少键的整数值

APPEND key value

将指定值附加到键

第三 Redis 基本数据类型之列表(List)

Redis列表:是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

Redis列表命令

命令及描述

BLPOP key1 [key2 ] timeout 
移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

BRPOP key1 [key2 ] timeout 
移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

BRPOPLPUSH source destination timeout 
从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

LINDEX key index 
通过索引获取列表中的元素

LINSERT key BEFORE|AFTER pivot value 
在列表的元素前或者后插入元素

LLEN key 
获取列表长度

LPOP key 
移出并获取列表的第一个元素

LPUSH key value1 [value2] 
将一个或多个值插入到列表头部

LPUSHX key value 
将一个值插入到已存在的列表头部

LRANGE key start stop 
获取列表指定范围内的元素

LREM key count value 
移除列表元素

LSET key index value 
通过索引设置列表元素的值

LTRIM key start stop 
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。

RPOP key 
移除并获取列表最后一个元素

RPOPLPUSH source destination 
移除列表的最后一个元素,并将该元素添加到另一个列表并返回

RPUSH key value1 [value2] 
在列表中添加一个或多个值

RPUSHX key value 
为已存在的列表添加值

第四 Redis 基本数据类型之集合(Set) 

Redis集合:是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
Redis 中 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

Redis集合命令

命令及描述

SADD key member1 [member2] 
向集合添加一个或多个成员

SCARD key 
获取集合的成员数

SDIFF key1 [key2] 
返回给定所有集合的差集

SDIFFSTORE destination key1 [key2] 
返回给定所有集合的差集并存储在 destination 中

SINTER key1 [key2] 
返回给定所有集合的交集

SINTERSTORE destination key1 [key2] 
返回给定所有集合的交集并存储在 destination 中

SISMEMBER key member 
判断 member 元素是否是集合 key 的成员

SMEMBERS key 
返回集合中的所有成员

SMOVE source destination member 
将 member 元素从 source 集合移动到 destination 集合

SPOP key 
移除并返回集合中的一个随机元素

SRANDMEMBER key [count] 
返回集合中一个或多个随机数

SREM key member1 [member2] 
移除集合中一个或多个成员

SUNION key1 [key2] 
返回所有给定集合的并集

SUNIONSTORE destination key1 [key2] 
所有给定集合的并集存储在 destination 集合中

SSCAN key cursor [MATCH pattern] [COUNT count] 
迭代集合中的元素

第五 Redis 基本数据类型之有序集合(Sorted Set)  

Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

Redis有序集合命令

命令及描述

ZADD key score1 member1 [score2 member2] 
向有序集合添加一个或多个成员,或者更新已存在成员的分数

ZCARD key 
获取有序集合的成员数

ZCOUNT key min max 
计算在有序集合中指定区间分数的成员数

ZINCRBY key increment member 
有序集合中对指定成员的分数加上增量 increment

ZINTERSTORE destination numkeys key [key ...] 
计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中

ZLEXCOUNT key min max 
在有序集合中计算指定字典区间内成员数量

ZRANGE key start stop [WITHSCORES] 
通过索引区间返回有序集合成指定区间内的成员

ZRANGEBYLEX key min max [LIMIT offset count] 
通过字典区间返回有序集合的成员

ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] 
通过分数返回有序集合指定区间内的成员

ZRANK key member 
返回有序集合中指定成员的索引

ZREM key member [member ...] 
移除有序集合中的一个或多个成员

ZREMRANGEBYLEX key min max 
移除有序集合中给定的字典区间的所有成员

ZREMRANGEBYRANK key start stop 
移除有序集合中给定的排名区间的所有成员

ZREMRANGEBYSCORE key min max 
移除有序集合中给定的分数区间的所有成员

ZREVRANGE key start stop [WITHSCORES] 
返回有序集中指定区间内的成员,通过索引,分数从高到底

ZREVRANGEBYSCORE key max min [WITHSCORES] 
返回有序集中指定分数区间内的成员,分数从高到低排序

ZREVRANK key member 
返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序

ZSCORE key member 
返回有序集中,成员的分数值

ZUNIONSTORE destination numkeys key [key ...] 
计算给定的一个或多个有序集的并集,并存储在新的 key 中

ZSCAN key cursor [MATCH pattern] [COUNT count] 
迭代有序集合中的元素(包括元素成员和元素分值)

 第六 Redis 哈希(Hash)  

Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。
Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)。

Redis 哈希命令

命令及描述

HDEL key field2 [field2] 
删除一个或多个哈希表字段

HEXISTS key field 
查看哈希表 key 中,指定的字段是否存在。

HGET key field 
获取存储在哈希表中指定字段的值。

HGETALL key 
获取在哈希表中指定 key 的所有字段和值

HINCRBY key field increment 
为哈希表 key 中的指定字段的整数值加上增量 increment 。

HINCRBYFLOAT key field increment 
为哈希表 key 中的指定字段的浮点数值加上增量 increment 。

HKEYS key 
获取所有哈希表中的字段

HLEN key 
获取哈希表中字段的数量

HMGET key field1 [field2] 
获取所有给定字段的值

HMSET key field1 value1 [field2 value2 ] 
同时将多个 field-value (域-值)对设置到哈希表 key 中。

HSET key field value 
将哈希表 key 中的字段 field 的值设为 value 。

HSETNX key field value 
只有在字段 field 不存在时,设置哈希表字段的值。

HVALS key 
获取哈希表中所有值

HSCAN key cursor [MATCH pattern] [COUNT count] 
迭代哈希表中的键值对。

 

第七 Windows 系统安装Redis

7.1 CMD窗口启动Redis服务

      切换至Redis解压文件的bin目录,执行如下代码:redis-server redis.windows.conf

7.2 将Redis作为服务安装在本机电脑中:

     切换至Redis解压文件的bin目录,执行如下代码:redis-server --service-install redis.windows.conf

安装完之后,就可看到Redis已经作为windows服务了:

redis解决刷新 redis多久刷新一次_有序集合

但是安装好之后,Redis并没有启动,启动命令如下:redis-server --service-start

Redis 服务停止,执行如下命令: redis-server --service-stop

Redif 服务协助,执行如下命令:redis-server --service-uninstall

 

7.3 redis.bat 脚本指令启动Redis 服务

在redis的目录下新建一个redis.bat脚本指令,脚本文件内容如下:

d:
cd redis
redis-server.exe redis.windows.conf

 

第8 Java项目之Redis 集成

8.1 Java Maven 项目集成Redis ,pom.xml 文件添加如下依赖:

<!-- redis jar包 -->
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>3.2.0</version>
		</dependency>

8.2 Redis 之字符串

package com.zzg.redis.util;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class RedisFactory {
	public static JedisPool jedisPool; // 池化管理jedis链接池

	static {
		JedisPoolConfig config = new JedisPoolConfig();
		// 设置最大连接数
		config.setMaxTotal(300);
		// 设置最大空闲数
		config.setMaxIdle(600);
//              //设置超时时间
		config.setMaxWaitMillis(10000);
		// 初始化连接池
		jedisPool = new JedisPool(config, "127.0.0.1", 6379, 10 * 1000);
	}

	/**
	 * 获取 redis链接
	 * 
	 * @return 2017年9月13日
	 */
	public static Jedis getResource() {
		return jedisPool.getResource();
		
	}
	
	public static void close() {
		jedisPool.close();
		
	}
	
	
	
}


package com.zzg.redis.string;

/**
 * Redis 字符串类型
 * @author Administrator
 *
 * @param <K>
 * @param <V>
 */
public interface RedisString<K, V> {
	 /**
     * 写入缓存 可以是对象
     * @param key
     * @param value
     */
	void set(final K key, V value);
	 /**
     * 读取String缓存 可以是对象
     * @param key
     * @return
     */
	V get(final K key);
	
	/**
	 * 获取存储在键上的字符串的子字符串
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	V getrange(final K key, int start, int end);
	
	/**
	 * 设置键的字符串值并返回其旧值。
	 * @param key
	 * @param value
	 * @return
	 */
	V getset(final K key, V value);
	
	/**
	 * 移除指定key
	 * @param key
	 */
	void del(final K key);
	
	/**
	 * 同时设置多个key-value
	 * @param keyvalues
	 */
	void mset(String ...keyvalues);
	
	/**
	 * 属性值增加
	 * @param key
	 */
	void incr(final K key);
	
	/**
	 * 属性值追加
	 * @param value
	 */
	void append(final K key, V value);
}


package com.zzg.redis.string;

import com.zzg.redis.util.RedisFactory;

public class RedisStringHandler implements RedisString<String, String> {
	private RedisFactory factory;

	public RedisFactory getFactory() {
		return factory;
	}

	public void setFactory(RedisFactory factory) {
		this.factory = factory;
	}
	
	public RedisStringHandler(RedisFactory factory) {
		super();
		this.factory = factory;
	}

	@Override
	public void set(String key, String value) {
		// TODO Auto-generated method stub
		factory.getResource().set(key, value);
	}

	@Override
	public String get(String key) {
		// TODO Auto-generated method stub
		return factory.getResource().get(key);
	}

	@Override
	public String getrange(String key, int start, int end) {
		// TODO Auto-generated method stub
		return factory.getResource().getrange(key, start, start);
	}

	@Override
	public String getset(String key, String value) {
		// TODO Auto-generated method stub
		return factory.getResource().getSet(key, value);
	}

	@Override
	public void del(String key) {
		// TODO Auto-generated method stub
		factory.getResource().del(key);
	
	}

	@Override
	public void mset(String... keyvalues) {
		// TODO Auto-generated method stub
		factory.getResource().mset(keyvalues);
		
	}

	@Override
	public void incr(String key) {
		// TODO Auto-generated method stub
		factory.getResource().incr(key);
	}

	@Override
	public void append(final String key, String value) {
		// TODO Auto-generated method stub
		factory.getResource().append(key, value);
	}

}

测试代码:

package com.zzg.redis;

import com.zzg.redis.string.RedisStringHandler;
import com.zzg.redis.util.RedisFactory;

public class StringTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		RedisStringHandler handler = new RedisStringHandler(new RedisFactory());
		// redis 设置
		handler.set("key", "zzg");
		// redis 获取
		String value = handler.get("key");
		System.out.println("value is:" + value);
		
		// redis 追加
		handler.append("key", "is java developer");
		System.out.println("value is:" + handler.get("key"));
		
		// redis 移除
		handler.del("key");
		System.out.println("value is:" + handler.get("key"));
		
		// redis 设置多个key-value
		handler.mset("name", "zzg", "age", "28","qq","605739");
		handler.incr("age"); //进行加1操作
	    System.out.println(handler.get("name") + "-" + handler.get("age") + "-" + handler.get("qq"));
		
	    handler.getFactory().close();
	}

}

8.3 Redis 之Map

package com.zzg.redis.string;

import java.util.List;
import java.util.Set;

public interface RedisMap<K, V> {
	/**
	 * 写入缓存
	 * @param key
	 * @param value
	 */
	void hmset(final K key, V value);
	
	/**
	 * 获取缓存
	 * @param keys
	 * @return
	 */
	List<String> hmget(String key, String ...keys);
	
	/**
	 * 移除Map 指定属性值
	 * @param key
	 * @param properties
	 */
	void hdel(final K key, String properties);
	
	/**
	 * 获取指定Map中的指定属性
	 * @param key
	 * @param properties
	 * @return
	 */
	Object hmget(final K key, String properties);
	
	/**
	 * 返回指定Map中的个数
	 * @param key
	 * @return
	 */
	Long hlen(final K key);
	
	/**
	 * 判断指定key Map是否存在
	 * @param key
	 * @return
	 */
	boolean exists(final K key);
	
	/**
	 * 返回指定Map 所包含的key 值
	 * @param key
	 */
	Set<String> hkeys(final K key);
	
	
}


package com.zzg.redis.string;

import java.util.List;
import java.util.Map;
import java.util.Set;

import com.zzg.redis.util.RedisFactory;

public class RedisMapHandler implements RedisMap<String, Map> {

	private RedisFactory factory;

	public RedisFactory getFactory() {
		return factory;
	}

	public void setFactory(RedisFactory factory) {
		this.factory = factory;
	}
	
	public RedisMapHandler(RedisFactory factory) {
		super();
		this.factory = factory;
	}
	@Override
	public void hmset(String key, Map value) {
		// TODO Auto-generated method stub
		factory.getResource().hmset(key, value);
	}

	@Override
	public List<String> hmget(String key, String... fields) {
		// TODO Auto-generated method stub
		return factory.getResource().hmget(key, fields);
	}

	@Override
	public void hdel(String key, String properties) {
		// TODO Auto-generated method stub
		factory.getResource().hdel(key, properties);
	}

	@Override
	public Object hmget(String key, String properties) {
		// TODO Auto-generated method stub
		return factory.getResource().hmget(key, properties);
	}

	@Override
	public Long hlen(String key) {
		// TODO Auto-generated method stub
		return factory.getResource().hlen(key);
	}

	@Override
	public boolean exists(String key) {
		// TODO Auto-generated method stub
		return factory.getResource().exists(key);
	}

	@Override
	public Set<String> hkeys(String key) {
		// TODO Auto-generated method stub
		return factory.getResource().hkeys(key);
	}



}

测试代码:

package com.zzg.redis;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.zzg.redis.string.RedisMapHandler;
import com.zzg.redis.util.RedisFactory;

public class MapTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		RedisMapHandler handler = new RedisMapHandler(new RedisFactory());

		Map<String, String> map = new HashMap<String, String>();
		map.put("name", "zzg");
		map.put("age", "28");
		map.put("qq", "60573");
		// redis 设置
		handler.hmset("user", map);
		
		// redis 获取
		 List<String> rsmap = handler.hmget("user", "name", "age", "qq");
	     System.out.println(rsmap);  
	     
	     // redis 删除
	     handler.hdel("user","age");
	     
	     System.out.println(handler.hmget("user", "age")); //因为删除了,所以返回的是null  
	     System.out.println(handler.hlen("user")); //返回key为user的键中存放的值的个数2 
	     System.out.println(handler.exists("user"));//是否存在key为user的记录 返回true  
	     System.out.println(handler.hkeys("user"));//返回map对象中的所有key  
	     
	     
	     Iterator<String> iter=handler.hkeys("user").iterator();  
	        while (iter.hasNext()){  
	            String key = iter.next();  
	            System.out.println(key+":"+handler.hmget("user",key));  
	        }  
	}

}

8.5 Redis 之List

package com.zzg.redis.string;

import java.util.List;

public interface RedisList<K, V> {
	/**
	 * Redis 移除指定key
	 * @param key
	 */
	void del(final K key);
	
	/**
	 * Redis List 写入缓存
	 * @param key
	 * @param values
	 */
	void lpush(final K key, String ...values);
	
	/**
	 * Redis List 查询
	 * @param key
	 * @param start
	 * @param stop
	 * @return
	 */
	List<String> lrange(final K key, long start, long stop);
	
	/**
	 * Redis List 写入缓存
	 * @param key
	 * @param values
	 */
	void rpush(final K key, String ...values);
}


package com.zzg.redis.string;

import java.util.List;

import com.zzg.redis.util.RedisFactory;

public class RedisListHandler implements RedisList<String, List> {

	private RedisFactory factory;

	public RedisFactory getFactory() {
		return factory;
	}

	public void setFactory(RedisFactory factory) {
		this.factory = factory;
	}
	
	public RedisListHandler(RedisFactory factory) {
		super();
		this.factory = factory;
	}
	
	@Override
	public void del(String key) {
		// TODO Auto-generated method stub
		factory.getResource().del(key);
	}

	@Override
	public void lpush(String key, String... values) {
		// TODO Auto-generated method stub
		factory.getResource().lpush(key, values);
		
		
	}

	@Override
	public List<String> lrange(String key, long start, long stop) {
		// TODO Auto-generated method stub
		return factory.getResource().lrange(key, start, stop);
	}

	@Override
	public void rpush(String key, String... values) {
		// TODO Auto-generated method stub
		factory.getResource().rpush(key, values);
	}
	
	


}

测试功能代码:

package com.zzg.redis;

import com.zzg.redis.string.RedisListHandler;
import com.zzg.redis.util.RedisFactory;

public class ListTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		RedisListHandler handler = new RedisListHandler(new RedisFactory());
		
		handler.del("java framework");  
	    System.out.println(handler.lrange("java framework",0,-1)); 
	     
	    
	    //先向key java framework中存放三条数据  
	    handler.lpush("java framework","spring");  
	    handler.lpush("java framework","struts");  
	    handler.lpush("java framework","hibernate");  
        //再取出所有数据jedis.lrange是按范围取出,  
        // 第一个是key,第二个是起始位置,第三个是结束位置,jedis.llen获取长度 -1表示取得所有  
        System.out.println(handler.lrange("java framework",0,-1));  
        
        
        
        handler.del("java framework");
        handler.rpush("java framework","spring");  
        handler.rpush("java framework","struts");  
        handler.rpush("java framework","hibernate"); 
        System.out.println(handler.lrange("java framework",0,-1));
	     
	     handler.getFactory().close();
	}

}

8.6 Redis 之Set

package com.zzg.redis.string;

import java.util.List;
import java.util.Set;

public interface RedisSet<K, V> {
	/**
	 * redis 缓存对象
	 * @param key
	 * @param value
	 */
	void sadd(final K key, V value);
	
	/**
	 * redis 移除缓存对照
	 * @param key
	 * @param value
	 */
	void srem(final K key, V value);
	
	/**
	 * 获取指定key
	 * @param key
	 * @return
	 */
	Set<V> smembers(final K key);
	
	/**
	 * redis 指定key 是否包含指定的value
	 * @param key
	 * @param value
	 * @return
	 */
	boolean sismember(final K key, V value);
	
	/**
	 * redis 返回指定key 的数量
	 * @param key
	 * @return
	 */
	long scard(final K key);
	

}


package com.zzg.redis.string;

import java.util.Set;

import com.zzg.redis.util.RedisFactory;

public class RedisSetHandler implements RedisSet<String, String> {
	
	private RedisFactory factory;

	public RedisFactory getFactory() {
		return factory;
	}

	public void setFactory(RedisFactory factory) {
		this.factory = factory;
	}
	
	public RedisSetHandler(RedisFactory factory) {
		super();
		this.factory = factory;
	}
	
	@Override
	public void sadd(String key, String value) {
		// TODO Auto-generated method stub
		this.factory.getResource().sadd(key, value);
	}

	@Override
	public void srem(String key, String value) {
		// TODO Auto-generated method stub
		this.factory.getResource().srem(key, value);
	}

	@Override
	public Set<String> smembers(String key) {
		// TODO Auto-generated method stub
		return this.factory.getResource().smembers(key);
	}

	@Override
	public boolean sismember(String key, String value) {
		// TODO Auto-generated method stub
		return this.factory.getResource().sismember(key, value);
	}

	@Override
	public long scard(String key) {
		// TODO Auto-generated method stub
		return this.factory.getResource().scard(key);
	}

	

}

测试功能代码:

package com.zzg.redis;

import com.zzg.redis.string.RedisSetHandler;
import com.zzg.redis.util.RedisFactory;

public class SetTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		RedisSetHandler handler = new RedisSetHandler(new RedisFactory());
		
		handler.sadd("users","liuling");  
		handler.sadd("users","xinxin");  
		handler.sadd("users","ling");  
		handler.sadd("users","zhangxinxin");
		handler.sadd("users","who");  
        //移除noname  
		handler.srem("users","who");  
        System.out.println(handler.smembers("users"));//获取所有加入的value  
        System.out.println(handler.sismember("users", "who"));//判断 who 是否是user集合的元素  
  
        System.out.println(handler.scard("users"));//返回集合的元素个数  
		
		handler.getFactory().close();
	}

}

第9 SpringBoot项目之Redis 集成

参考文章:SpringBoot 集成Redis

第10 Windows 之Redis 主从复制

10.1 Redis 主从复制 概述

       1、redis的复制功能是支持多个数据库之间的数据同步。一类是主数据库(master)一类是从数据库(slave),主数据库可以进行读写操作,当发生写操作的时候自动将数据同步到从数据库,而从数据库一般是只读的,并接收主数据库同步过来的数据,一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。

       2、通过redis的复制功能可以很好的实现数据库的读写分离,提高服务器的负载能力。主数据库主要进行写操作,而从数据库负责读操作。

 

10.2 Redis 主从复制 图解

redis解决刷新 redis多久刷新一次_redis_02

过程:
1:当一个从数据库启动时,会向主数据库发送sync命令,
2:主数据库接收到sync命令后会开始在后台保存快照(执行rdb操作),并将保存期间接收到的命令缓存起来
3:当快照完成后,redis会将快照文件和所有缓存的命令发送给从数据库。
4:从数据库收到后,会载入快照文件并执行收到的缓存的命令。

10.3 Windows 系统配置Redis 主从配置

目标:windows 系统实现Redis 主从配置,主端口:7001 ,从端口:7002

主从配置一般流程:

1、在redis根目录建二个文件夹,分别命名为7001、7002。这二个名称分为表示redis启动后的端口号。

redis解决刷新 redis多久刷新一次_Redis_03

2、再建立二个bat,分别命名为7001、7002。内容如下,为了方便启动二个redis服务使用。

redis解决刷新 redis多久刷新一次_有序集合_04

redis解决刷新 redis多久刷新一次_有序集合_05

3、将redis根目录下的redis.windows.conf文件分别拷贝至7001、7002文件夹。

4、打开7001文件夹下的redis.windows.conf文件。

修改项目内容:port 。值7001。

redis解决刷新 redis多久刷新一次_有序集合_06

 

5、打开7002文件夹下的redis.windows.conf文件。

修改项目内容:port、slaveof。

7002的文件修改端口为7002,

slaveof  127.0.0.1 7001 

redis解决刷新 redis多久刷新一次_Redis_07

redis解决刷新 redis多久刷新一次_Redis_08

这样一一主一从就配置完毕了,简单点说只是修改了一句话即可。slaveof  127.0.0.1 7001 

6、先从7001、7002依次启动二个服务。

启动服务后二个服务的控制台分别会显示主从信息。

主控制台会显示那些从服务加入进来。内容显示7002分别加入进来了并同步数据

redis解决刷新 redis多久刷新一次_redis解决刷新_09

从控制台会显示加入了那个主服务。

redis解决刷新 redis多久刷新一次_有序集合_10

 

7、主从测试

redis-cli.exe -h 127.0.0.1 -p 7001 #首先连接7001
127.0.0.1:7001> keys * #查看是否存在key
(empty list or set) #显示不存在任何key
127.0.0.1:7001> set testkey tesvvalue #设置一个名字叫做testkey的key,值为tesvvalue。
OK #赋值成功
127.0.0.1:7001> quit #退出7001

redis-cli.exe -h 127.0.0.1 -p 7002 #连接7002
127.0.0.1:7002> keys * #查看是否存在key
1) "testkey" #显示存在一个名字叫做testkey的key
127.0.0.1:7002> get testkey #获得testkey的值
"tesvvalue" #值为tesvvalue
127.0.0.1:7002> set noinsert yes #测试是否可以在7002set数据。
(error) READONLY You can't write against a read only slave. #出错,提示这是一个只读的从服务。

经过以上测试表示我们的主从复制成功了。

 第11 Windows 之Redis 哨兵机制

11.1 哨兵机制概述

Redis的哨兵(sentinel) 系统用于管理多个 Redis 服务器,该系统执行以下三个任务:

·        监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master和Slave是否运作正常。

·        提醒(Notification):当被监控的某个 Redis出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知。

·        自动故障迁移(Automatic failover):当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作,它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其他Slave改为复制新的Master; 当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用Master代替失效Master。

哨兵(sentinel) 是一个分布式系统,你可以在一个架构中运行多个哨兵(sentinel) 进程,这些进程使用流言协议(gossipprotocols)来接收关于Master是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master.

每个哨兵(sentinel) 会向其它哨兵(sentinel)、master、slave定时发送消息,以确认对方是否”活”着,如果发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂(所谓的”主观认为宕机” Subjective Down,简称sdown).

若“哨兵群”中的多数sentinel,都报告某一master没响应,系统才认为该master"彻底死亡"(即:客观上的真正down机,Objective Down,简称odown),通过一定的vote算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置.

虽然哨兵(sentinel) 释出为一个单独的可执行文件 redis-sentinel ,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你可以在启动一个普通 Redis 服务器时通过给定 --sentinel 选项来启动哨兵(sentinel).

哨兵(sentinel) 的一些设计思路和zookeeper非常类似

11.2 哨兵机制图解

redis解决刷新 redis多久刷新一次_redis解决刷新_11

11.3 windows 搭建哨兵机制

哨兵机制搭建一般流程:

参考 :10.3 Windows 系统配置Redis 主从配置

哨兵配置:

 1、在每一个7001 和7002文件夹中都创建一个文sentinel.conf文件。

 2、7001的sentinel.conf文件内容:

#当前Sentinel服务运行的端口
port 26379
#master
#Sentinel去监视一个名为mymaster的主redis实例,这个主实例的IP地址为本机地址127.0.0.1,端口号为6379,
#而将这个主实例判断为失效至少需要2个 Sentinel进程的同意,只要同意Sentinel的数量不达标,自动failover就不会执行
sentinel monitor mymaster 127.0.0.1 7001 1
#指定了Sentinel认为Redis实例已经失效所需的毫秒数。当 实例超过该时间没有返回PING,或者直接返回错误,那么Sentinel将这个实例标记为主观下线。
#只有一个 Sentinel进程将实例标记为主观下线并不一定会引起实例的自动故障迁移:只有在足够数量的Sentinel都将一个实例标记为主观下线之后,实例才会被标记为客观下线,这时自动故障迁移才会执行
sentinel down-after-milliseconds mymaster 5000
#指定了在执行故障转移时,最多可以有多少个从Redis实例在同步新的主实例,在从Redis实例较多的情况下这个数字越小,同步的时间越长,完成故障转移所需的时间就越长
sentinel config-epoch mymaster 12
#如果在该时间(ms)内未能完成failover操作,则认为该failover失败
sentinel leader-epoch mymaster 13

3、7002 的sentinel.conf文件内容:

#当前Sentine2服务运行的端口
port 26479
#slave1
sentinel monitor mymaster 127.0.0.1 7001 1
sentinel down-after-milliseconds mymaster 5000
sentinel config-epoch mymaster 12
sentinel leader-epoch mymaster 13

4、编写7001_sentinel.bat 和7002_sentinel.bat 脚本指令

7001_sentinel.bat 内容如下:

cd 7001
title sentinel-7001
redis-server.exe sentinel.conf --sentinel

说明:title命名规则 启动成功后显示的title, 注意“--sentinel”不是注释,必须存在。

7002_sentinel.bat 内容如下:

cd 7002
title sentinel-7002
redis-server.exe sentinel.conf --sentinel

说明:title命名规则 启动成功后显示的title, 注意“--sentinel”不是注释,必须存在。

redis解决刷新 redis多久刷新一次_redis_12

5、测试 

启动redis 主服务(7001.bat) 和redis 从服务(7002bat)

启动redis 主服务的哨兵机制(7001_sentinel.bat) 和redis 从服务的哨兵机制(7002_sentinel.bat)

 

查看是主库还是从库,cmd进入对应的redis文件夹-》redis-cli.exe -p 服务端口

然后再输入: info replication

7001 主:

redis解决刷新 redis多久刷新一次_redis_13

7002 从:

redis解决刷新 redis多久刷新一次_Redis_14

  3、查看哨兵sentinel状态,cmd进入对应的redis文件夹-》redis-cli.exe -p 哨兵配置端口,输入info sentinel

          主:

redis解决刷新 redis多久刷新一次_有序集合_15

      从:

 

redis解决刷新 redis多久刷新一次_redis解决刷新_16

4、关闭掉主库,然后再按照步骤5测试一遍+观察。从库会变成主库,再启动原来被关闭掉的主库后,会自动变成从库。

第12 Redis 事务

Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证:

事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

一个事务从开始到执行会经历以下三个阶段:

开始事务。

命令入队。

执行事务。

 

12.1 实例

以下是一个事务的例子, 它先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令:

redis 127.0.0.1:6379> MULTI
OK

redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUED

redis 127.0.0.1:6379> GET book-name
QUEUED

redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED

redis 127.0.0.1:6379> SMEMBERS tag
QUEUED

redis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
   2) "C++"
   3) "Programming"

12.2 事务常用命令

序号

命令及描述

1

DISCARD 
取消事务,放弃执行事务块内的所有命令。

2

EXEC 
执行所有事务块内的命令。

3

MULTI 
标记一个事务块的开始。

4

UNWATCH 
取消 WATCH 命令对所有 key 的监视。

5

WATCH key [key ...] 
监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

 

第13 Redis 持久化

13.1 什么是持久化

什么是Redis持久化,就是将内存数据保存到硬盘。

Redis 持久化存储 (AOF 与 RDB 两种模式)

13.2 RDB持久化

RDB 是在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。
优点:使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能
缺点:RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候

这里说的这个执行数据写入到临时文件的时间点是可以通过配置来自己确定的,通过配置redis 在 n 秒内如果超过 m 个 key 被修改这执行一次 RDB 操作。这个操作就类似于在这个时间点来保存一次 Redis 的所有数据,一次快照数据。所有这个持久化方法也通常叫做 snapshots。

RDB 默认开启,redis.conf 中的具体配置参数如下;

#dbfilename:持久化数据存储在本地的文件
dbfilename dump.rdb
#dir:持久化数据存储在本地的路径,如果是在/redis/redis-3.0.6/src下启动的redis-cli,则数据会存储在当前src目录下
dir ./
##snapshot触发的时机,save    
##如下为900秒后,至少有一个变更操作,才会snapshot  
##对于此值的设置,需要谨慎,评估系统的变更操作密集程度  
##可以通过“save “””来关闭snapshot功能  
#save时间,以下分别表示更改了1个key时间隔900s进行持久化存储;更改了10个key300s进行存储;更改10000个key60s进行存储。
save 900 1
save 300 10
save 60 10000
##当snapshot时出现错误无法继续时,是否阻塞客户端“变更操作”,“错误”可能因为磁盘已满/磁盘故障/OS级别异常等  
stop-writes-on-bgsave-error yes  
##是否启用rdb文件压缩,默认为“yes”,压缩往往意味着“额外的cpu消耗”,同时也意味这较小的文件尺寸以及较短的网络传输时间  
rdbcompression yes

13.3 AOF持久化

Append-only file,将“操作 + 数据”以格式化指令的方式追加到操作日志文件的尾部,在 append 操作返回后(已经写入到文件或者即将写入),才进行实际的数据变更,“日志文件”保存了历史所有的操作过程;当 server 需要数据恢复时,可以直接 replay 此日志文件,即可还原所有的操作过程。AOF 相对可靠,它和 mysql 中 bin.log、apache.log、zookeeper 中 txn-log 简直异曲同工。AOF 文件内容是字符串,非常容易阅读和解析。
优点:可以保持更高的数据完整性,如果设置追加 file 的时间是 1s,如果 redis 发生故障,最多会丢失 1s 的数据;且如果日志写入不完整支持 redis-check-aof 来进行日志修复;AOF 文件没被 rewrite 之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的 flushall)。
缺点:AOF 文件比 RDB 文件大,且恢复速度慢。

我们可以简单的认为 AOF 就是日志文件,此文件只会记录“变更操作”(例如:set/del 等),如果 server 中持续的大量变更操作,将会导致 AOF 文件非常的庞大,意味着 server 失效后,数据恢复的过程将会很长;事实上,一条数据经过多次变更,将会产生多条 AOF 记录,其实只要保存当前的状态,历史的操作记录是可以抛弃的;因为 AOF 持久化模式还伴生了“AOF rewrite”。
AOF 的特性决定了它相对比较安全,如果你期望数据更少的丢失,那么可以采用 AOF 模式。如果 AOF 文件正在被写入时突然 server 失效,有可能导致文件的最后一次记录是不完整,你可以通过手工或者程序的方式去检测并修正不完整的记录,以便通过 aof 文件恢复能够正常;同时需要提醒,如果你的 redis 持久化手段中有 aof,那么在 server 故障失效后再次启动前,需要检测 aof 文件的完整性。

AOF 默认关闭,开启方法,修改配置文件 reds.conf:appendonly yes

##此选项为aof功能的开关,默认为“no”,可以通过“yes”来开启aof功能  
##只有在“yes”下,aof重写/文件同步等特性才会生效  
appendonly yes  

##指定aof文件名称  
appendfilename appendonly.aof  

##指定aof操作中文件同步策略,有三个合法值:always everysec no,默认为everysec  
appendfsync everysec  
##在aof-rewrite期间,appendfsync是否暂缓文件同步,"no"表示“不暂缓”,“yes”表示“暂缓”,默认为“no”  
no-appendfsync-on-rewrite no  

##aof文件rewrite触发的最小文件尺寸(mb,gb),只有大于此aof文件大于此尺寸是才会触发rewrite,默认“64mb”,建议“512mb”  
auto-aof-rewrite-min-size 64mb  

##相对于“上一次”rewrite,本次rewrite触发时aof文件应该增长的百分比。  
##每一次rewrite之后,redis都会记录下此时“新aof”文件的大小(例如A),那么当aof文件增长到A*(1 + p)之后  
##触发下一次rewrite,每一次aof记录的添加,都会检测当前aof文件的尺寸。  
auto-aof-rewrite-percentage 100

AOF 是文件操作,对于变更操作比较密集的 server,那么必将造成磁盘 IO 的负荷加重;此外 linux 对文件操作采取了“延迟写入”手段,即并非每次 write 操作都会触发实际磁盘操作,而是进入了 buffer 中,当 buffer 数据达到阀值时触发实际写入(也有其他时机),这是 linux 对文件系统的优化,但是这却有可能带来隐患,如果 buffer 没有刷新到磁盘,此时物理机器失效(比如断电),那么有可能导致最后一条或者多条 aof 记录的丢失。通过上述配置文件,可以得知 redis 提供了 3 中 aof 记录同步选项:

always:每一条 aof 记录都立即同步到文件,这是最安全的方式,也以为更多的磁盘操作和阻塞延迟,是 IO 开支较大。

everysec:每秒同步一次,性能和安全都比较中庸的方式,也是 redis 推荐的方式。如果遇到物理服务器故障,有可能导致最近一秒内 aof 记录丢失(可能为部分丢失)。

no:redis 并不直接调用文件同步,而是交给操作系统来处理,操作系统可以根据 buffer 填充情况 / 通道空闲时间等择机触发同步;这是一种普通的文件操作方式。性能较好,在物理服务器故障时,数据丢失量会因 OS 配置有关。

其实,我们可以选择的太少,everysec 是最佳的选择。如果你非常在意每个数据都极其可靠,建议你选择一款“关系性数据库”吧。
AOF 文件会不断增大,它的大小直接影响“故障恢复”的时间, 而且 AOF 文件中历史操作是可以丢弃的。AOF rewrite 操作就是“压缩”AOF 文件的过程,当然 redis 并没有采用“基于原 aof 文件”来重写的方式,而是采取了类似 snapshot 的方式:基于 copy-on-write,全量遍历内存中数据,然后逐个序列到 aof 文件中。因此 AOF rewrite 能够正确反应当前内存数据的状态,这正是我们所需要的;*rewrite 过程中,对于新的变更操作将仍然被写入到原 AOF 文件中,同时这些新的变更操作也会被 redis 收集起来(buffer,copy-on-write 方式下,最极端的可能是所有的 key 都在此期间被修改,将会耗费 2 倍内存),当内存数据被全部写入到新的 aof 文件之后,收集的新的变更操作也将会一并追加到新的 aof 文件中,此后将会重命名新的 aof 文件为 appendonly.aof, 此后所有的操作都将被写入新的 aof 文件。如果在 rewrite 过程中,出现故障,将不会影响原 AOF 文件的正常工作,只有当 rewrite 完成之后才会切换文件,因为 rewrite 过程是比较可靠的。*

触发 rewrite 的时机可以通过配置文件来声明,同时 redis 中可以通过 bgrewriteaof 指令人工干预。

redis-cli -h ip -p port bgrewriteaof

因为 rewrite 操作 /aof 记录同步 /snapshot 都消耗磁盘 IO,redis 采取了“schedule”策略:无论是“人工干预”还是系统触发,snapshot 和 rewrite 需要逐个被执行。

AOF rewrite 过程并不阻塞客户端请求。系统会开启一个子进程来完成。

13.4 AOF与RDB区别

AOF 和 RDB 各有优缺点,这是有它们各自的特点所决定:

1) AOF 更加安全,可以将数据更加及时的同步到文件中,但是 AOF 需要较多的磁盘 IO 开支,AOF 文件尺寸较大,文件内容恢复数度相对较慢。
*2) snapshot,安全性较差,它是“正常时期”数据备份以及 master-slave 数据同步的最佳手段,文件尺寸较小,恢复数度较快。

可以通过配置文件来指定它们中的一种,或者同时使用它们(不建议同时使用),或者全部禁用,在架构良好的环境中,master 通常使用 AOF,slave 使用 snapshot,主要原因是 master 需要首先确保数据完整性,它作为数据备份的第一选择;slave 提供只读服务(目前 slave 只能提供读取服务),它的主要目的就是快速响应客户端 read 请求;但是如果你的 redis 运行在网络稳定性差 / 物理环境糟糕情况下,建议你 master 和 slave 均采取 AOF,这个在 master 和 slave 角色切换时,可以减少“人工数据备份”/“人工引导数据恢复”的时间成本;如果你的环境一切非常良好,且服务需要接收密集性的 write 操作,那么建议 master 采取 snapshot,而 slave 采用 AOF。

第14 Redis 发布订阅

Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

Redis 客户端可以订阅任意数量的频道。

下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:

redis解决刷新 redis多久刷新一次_Redis_17

当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

redis解决刷新 redis多久刷新一次_Redis_18

14.1 实例

以下实例演示了发布订阅是如何工作的。在我们实例中我们创建了订阅频道名为 redisChat:

redis 127.0.0.1:6379> SUBSCRIBE redisChat

Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1

现在,我们先重新开启个 redis 客户端,然后在同一个频道 redisChat 发布两次消息,订阅者就能接收到消息。

redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique"

(integer) 1

redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis by runoob.com"

(integer) 1

# 订阅者的客户端会显示如下消息
1) "message"
2) "redisChat"
3) "Redis is a great caching technique"
1) "message"
2) "redisChat"
3) "Learn redis by runoob.com"

14.2 Redis 发布订阅常用命令

序号

命令及描述

1

PSUBSCRIBE pattern [pattern ...] 
订阅一个或多个符合给定模式的频道。

2

PUBSUB subcommand [argument [argument ...]] 
查看订阅与发布系统状态。

3

PUBLISH channel message 
将信息发送到指定的频道。

4

PUNSUBSCRIBE [pattern [pattern ...]] 
退订所有给定模式的频道。

5

SUBSCRIBE channel [channel ...] 
订阅给定的一个或多个频道的信息。

6

UNSUBSCRIBE [channel [channel ...]] 
指退订给定的频道。

第15 Redis 集群