springboot整合redis

一、redis简介

开源免费用C语言编写的,是一个高性能的key-value分布式内存数据库并支持持久化的nosql数据库。

特点:

  • Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用
  • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储
  • Redis支持数据的备份,即master-slave模式的数据备份

二、linux下安装redis

下载地址1 下载地址2
1、快速安装
下载地址:https://redis.io/download

命令行下载并安装:
    $ wget http://download.redis.io/releases/redis-4.0.9.tar.gz
    $ tar -zxvf redis-4.0.9.tar.gz
    $ cd redis-5.0.0
    $ make
    $ make install

如果make安装失败,则提示缺少gcc环境
    $ yum install gcc-c++
    $ make distclean
    $ make
    $ make install    

启动服务端:src/redis-server
启动客户端:src/redis-cli
    
存取数据测试:
    $ redis> set foo bar
    $ OK
    $ redis> get foo
    $ "bar"
    $ ps -ef|grep redis 查看redis运行进程
    $ shutdown  关闭
    $ exit  退出
复制代码
2、默认是本地访问的,需要开放外网访问
打开redis.conf文件在NETWORK部分修改
注释掉bind 127.0.0.1可以使所有的ip访问redis
修改 protected-mode,值改为no
修改 daemonize,值改为yes
复制代码
3、Redis启动后杂项基础知识讲解
单进程,默认16个数据库,类似数组下表从零开始,初始默认使用零号库
    $ select index      切换数据库
    $ dbsize            查看当前数据库的key的数量
    $ flushdb           清空当前库
    $ flushall          通杀全部库
统一密码管理,16个库都是同样密码,要么都OK要么一个也连接不上
Redis索引都是从零开始
默认端口号6379
复制代码
4、api的理解和使用
通用命令:
    $ keys *    遍历所有的key
    $ keys nam*     查找nam开头的所有key
    $ dbsize    计算key的总数
    $ exists name   检查key为name的数据是否存在,存在返回1,不存在返回0
    $ del name  删除指定key为name的数据,删除成功返回1,失败返回0
    $ expire name 5     key为name的数据在5s后过期,成功返回1,失败返回0
    $ ttl name  查看key为name的数据剩余过期时间,-2过期,-1永久不过期
    $ persist name   去掉key为name的数据过期时间,成功返回1,失败返回0
    $ type name     返回key为name的数据的类型
    $ flushdb   清空数据库

单线程速度快
    纯内存中操作
    非阻塞IO
    避免线程切换和竟态消耗
复制代码

三、redis常见数据类型操作指令链接

四、redis中key常用指令

key *               查找所有的key值
exists key          判断某个key是否存在
move key dbIndex    把当前数据库指定的key移动到指定的数据库里
expire key s        为给定的key设置过期时间(秒)
ttl key             查看还有多少秒过期,-1表示永不过期,-2表示已过期
type key            查看你的key是什么类型
复制代码

五、字符串类型(String)

1、String操作指令
  • String类型是redis最基本的数据类型,一个key对应一个value
  • String类型是二进制安全的,redis的String可以包含任何数据,比如jpg图片或者序列化的对象
  • String类型是redis最基本的数据类型,一个redis中字符串value最多可以是512M
第一组
set key value       设置值
get key             获取值
del key             删除值
append key value    将value追加到指定key的旧的value
strlen key          获取key对应的value的长度
复制代码
第二组
incr key        自增1,如果key不存在,自增后get(key)=1
decr key        自减1,如果key不存在,自增后get(key)=-1
incrby key k    自增k,如果key不存在,自增后get(key)=k
decrby key k    自减k,如果key不存在,自增后get(key)=-k
复制代码
第三组
getrange key startIndex endIndex    范围内取值,endIndex取-1,代表最末尾
setrange key startIndex endIndex    范围内设置值
复制代码
第四组
setex key s value   设置key-value的过期时间
set key value       不管key是否存在,都设置值
setnx key value     key不存在才设置,成功返回1,失败返回0
set key value xx    key存在才设置
复制代码
第五组
mget key1 key2 key3             批量获取key
mset key1 value1 key2 value2    批量设置key-value
msetnx k1 v1 k2 v2              批量设置key-value(k都不存在)    
getset key newvalue             设置并返回旧的value
复制代码
jedis介绍
Jedis是redis的java版本的客户端实现。
    单实例连接:通过创建单实例jedis对象连接redis服务
复制代码
    /**
     * 单实例连接jedis
     */
    @Test
    public void testJedis() {
        Jedis jedis = new Jedis("192.168.66.129",6379);
        jedis.set("name", "zhangsan");
        String name = jedis.get("name");
        System.out.println(name);//张三
        System.out.println(jedis.get("age"));
        jedis.close();
    }
复制代码
    使用连接池连接:通过单实例连接redis不能对redis连接进行共享,可以使用连接池对redis进行连接共享,
    提高资源利用率,使用jedisPool连接redis服务
复制代码
    /**
    * 使用连接池
    */
    @Test
    public void TestJedisPool() {
    	JedisPoolConfig config = new JedisPoolConfig();
    	config.setMaxTotal(30);//最大连接数
    	config.setMaxIdle(2);//最大连接空闲数
    	JedisPool pool = new JedisPool(config,"192.168.66.129",6379);
    	Jedis jedis = null;
    	try {
            jedis = pool.getResource();
            jedis.set("username", "zhubanjie");
            String name = jedis.get("username");
            System.out.println(name);
    	} catch (Exception e) {
            e.printStackTrace();
    	}finally {
            if(jedis!=null) {
            	jedis.close();//关闭连接
            }
    	}
    }
复制代码
java中使用jedis
public class StringTest {
    @Test
    public void test1() {
    	JedisPoolConfig config = new JedisPoolConfig();
    	config.setMaxTotal(30);
    	config.setMaxIdle(2);
    	JedisPool pool = new JedisPool(config,"192.168.66.129",6379);
    	Jedis jedis = null;
    	try {
            jedis = pool.getResource();
            jedis.set("name", "zhangsan");
            String name = jedis.get("name");
            System.out.println(name);//zhangsan
            
            jedis.append("name", " hello");
            System.out.println(jedis.get("name"));//zhangsan hello
            
            jedis.set("name", "zhubajie");
            System.out.println(jedis.get("name"));//zhubajie
            
            jedis.del("name");
            System.out.println(jedis.get("name"));//null
            
            jedis.mset("name","wukong","age","18");
            System.out.println(jedis.mget("name"));//[wukong]
            System.out.println(jedis.mget("age"));//[18]
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(jedis!=null) {jedis.close();}
        }
    }
    
    @Test
    public void test2() {
    	JedisPoolConfig config = new JedisPoolConfig();
    	config.setMaxTotal(30);
    	config.setMaxIdle(2);
    	JedisPool pool = new JedisPool(config,"192.168.66.129",6379);
    	Jedis jedis = null;
    	try {
            jedis = pool.getResource();
            jedis.set("name", "zhangsan");
            String name = jedis.get("name");
            System.out.println(name);//zhangsan
            
            System.out.println(jedis.flushDB());//OK
            
            jedis.set("uname", "wukong");
            System.out.println(jedis.get("uname"));//wukong
            
            jedis.setnx("age", "18");
            System.out.println(jedis.get("age"));//18
            
            jedis.setex("username", 10, "zhangxiaosan");
            System.out.println(jedis.get("username"));//zhangxiaosan
            
            try {
            	Thread.sleep(15000);
            } catch (Exception e) {}
            System.out.println(jedis.get("username"));//null
            
            jedis.set("name", "zhangsan");
            System.out.println(jedis.getrange("name", 1, 3));//han
    	} catch (Exception e) {
            e.printStackTrace();
    	}finally {
            if(jedis!=null) {jedis.close();}
    	}
    }
}
复制代码

六、哈希类型(Hash,类似于java里的Map)

  • Hash是一个键值对集合

  • Hash是一个String类型的field和value的映射表,Hash特别适合于存储对象

  • 类似于java里面的Map<String,Object>

    Hash类型:散列类型,提供了字段和字段值的映射,字段值只能是字符串,不支持散列类型、集合类型等其他类型 hash如果存储对象,命名规则:user:1:info(key) key 字段 value

hash操作指令
第一组
hget key col              获取hash的key对应的字段的字段值
hset key col val          设置hash的key对应的字段和字段值
hmget key col1 col2       批量获取hash的key的一批字段的字段值
hmset key col1 val1 col2 val2    批量设置hash的key的一批字段和字段值    
hgetall key               获取所有的字段和字段值
hdel key col              删除hash的key对应的字段的字段值
复制代码
第二组
hlen key            获取hash的key字段的数量
hexists key col     判断hash的key是否有该字段
复制代码
第三组
hvals key   返回所有字段的字段值
hkeys key   返回所有字段
复制代码
第四组
hincrby key col n       hash的key对应的col的字段值自增n(整数)
hincrbyfloat key col f  hash的key对应的col的字段值自增f(小数) 
复制代码
第五组
hsetnx key col val      hash的key对应的字段不存在才设置
复制代码
java中使用jedis
public class HashTest {
    public static void main(String[] args) {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(30);
        config.setMaxIdle(2);
        JedisPool pool = new JedisPool(config,"192.168.66.129",6379);
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.hset("user:3:info", "username", "青子");
            String value=jedis.hget("user:3:info", "username");
            System.out.println(value);//青子
            
            Map<String,String> map = new HashMap<>();
            map.put("userid", "1001");
            map.put("username", "zhangsan");
            map.put("usersex", "man");
            jedis.hmset("user:1:info", map);
            List<String> username = jedis.hmget("user:1:info", "username");
            System.out.println(username);//[zhangsan]
            
            jedis.hdel("user:1:info", "usersex");
            System.out.println(jedis.hmget("user:1:info", "usersex"));//[null]
            
            System.out.println(jedis.hlen("user:1:info"));//3
            System.out.println(jedis.exists("user:2:info"));//true
            
            System.out.println(jedis.hkeys("user:1:info"));//[name, userid, username]
            System.out.println(jedis.hvals("user:1:info"));//[zhangsan, zhangsan, 1001]
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(jedis!=null) {jedis.close();}
        }
    }
}
复制代码

七、列表类型(list)

  • redis列表是最简单的字符串列表,按照插入顺序排序,可以添加一个元素到列表的头部(左边)或者尾部(右边),它的底层实际是个链表

    列表类型可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素或者获取列表某一个片段 有序,可以包含重复的元素

list操作指令
第一组
lpush key value         从列表的左端插入值(1-N个)
rpush key value         从列表的右端插入值(1-N个)
lrange key start stop   从左边遍历指定的key对应的value     
复制代码
第二组
lpop key    从列表左侧弹出一个item(删除)
rpop key    从列表右侧弹出一个item(删除)
复制代码
第三组
lindex key            获取列表指定索引的元素
llen key              获取列表的长度
ltrim key start end   按照索引范围修剪列表,删除所有以外的元素
复制代码
第四组
lrem key count value    根据count的值,从列表中删除与value相等的元素
    count=0,删除所有与value值相等的值
    count>0,从左到右,删除最多count个value相等的值
    count<0,从右到左,删除最多个Math.abs(count)个value相等的值
复制代码
第五组
rpoplpush key1 key2        从key1列表右侧弹出一个item插入到key2的左侧  
lset key index newValue    设置列表指定索引值为newValue
复制代码
第六组
linsert key before|after value newValue    在list指定的值(value)前后插入新的元素(newValue)
复制代码
java中使用jedis
public class ListTest {
	public static void main(String[] args) {
		JedisPoolConfig config = new JedisPoolConfig();
		config.setMaxTotal(30);
		config.setMaxIdle(2);
		JedisPool pool = new JedisPool(config,"192.168.66.129",6379);
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			jedis.del("mylist");
			
			jedis.lpush("mylist", "aa");
			jedis.lpush("mylist", "bb");
			jedis.lpush("mylist", "cc");
			jedis.lpush("mylist", "aa");
			jedis.lpush("mylist", "dd");
			jedis.lpush("mylist", "ee");
			
			//清空数据
			jedis.flushDB();
			
			jedis.rpush("mylist", "aa");
			jedis.rpush("mylist", "bb");
			jedis.rpush("mylist", "cc");
			jedis.rpush("mylist", "aa");
			jedis.rpush("mylist", "dd");
			jedis.rpush("mylist", "ee");
			
			List<String> values = jedis.lrange("mylist", 0, -1);
			System.out.println(values);//[aa, bb, cc, aa, dd, ee]
			
			System.out.println(jedis.llen("mylist"));//6
			jedis.lset("mylist", 3, "Java");
			
			System.out.println(jedis.lindex("mylist", 3));//java
			jedis.lrem("mylist", 1, "Java");
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			if(jedis!=null) {jedis.close();}
		}
	}
}
复制代码

八、集合类型(Set)

  • redis的Set是String类型的无序集合,它是通过HashTable来实现的

    在集合中每个元素都是不同的,而且没有顺序

set操作指令
第一组
sadd key value        向集合key添加元素(如果元素已经存在则添加失败)
smembers key          从集合中查询元素
sismember key el      判断元素是否在集合中,返回1或0
srem key el           将集合key中的元素移除掉
复制代码
第二组
scard key   计算集合的大小(个数)    
spop key    从集合中随机删除一个元素
复制代码
第三组
srandmember key n       从集合key中随机出n个数
复制代码
第四组
smove key1 key2 k1el    将key1里的某个值赋给key2
复制代码
第五组
sdiff key1 key2       差集(在第一个set里面而不在第二个set里面的元素)
sinter key1 key2      交集
sunion key1 key2      并集
复制代码
java中使用jedis
public class SetTest {
	public static void main(String[] args) {
		JedisPoolConfig config = new JedisPoolConfig();
		config.setMaxTotal(30);
		config.setMaxIdle(2);
		JedisPool pool = new JedisPool(config,"192.168.66.129",6379);
		Jedis jedis = null;
		
		try {
			jedis = pool.getResource();
			jedis.sadd("userset", "唐僧");
			jedis.sadd("userset", "悟空");
			jedis.sadd("userset", "悟能");
			jedis.sadd("userset", "悟净");
			
			Set<String> setValues = jedis.smembers("userset");
			System.out.println(setValues);//[唐僧, 悟空, 悟能, 悟净]
			
			System.out.println(jedis.scard("userset"));//4
			System.out.println(jedis.sismember("userset", "悟空"));//true
			System.out.println(jedis.spop("userset"));//悟空
			setValues = jedis.smembers("userset");
			System.out.println(setValues);//[唐僧, 悟能, 悟净]
			
			System.out.println(jedis.srem("userset", "悟能"));//1
			setValues = jedis.smembers("userset");
			System.out.println(setValues);//[唐僧, 悟空]
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(jedis!=null) {jedis.close();}
		}
	}
}
复制代码

九、有序集合类型(Zset,即sorted set)

  • redis Zset和Set一样也是String类型元素的集合,且不允许有重复的元素

  • 不同的是每个元素都会关联一个double类型的分数

  • redis正是通过分数来为集合中的成员进行从小到大的排序,Zset的成员是唯一的,但分数(score)却可以重复

    在集合类型的基础上有序集合类型为集合中的每一个元素都关联一个分数,这使得我们不仅可以完成插入、删除和判断元素是否存在集合中, 还能够获得分数最高或最低的前N个元素、获取指定分数范围内的元素等与分数有关的操作

    与列表类型(list)的相同点 二者都是有序的 二者都可以获得某一范围内的元素 与列表类型(list)的不同点 列表类型都通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会变慢 有序集合类型使用散列表实现,所以即使读取位于中间部分的数据也很快 列表中不能简单的调整某个元素的位置,但是有序集合可以(通过更改分数实现) 有序集合要比列表类型更耗内存

Zset操作指令
第一组
zadd key score val       添加元素:向有序集合中加入一个元素和该元素的分数,
    如果该元素已经存在则会用新的分数替换原有的分数。
    返回值是新加入到集合中的元素个数
zrange key start stop       获取排名在某个范围的元素列表  
zrevrange key start stop    获取排名在某个范围的元素逆序列表
zrange key start stop withscores    获取元素和分数  
复制代码
第二组
zrangebyscore key (min max [withscores] [limit startstep step]   获取指定分数范围内的元素('('是不包含的意思)
zrevrangebyscore key max min [withscores] [limit startstep step]   获取指定分数范围内的元素
复制代码
第三组
zrem key val        删除元素
zcard key           获取集合中元素的数量
zcount key min max  获取指定分数范围的元素个数
zrank key val       获取下标值
zscore key val      获取分数
复制代码
第四组
zrevrank key val    逆序获取下标值
zrangebyscore key min max [withscores]    获取指定分数范围内的元素
复制代码
第五组
zincrby zset score val               增加某个元素的分数,返回值是更改后的分数
zremrangebyrank key start stop       按照排名范围删除元素
zremrangebyscore key start stop      删除分数在start与stop之间的元素
复制代码
java中使用jedis
public class SortedSetTest {
    public static void main(String[] args) {
    	JedisPoolConfig config = new JedisPoolConfig();
    	config.setMaxTotal(30);
    	config.setMaxIdle(2);
    	JedisPool pool = new JedisPool(config,"192.168.66.129",6379);
    	Jedis jedis = null;
    	
    	try {
            jedis = pool.getResource();
            
            jedis.zadd("users", 1001,"张三");
            jedis.zadd("users", 9527,"华安");
            jedis.zadd("users", 2001,"八戒");
            //由小到大获取
            Set<String> setvalues = jedis.zrange("users", 0, -1);
            System.out.println(setvalues);//[张三, 八戒, 华安]
            //由大到小获取
            Set<String> setvalues2 = jedis.zrevrange("users", 0, -1);
            System.out.println(setvalues2);//[华安, 八戒, 张三]
            
            jedis.zadd("zset", 3.1,"aa");
            jedis.zadd("zset", 2.5,"bb");
            jedis.zadd("zset", 5.6,"cc");
            
            Set<String> setvalues3 = jedis.zrevrange("zset", 0, -1);
            System.out.println(setvalues3);
            
            //获取元素的个数
            System.out.println(jedis.zcard("zset"));//3
            
            //删除元素
            System.out.println(jedis.zrem("zset", "aa"));
    	} catch (Exception e) {
    	    e.printStackTrace();
    	}finally {
            if(jedis!=null) {jedis.close();}
    	}
    }
}
复制代码

十、配置文件(redis.conf)

1、Units单位
  • 配置大小单位,开头定义了一些基本的度量单位,只支持bytes,不支持bit
  • 对大小写不敏感
2、Includes包含
  • 和我们的Struts2配置文件类似,可以通过includes包含,redis.conf可以作为总闸,包含其他配置
3、General通用
  • daemonize:守护进程(yes)
  • port:端口号(6379)
  • bind 127.0.0.1:绑定IP
  • loglevel:日志级别
  • databases:数据库的数量16
4、Security安全
$ config set requirepass 111111
$ config get requirepass
$ auth 111111
复制代码
5、Menory management内存管理
  • maxmemory-policy:noeviction(永不过期)

      volatile-lru:使用LRU算法移除key,只对设置了过期时间的键
      allkeys-lru:使用LRU算法移除key
      volatile-lfu:使用LFU算法移除key,只对设置了过期时间的键
      allkeys-lfu:使用LFU算法移除key
      volatile-random:在过期集合中移除随机的key,只对设置了过期时间的键
      allkeys-random:移除随机的key
      volatile-ttl:移除那些TTL值最小的key,即那些最近要过期的key
      noeviction:不进行移除。针对写操作,只是返回错误信息
    复制代码
6、SNAPSHOTTING快照
  • RDB是整个内存压缩过的Snapshot,RDB的数据结构,可以配置复合的快照触发条件,默认设置为(save 秒钟 写操作次数):

      save 900 1
      219 save 300 10
      220 save 60 10000
      $ save  手动写入内存rdb文件中
    复制代码
  • 如果想要禁用RDB持久化的策略,只要不设置任何save指令,或者给save传入一个空字符串参数也可以

  • stop-writes-on-bgsave-error:默认是yes,如果配置成no,表示你不在乎数据不一致或者有其他的手段发现和控制

  • rdbcompression:默认是yes,对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩。如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能

  • rdbchecksum:默认是yes,在存储快照后,还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能

  • dbfilename:dump.rdb

  • dir:./

7、APPEND ONLY MODE追加
  • appendonly:默认no

  • appendfilename:appendonly.aof

  • appendfsync:默认everysec

      always:同步持久化,每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好
      everysec:出厂默认推荐,异步操作,每秒记录,如果一秒内宕机,有数据丢失
      no:从不同步
    复制代码
  • no-appendfsync-on-rewrite:默认值no,重写时是否可以运用Appendfsync,用默认no即可,保证数据安全性。

  • auto-aof-rewrite-min-size:默认值64mb,设置重写的基准值

  • auto-aof-rewrite-percentage:默认自100,设置重写的基准值

十一、redis的持久化

1、RDB(Redis DataBase)
1.1、官网介绍

在指定的时间间隔内将内存中的数据集快照写入磁盘, 也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里

1.2、是什么:

Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。

1.3、Fork

fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程

1.4、rdb 保存的是dump.rdb文件

RDB是整个内存压缩过的Snapshot,RDB的数据结构,可以配置复合的快照触发条件,默认设置为:

  • 15分钟内至少修改1次
  • 5分钟内至少修改10次
  • 1分钟内至少修改10000次
1.5、如何触发rdb快照
  • save命令:save时只管保存,其它不管,全部阻塞
  • bgsave命令:Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。可以通过lastsave命令获取最后一次成功执行快照的时间
  • 执行flushall命令,也会产生dump.rdb文件,但里面是空的,无意义
1.6、如何恢复
  • 将备份文件 (dump.rdb) 移动到redis安装目录并启动服务即可
  • CONFIG GET dir:获取dump.rdb所在目录
1.7、优势
  • 适合大规模的数据恢复
  • 对数据完整性和一致性要求不高
1.8、劣势
  • 在一定时间间隔做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改
  • fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑
1.9、如何停止
  • 动态所有停止RDB保存规则的方法:redis-cli config set save ""
2、AOF(Append Only File)
2.1、是什么

以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作

2.2、Aof保存的是appendonly.aof文件
2.3、AOF启动/修复/恢复
  • 启动:修改默认的appendonly no,改为yes
  • 恢复:将有数据的aof文件复制一份保存到对应目录(config get dir),重启redis然后重新加载
  • 修复:备份被写坏的AOF文件,执行命令(redis-check-aof --fix appendonly.aof),重启redis,然后重新加载
2.4、rewrite
  • 是什么:AOF采用文件追加方式,文件会越来越大,为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时(64MB),Redis就会对AOF文件的内容压缩,只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof
  • 重写原理:AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename),遍历新进程的内存中数据,每条记录有一条Set语句。重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似
  • 触发机制:Redis会记录上次重写时的AOF文件大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍(100)且文件大于64M时触发
2.5、优势
  • 修改同步(appendfsync always):同步持久化,每次数据发生变化会被立即记录到磁盘,性能较差但数据完整性比较好
  • 每秒同步(appendfsync everysec):异步操作,每秒记录,如果一秒内宕机,有数据丢失
  • 不同步(appendfsync no):从不同步
2.6、劣势
  • 相同数据集的数据aof文件要远大于rdb文件,恢复速度慢于rdb
  • aof运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同
3、总结
  • RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储

  • AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些 命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾. Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大

  • 只做缓存:如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.

  • 同时开启两种持久化方式

      在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,
      因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.
      RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?
      建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),而且不会有AOF可能潜在的bug,留着作为一个万一的手段。
    复制代码

redis消息订阅发布

进程间的一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

先订阅,后发布,才能收到消息,
SUBSCRIBE c1 c2 c3          订阅消息,可以一次性订阅多个
PUBLISH c2 hello-redis      发布消息

PSUBSCRIBE new*             订阅消息,订阅多个,通配符*, 
PUBLISH new1 redis2015      发布消息
复制代码

redis主从复制(Master/Slave)

1、redis主从复制是什么?

行话:也就是我们所说的主从复制,主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主

2、能干嘛

读写分离

容灾恢复

3、怎么玩
配从(库)不配主(库)
从库配置:slaveof 主库IP 主库端口
每次与master断开之后,都需要重新连接,除非你配置进redis.conf文件
info replication
复制代码
修改配置文件细节操作
拷贝多个redis.conf文件
开启daemonize yes
修改pid文件名字
修改端口号
修改log文件的名字
修改dump.rdb名字
复制代码
常用3招

一主二仆

一个Master两个Slave
日志查看(主机日志、备机日志、info replication)
1、切入点问题?slave1、slave2是从头开始复制还是从切入点开始复制?
    从头开始复制(全量复制)
2、从机是否可以写?set可否?
    不可以,从机只读
3、主机shutdown后情况如何?从机是上位还是原地待命
    原地待命
4、主机又回来了后,主机新增记录,从机还能否顺利复制?
    从机可以顺利复制
5、其中一台从机down后情况如何?依照原有它能跟上大部队吗?
    可以
复制代码

薪火相传

上一个Slave可以是下一个slave的Master,Slave同样可以接收其他slaves的连接和同步请求,那么该slave作为链条中下一个的master,可以有效减轻master的写压力(去中心化)
中途变更转向:会清除之前的数据,重新建立拷贝最新的
slaveof 新主库IP 新主库端口
复制代码

反客为主

SLAVEOF no one:使当前数据库停止与其他数据库的同步,转成主数据库
复制代码
4、复制原理
slave启动成功连接到master后会发送一个sync命令
Master接到命令后,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步
全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步
但是只要是重新连接master,一次完全同步(全量复制)将被自动执行
复制代码
5、哨兵模式(sentinel)
哨兵模式是什么
是反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库
复制代码
怎么玩
调整结构,6379带着6380、6381
自定义的/redis目录下新建sentinel.conf文件,名字绝不能错
配置哨兵,填写内容
     sentinel monitor 被监控的主机名字(自己起名字) 127.0.0.1 6379 1
     上面最后一个数字1,表示主机挂掉后salve投票看让谁接替成为主机,得票数多少后成为主机
启动哨兵
    redis-sentinel /redis/sentinel.conf 
    上述目录依照各自的实际情况配置,可能目录不同
    如果原有的master挂了,会投票重新选举出一个作为新的master,如果之前的master重启回来,会成为新选举出来的master的slave
    一组sentinel能同时监控多个Master
复制代码
6、复制的缺点

由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。

转载于:https://juejin.im/post/5be2d67ce51d453973142cbb

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值