Redis(Remote Dictionary Server)学习笔记

常识

  • 中文官网:http://www.redis.cn/ 国际官网:https://redis.io/
  • 默认有16个数据库 默认使用第一个 切换数据库命令:select 1
  • 观看视频:https://www.bilibili.com/video/BV1S54y1R7SB

常用命令

  • 设置失效时间 单位为秒
expire key 10
  • 查看剩余有效时间 单位为秒 -1为没有设置失效时间 -2为已经失效
ttl key
  • 查询key
keys *
  • 查询key是否还存在
exists key
  • 查看key对应的值的类型
type key
  • 字符串追加内容
append key hello
  • 查询字符串长度
strlen key
  • 自增自减及设置步长
127.0.0.1:6379[3]> set v1 0
OK
127.0.0.1:6379[3]> incr v1
(integer) 1
127.0.0.1:6379[3]> decr v1
(integer) 0
127.0.0.1:6379[3]> incrby v1 10
(integer) 10
127.0.0.1:6379[3]> decrby v1 5
(integer) 5
  • 截取字符串
127.0.0.1:6379[3]> GETRANGE test 0 3
  • 从指定位置开始替换字符串
127.0.0.1:6379[3]> get test
"testhelloworld"
127.0.0.1:6379[3]> setrange test 0 xx
(integer) 14
127.0.0.1:6379[3]> get test
"xxsthelloworld"
  • 设值的同时设置过期时间 set with expire
127.0.0.1:6379[3]> setex v2 20 hello
OK
127.0.0.1:6379[3]> ttl v2
(integer) 17
  • 当不存在时才设值 set if not exists
127.0.0.1:6379[3]> setnx v3 world
(integer) 1
127.0.0.1:6379[3]> setnx v3 hello
(integer) 0
127.0.0.1:6379[3]> get v3
"world"
  • 批量设值
127.0.0.1:6379[3]> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379[3]> keys *
1) "k3"
2) "k2"
3) "k1"
  • 批量设值 当不存在时(原子性操作,一个失败全部失败)
127.0.0.1:6379[3]> msetnx k4 v4 k1 v1
(integer) 0
127.0.0.1:6379[3]> keys *
1) "k3"
2) "k2"
3) "k1"
  • 同时获取多个值
127.0.0.1:6379[3]> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
  • 获取并设值值
127.0.0.1:6379[3]> getset test hello
(nil)
127.0.0.1:6379[3]> get test
"hello"
127.0.0.1:6379[3]> getset test world
"hello"
127.0.0.1:6379[3]> get test
"world"
List
  • 添加和获取
127.0.0.1:6379[3]> lpush one
(error) ERR wrong number of arguments for 'lpush' command
127.0.0.1:6379[3]> LPUSH list one # 左侧添加
(integer) 1
127.0.0.1:6379[3]> LPUSH list two
(integer) 2
127.0.0.1:6379[3]> LPUSH list three
(integer) 3
127.0.0.1:6379[3]> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379[3]> rpush list zero #右侧添加
(integer) 4
127.0.0.1:6379[3]> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "zero"
  • 移除元素
127.0.0.1:6379[3]> lpop list
"three"
127.0.0.1:6379[3]> lrange list 0 -1
1) "two"
2) "one"
3) "zero"
127.0.0.1:6379[3]> rpop list
"zero"
127.0.0.1:6379[3]> lrange list 0 -1
1) "two"
2) "one"
  • 通过下标获取元素
127.0.0.1:6379[3]> lindex list 1
"one"
127.0.0.1:6379[3]> lindex list 0
"two"
  • 查看list长度
127.0.0.1:6379[3]> lpush list one two three
(integer) 3
127.0.0.1:6379[3]> llen list
(integer) 3
  • 移除指定的值 第二个参数是要移除的个数
127.0.0.1:6379[3]> LREM list 1 one
(integer) 1
127.0.0.1:6379[3]> lrange list 0 -1
1) "three"
2) "two"
127.0.0.1:6379[3]> lpush list three
(integer) 3
127.0.0.1:6379[3]> LREM list 1 three
(integer) 1
127.0.0.1:6379[3]> lrange list 0 -1
1) "three"
2) "two"
127.0.0.1:6379[3]> lpush list three
(integer) 3
127.0.0.1:6379[3]> lrem list 2 three
(integer) 2
127.0.0.1:6379[3]> lrange list 0 -1
1) "two"
  • 截取部分元素
127.0.0.1:6379[3]> lpush newlist l1 l2 l3 l4
(integer) 4
127.0.0.1:6379[3]> LRANGE newlist 0 -1
1) "l4"
2) "l3"
3) "l2"
4) "l1"
127.0.0.1:6379[3]> LTRIM newlist 1 3
OK
127.0.0.1:6379[3]> LRANGE newlist 0 -1
1) "l3"
2) "l2"
3) "l1"
  • 取出列表最后一个值放到另一个列表中
127.0.0.1:6379[3]> lpush list1 hello world
(integer) 2
127.0.0.1:6379[3]> lrange list1 0 -1
1) "world"
2) "hello"
127.0.0.1:6379[3]> rpoplpush list1 list2
"hello"
127.0.0.1:6379[3]> lrange list1 0 -1
1) "world"
127.0.0.1:6379[3]> lrange list2 0 -1
1) "hello"
  • 将列表中指定下标位置的值替换为另一个值 下标不存在则报错
127.0.0.1:6379[3]> LSET list1 0 hello
OK
127.0.0.1:6379[3]> LRANGE list1 0 -1
1) "hello"
127.0.0.1:6379[3]> LSET list1 1 world
(error) ERR index out of range
  • 往某个元素的前面或后面插入一个新的元素
127.0.0.1:6379[3]> rpush mylist hello world
(integer) 2
127.0.0.1:6379[3]> LINSERT mylist before hello ambition
(integer) 3
127.0.0.1:6379[3]> lrange mylist 0 -1
1) "ambition"
2) "hello"
3) "world"
127.0.0.1:6379[3]> linsert mylist after world redis
(integer) 4
127.0.0.1:6379[3]> lrange mylist 0 -1
1) "ambition"
2) "hello"
3) "world"
4) "redis"
Set
  • 存值、查看所有元素、判断元素是否存在于该set
127.0.0.1:6379[3]> sadd myset my new set
(integer) 3
127.0.0.1:6379[3]> SMEMBERS myset
1) "my"
2) "set"
3) "new"
127.0.0.1:6379[3]> SISMEMBER myset my
(integer) 1
  • 获取set集合大小
127.0.0.1:6379[3]> scard myset
(integer) 3
  • 随机获取集合中的某一个或多个元素
127.0.0.1:6379[3]> SRANDMEMBER myset
"new"
127.0.0.1:6379[3]> srandmember myset
"hello"
127.0.0.1:6379[3]> SRANDMEMBER myset 2
1) "set"
2) "hello"
  • 随机删除一个或多个元素
127.0.0.1:6379[3]> smembers myset
1) "world"
2) "hello"
3) "set"
4) "new"
127.0.0.1:6379[3]> spop myset
"set"
127.0.0.1:6379[3]> smembers myset
1) "world"
2) "hello"
3) "new"
  • 将指定元素移动到另一个set集合中
127.0.0.1:6379[3]> sadd myset2 set2
(integer) 1
127.0.0.1:6379[3]> smove myset myset2 new
(integer) 1
127.0.0.1:6379[3]> smembers myset
1) "world"
2) "hello"
127.0.0.1:6379[3]> smembers myset2
1) "set2"
2) "new"
  • 差集、交集、并集
127.0.0.1:6379[3]> sadd set1 a b c d
(integer) 4
127.0.0.1:6379[3]> sadd set2 c d e f
(integer) 4
127.0.0.1:6379[3]> SDIFF set1 set2
1) "b"
2) "a"
127.0.0.1:6379[3]> SINTER set1 set2
1) "c"
2) "d"
127.0.0.1:6379[3]> SUNION set1 set2
1) "c"
2) "b"
3) "a"
4) "f"
5) "d"
6) "e"
Hash
  • 存值、取值、批量存、批量取、获取所有
127.0.0.1:6379[3]> HSET myhash f1 ambition
(integer) 1
127.0.0.1:6379[3]> HGET myhash f1
"ambition"
127.0.0.1:6379[3]> hmset myhash f1 hellow f2 world
OK
127.0.0.1:6379[3]> hmget myhash f1 f2
1) "hellow"
2) "world"
127.0.0.1:6379[3]> HGETALL myhash
1) "f1"
2) "hellow"
3) "f2"
4) "world"
  • 删除指定的key
127.0.0.1:6379[3]> HDEL myhash f1
(integer) 1
127.0.0.1:6379[3]> HGETALL myhash
1) "f2"
2) "world"
  • 获取hash的大小
127.0.0.1:6379[3]> HLEN myhash
(integer) 1
  • 判断某个key是否存在
127.0.0.1:6379[3]> HEXISTS myhash f1
(integer) 1
127.0.0.1:6379[3]> HEXISTS myhash f3
(integer) 0
  • 获取hash的所有key和value
127.0.0.1:6379[3]> hkeys myhash
1) "f2"
2) "f1"
127.0.0.1:6379[3]> HVALS myhash
1) "world"
2) "hello"
ZSet(有序集合)
  • 存储、获取、批量存
127.0.0.1:6379[3]> ZADD myset 1 one
(integer) 1
127.0.0.1:6379[3]> zadd myset 2 two
(integer) 1
127.0.0.1:6379[3]> zadd myset 3 three
(integer) 1
127.0.0.1:6379[3]> ZRANGE myset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379[3]> zadd myset 4 three
(integer) 0
127.0.0.1:6379[3]> ZRANGE myset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379[3]> zadd myset 4 four 5 five
(integer) 2
127.0.0.1:6379[3]> ZRANGE myset 0 -1
1) "one"
2) "two"
3) "four"
4) "three"
5) "five"
  • 排序正序
127.0.0.1:6379[3]> ZRANGEBYSCORE myset -inf +inf withscores
 1) "one"
 2) "1"
 3) "two"
 4) "2"
 5) "three"
 6) "3"
 7) "four"
 8) "4"
 9) "five"
10) "5"
  • 倒叙输出
127.0.0.1:6379[3]> ZREVRANGE myset 0 -1
1) "five"
2) "four"
3) "three"
4) "two"
  • 删除指定元素
127.0.0.1:6379[3]> zrem myset one
(integer) 1
  • 获取指定区间的元素个数
127.0.0.1:6379[3]> ZCOUNT myset 1 5
(integer) 4
geospatial地理位置
  • 存入位置 经度 纬度 名字
127.0.0.1:6379[3]> geoadd china:city 116.408 39.904 beijing 113.265 23.108 guangzhou 114.109 22.544 shenzhen 120.165 30.319 hangzhou
(integer) 4
  • 获取指定城市经纬度
127.0.0.1:6379[3]> geopos china:city beijing
1) 1) "116.40800267457962"
   2) "39.903999881660361"
  • 返回两个给定位置之间的直线距离
127.0.0.1:6379[3]> GEODIST china:city guangzhou shenzhen km
"106.8712"
  • 以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素
127.0.0.1:6379[3]> georadius china:city 120.000 30.000 500 km
1) "hangzhou"
  • 以给定的元素为中心寻找指定范围内的元素
127.0.0.1:6379[3]>  GEORADIUSBYMEMBER china:city guangzhou 500 km
1) "shenzhen"
2) "guangzhou"
  • ZSet的命令都可以用,它就是一个特殊的ZSet
127.0.0.1:6379[3]> ZRANGE china:city 0 -1
1) "shenzhen"
2) "guangzhou"
3) "hangzhou"
Hyperloglog(会有0.81%的错误率)
  • 新增一个集合、获取不重复元素的个数
127.0.0.1:6379[3]> pfadd mykey a a b b c c d
(integer) 1
127.0.0.1:6379[3]> pfcount mykey
(integer) 4
  • 合并集合
127.0.0.1:6379[3]> pfadd mykey2 d e e f g h
(integer) 1
127.0.0.1:6379[3]> PFMERGE mykey mykey2
OK
127.0.0.1:6379[3]> PFCOUNT mykey
(integer) 8
Bitmaps 位存储 只有 0 和 1 两种状态
  • 存储和获取
127.0.0.1:6379[3]> SETBIT mykey 0 1
(integer) 0
127.0.0.1:6379[3]> SETBIT mykey 1 0
(integer) 0
127.0.0.1:6379[3]> GETBIT mykey 0
(integer) 1
127.0.0.1:6379[3]> getbit mykey 1
(integer) 0
  • 统计字符串被设置为1的bit数
127.0.0.1:6379[3]> BITCOUNT mykey
(integer) 1

事务

redis的单条命令是保证原子性的,但是事务是不保证原子性的
redis事务的本质:一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行的过程中命令会按顺序执行
一次性 顺序性 排他性地执行
redis事务没有隔离级别的概念,所有的命令在进入事务的时候并没有被执行,只有在事务发起执行命令的时候才执行

  • 开启事务 -> 命令入队 -> 执行
127.0.0.1:6379[3]> MULTI
OK
127.0.0.1:6379[3]> set k1 v1
QUEUED
127.0.0.1:6379[3]>  set k2 v2
QUEUED
127.0.0.1:6379[3]> get k2
QUEUED
127.0.0.1:6379[3]> set k3 v3
QUEUED
127.0.0.1:6379[3]> exec
1) OK
2) OK
3) "v2"
4) OK
  • 放弃事务
127.0.0.1:6379[3]> multi
OK
127.0.0.1:6379[3]> set k4 v4
QUEUED
127.0.0.1:6379[3]> discard
OK
事务出现错误的情况
  • 命令错误:所有命令都不执行
127.0.0.1:6379[3]> multi
OK
127.0.0.1:6379[3]> sat k1 v1
(error) ERR unknown command 'sat'
127.0.0.1:6379[3]> set k2 v2
QUEUED
127.0.0.1:6379[3]> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379[3]> get k2
(nil)
  • 运行时错误:其他命令正常执行
127.0.0.1:6379[3]> multi
OK
127.0.0.1:6379[3]> set k1 v1
QUEUED
127.0.0.1:6379[3]> incr k1
QUEUED
127.0.0.1:6379[3]> set k2 v2
QUEUED
127.0.0.1:6379[3]> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
127.0.0.1:6379[3]> get k2
"v2"
Watch(乐观锁)
  • 标记所有指定的key 被监视起来,在事务中有条件的执行。当事务还未被执行时,key对应的值被其他请求修改,此事务就会执行失败
127.0.0.1:6379[3]> set money 100
OK
127.0.0.1:6379[3]> watch money
OK
127.0.0.1:6379[3]> multi
OK
127.0.0.1:6379[3]> set money 120
QUEUED
127.0.0.1:6379[3]> exec
(nil)
127.0.0.1:6379[3]> get money
"80"
127.0.0.1:6379[3]> get money
"100"
127.0.0.1:6379[3]> set money 80
OK

简单引入Jedis

	<dependency>
	   <groupId>redis.clients</groupId>
	   <artifactId>jedis</artifactId>
	   <version>4.2.2</version>
	</dependency>
	<dependency>
	     <groupId>org.slf4j</groupId>
	     <artifactId>slf4j-log4j12</artifactId>
	     <version>1.7.36</version>
	 </dependency>
public static void main(String[] args) {
        JedisClientConfig config = DefaultJedisClientConfig.builder().database(3).password("123456").build();

        Jedis jedis = new Jedis("127.0.0.1", 6379, config);
        System.out.println(jedis.ping());
        jedis.watch("jedis");
        Transaction transaction = jedis.multi();
        try {
            transaction.set("jedis","TestPing");
            transaction.expire("jedis",10);
            transaction.set("test","test");
            System.out.println(transaction.exec());
        } catch (Exception e) {
            transaction.discard();
            e.printStackTrace();
        } finally {
            jedis.close();
        }

    }

SpringBoot整合redis

在SpringBoot2.x之后jedis就被替换为了lettuce
Jedis是直接连接Redis,非线程安全,在性能上,每个线程都去拿自己的 Jedis 实例,当连接数量增多时,资源消耗阶梯式增大,连接成本就较高了。
Lettuce的连接是基于Netty的,Netty 是一个多线程、事件驱动的 I/O 框架。连接实例可以在多个线程间共享,当多线程使用同一连接实例时,是线程安全的。

  • pom引入
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 配置
spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: 123456
    database: 3
  • 代码测试
    @Autowired
    RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
        redisTemplate.opsForValue().set("springboot","redis");
        System.out.println(redisTemplate.opsForValue().get("springboot"));
    }
  • 自定义redisTemplate
package com.ambition.springbootredis.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
//        JSON序列化
        Jackson2JsonRedisSerializer<Object> objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
        objectJackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//        字符串序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setValueSerializer(objectJackson2JsonRedisSerializer);
        template.setHashValueSerializer(objectJackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

config配置文件详解

  • units单位对大小写不敏感
  • 可以通过 include 引入其他配置文件
include .\path\to\local.conf
  • bind 可以控制谁能访问,注释掉就不控制
bind 127.0.0.1
  • 保护模式,默认是yes 当你需要不限制访问地址并且无需配置密码时可以关闭它,否则在没有配置bind 和 requirepass 的时候保护模式就会启动,此时只允许本地机器访问redis
protected-mode yes
  • 端口
port 6379
  • 以守护进程的形式运行(也就是后台运行),Windows不支持,开启后台运行需要指定pid文件
daemonize no
pidfile /var/run/redis.pid
  • 日志级别及日志文件位置
loglevel notice
logfile ""
  • 数据库数量,默认是 16个
databases 16
  • 配置保存数据到硬盘的规则 save 时间(秒) 最少修改次数
save 900 1 # 900s内 至少修改了1次 就会进行持久化
save 300 10
save 60 10000
  • 持久化失败时是否继续工作 默认为是
stop-writes-on-bgsave-error yes
  • 是否压缩rdb文件
rdbcompression yes
  • 是否校验rdb文件,默认开启,在存储快照后会使用CRC64算法校验数据,会增加大约10%的性能损耗
rdbchecksum yes
  • rdb文件名称及存储位置
dbfilename dump.rdb
dir ./
  • 设值访问密码
requirepass 123456
  • 限制
maxclients 10000 # 最大连接数
maxmemory <bytes> #内存最大占用量
maxmemory-policy noeviction # 达到最大占用量后的策略

1、volatile-lru:只对设置了过期时间的key进行LRU算法进行删除
2、allkeys-lru : 对所有key执行LRU算法进行删除
3、volatile-lfu:只对设置了过期时间的key进行LFU算法进行删除
4、allkeys-lfu:对所有key执行LFU算法进行删除
5、volatile-random:随机删除设置有过期时间的key
6、allkeys-random:随机删除
7、volatile-ttl : 删除即将过期的
8、noeviction : 永不过期,返回错误

  • aof 默认不开启,使用rdb模式进行持久化
appendonly no
appendfilename "appendonly.aof" #持久化文件名称
# appendfsync always 每次修改都同步
appendfsync everysec #每秒同步一次
# appendfsync no 不主动同步

Redis持久化

中文官网有详细解释:http://www.redis.cn/topics/persistence.html

  • rdb:能够在指定的时间间隔能对你的数据进行快照存储,数据备份时父进程会fork出一个子进程来做这件事情,父进程则不会再管备份数据的事情
  • aof:记录每次对服务器写的操作到aof文件里,当服务器重启后会重新执行这些命令来恢复数据

订阅与发布

  • 订阅
127.0.0.1:6379> SUBSCRIBE ch1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "ch1"
3) (integer) 1
1) "message"
2) "ch1"
3) "hello"
1) "message"
2) "ch1"
3) "world"
  • 发布
127.0.0.1:6379> PUBLISH ch1 hello
(integer) 1 # 返回接收到消息的客户端个数
127.0.0.1:6379> PUBLISH ch1 world
(integer) 1
  • 代码实现
    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
//        new PatternTopic("ch1") 要订阅的频道 可以是一个 也可以传list
        container.addMessageListener(listenerAdapter,new PatternTopic("ch1"));
        return container;
    }

    @Bean
    public MessageListenerAdapter listenerAdapter() {
//        接受到消息后处理消息的类 和指定的方法名 方法不存在会报错
        return new MessageListenerAdapter(new Receiver(),"getMessage");
    }
class Receiver{
    public void getMessage(String message) {
        System.out.println(message);
    }
}

Redis主从复制

中文文档:http://www.redis.cn/topics/replication.html

  • 查看复制信息,一般都是默认自己是主服务
127.0.0.1:6379> info replication
# Replication
role:master #角色
connected_slaves:0 #从服务器个数
master_failover_state:no-failover
master_replid:1e1885563bd7a6e554e08e3975dc768b88d84f8d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

  • 在一台服务器上模拟集群,复制三份redis.conf修改对应的配置,然后分别启动

  • 登录要作为从机的redis服务 配置为从机(可以直接修改配置文件,或者运行中使用命令配置)

127.0.0.1:6380> SLAVEOF 127.0.0.1 6379
OK
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_read_repl_offset:1
slave_repl_offset:1
master_link_down_since_seconds:-1
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:8d6ba40d4b1ab0bc4ab49b4c70eb968481332b1d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

  • 如果主机配置了密码则需要在从机上配置或使用命令设置主机密码
config set masterauth <password>
  • 只有主机可以写,从机只能读
127.0.0.1:6380> set k3 v3
(error) READONLY You can't write against a read only replica.

哨兵模式

中文文档:http://www.redis.cn/topics/sentinel.html

  • 启动方式,需要一个sentinel.conf配置文件 使用–sentinel命令启动哨兵模式
redis-server /path/to/sentinel.conf --sentinel
  • 断线重连的主机回来之后会自动变成从机,想要恢复成主机只能重新配置主从关系
重要的配置项
  • 配置需要监控的主机地址,指示 Sentinel 去监视一个名为 mymaster 的主服务器, 这个主服务器的 IP 地址为 127.0.0.1 , 端口号为 6379 , 而将这个主服务器判断为失效至少需要 2 个 Sentinel 同意 (只要同意 Sentinel 的数量不达标,自动故障迁移就不会执行)。
sentinel monitor mymaster 127.0.0.1 6379 2
  • 认为服务器已经断线所需的毫秒数
sentinel down-after-milliseconds mymaster 60000
  • parallel-syncs 选项指定了在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步, 这个数字越小, 完成故障转移所需的时间就越长
sentinel parallel-syncs mymaster 1
  • 配置连接主机所需的密码
sentinel auth-pass mymaster 123456

缓存穿透和雪崩

  • 缓存穿透

一般的程序设计是请求访问进来先查找redis,redis里找不到就会去数据库中查找。假如某一个时间点突然涌入大量请求,这个时候程序会反复访问redis,即使redis命中率低,程序发现redis里找不到,又会疯狂访问数据库,数据库如果扛不住如此大量的请求就会崩溃,这种情况称之为缓存穿透
总结:请求量大 redis命中率低 导致一直查询数据库
解决方案:设置空值缓存;布隆过滤器

  • 缓存击穿:当redis的key失效时突然进入大量请求要访问这个key,导致redis崩溃
  • 雪崩:当redis的key失效时突然进入大量请求要访问这个key,导致redis崩溃,此时请求又大量涌向数据库导致数据库也崩溃了,最后整个服务都崩溃了,也就是redis崩溃导致的连锁反应

搭建分片集群Demo

  1. 开启6个实例,目的是搭建一个三主三从的集群,需要注意的是这里的主从关系是集群创建后自动建立的不需要我们手动去写配置,需要特别开启的配置如下
  • 开启集群模式
cluster-enabled yes
  • 保存节点配置文件的路径,不需要手动新增,会自动生成
cluster-config-file nodes.conf
  • 节点连接超时时间
cluster-node-timeout 5000
  • 开启aof
appendonly yes
  1. 最新版本的集群功能已经集成到redis-cli,而不再是中文官网上写的 redis-trib.rb ,-replicas 1表示我们希望为集群中的每个主节点创建一个从节点
./bin/redis-cli --cluster create 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:7380 127.0.0.1:7381 127.0.0.1:7382 --cluster-replicas 1 -a <你的密码>
  1. 以上命令执行成功后会输出一份主从关系的配置表,觉得没问题就输入 yes
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:7381 to 127.0.0.1:6380
Adding replica 127.0.0.1:7382 to 127.0.0.1:6381
Adding replica 127.0.0.1:7380 to 127.0.0.1:6382
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 3d59014259eec6aa4e3aae1cd1e7f9b96f2b1e0f 127.0.0.1:6380
   slots:[0-5460] (5461 slots) master
M: 620085dd92df7ff53613de6e69c259cc752fb0ad 127.0.0.1:6381
   slots:[5461-10922] (5462 slots) master
M: c5d9a1c6595a45ac9baf33cbfcd3ba8734226b31 127.0.0.1:6382
   slots:[10923-16383] (5461 slots) master
S: 645b90ceb1e2d8c355d963742c89c16cf4125cbd 127.0.0.1:7380
   replicates c5d9a1c6595a45ac9baf33cbfcd3ba8734226b31
S: beac0bdf6f7716b7c9756c215d47be34b5010823 127.0.0.1:7381
   replicates 3d59014259eec6aa4e3aae1cd1e7f9b96f2b1e0f
S: 647a50696c329da490b6714206dbff4639a03eda 127.0.0.1:7382
   replicates 620085dd92df7ff53613de6e69c259cc752fb0ad
Can I set the above configuration? (type 'yes' to accept): yes

  1. 最后连接随意一个实例测试一下
[root@VM-8-2-centos redis-6.2.6]# ./bin/redis-cli -c  -p 6380 -a <你的密码>
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6380> set k1 v1
-> Redirected to slot [12706] located at 127.0.0.1:6382
OK

Redis和数据库保证数据一致性?

1.先操作缓存
线程一:修改数据线程
线程二:读取数据线程
①线程一发起请求->②删除缓存->③更新数据库->④延迟删除缓存
线程二发起请求时可能线程一还在③数据还未更新完,此时读取到的仍然会是旧数据,会导致一次数据不一致,线程一第二次延迟删除的原因是,如果更新完数据立马删除,此时线程二读取数据库旧数据再次放进缓存,redis中的数据就会一直是错误数据,延迟删除确保读操作执行完再删除旧值。
此操作只确保数据最终一致性,如果想要保证强一致性就得加锁保证数据操作的原子性,但是加锁会导致性能降低,就违背了一开始使用redis提升性能的初衷。
2.先操作数据
①线程一发起请求->②更新数据库->③删除缓存->④删除失败重试机制(mq等)
线程二发起请求,在线程一删除缓存之前读的都是旧值
3.使用第三方工具Canal监听mysql 的binlog,根据binlog同步修改redis缓存,

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值