redis的完整学习

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

Redis

1.Nosql

  1. 单机mysql
  2. 缓存机制
  3. 分库分表+水平拆分+mysql集群本质上是数据库的读写
    • MyISAM:表锁效率低
    • Innodb行锁

特点

解耦

1.方便扩展

2.大数据量高性能

3.数据类型是多样型的不需要设计数据库随取随用

4.传统RDBMS和NoSQL

  • 传统的RDBMS
    • 结构化组织
    • 数据和关系存在单独的表中
    • 严格的一致性
    • 基础的事务
  • Nosql
    • 不仅仅是数据
    • 没有固定的查询语言
    • 键值对存储列存储文档存储图形数据库
    • 最终一致性
    • CAP定理和BASE
    • 高性能高可用高可扩
  1. 3V+3高
  • 海量Volume
  • 多样Variety
  • 实时Velocity
  • 高并发
  • 高可用
  • 高性能

四大分类

KV键值对

  • Redis、Tair、memacache

文档型数据库bson和json一样

  • mongoDB基于分布式文件存储的数据库用来处理大量的文档
  • mongoDB是一个介于关系型数据库和非关系型数据库之间的产品。
  • ConthDB

列存储数据库

  • HBase
  • 分布式文件系统

图关系数据库

  • 存放的是关系如朋友圈社交网络

  • Neo4j,InfoGrid

2.Redis入门

概述

Redis(Remote Dictionary Server)远程字典服务

作用

  1. 内存存储持久化内存是断电即失持久化rdb、aof
  2. 效率高可用用于高速缓存
  3. 发布订阅系统
  4. 地图信息分析
  5. 计时器、计数器

特性

  1. 多样的数据类型
  2. 持久化
  3. 集群
  4. 事务

配置

  1. https://redis.io/

3.Windows安装

github地址

在这里插入图片描述

开启redis开启服务

在这里插入图片描述

使用客户端连接redis

在这里插入图片描述

redis-benchmark性能测试

在这里插入图片描述

4.基础知识

端口号6379

127.0.0.1:6379> select 3 //切换数据库
OK
127.0.0.1:6379[3]> DBSIZE //db大小
(integer) 0
127.0.0.1:6379[3]> keys * //查询所有的db数
1) "name"
127.0.0.1:6379[3]> flushdb //清空当前数据库
OK
127.0.0.1:6379[3]> FLUSHALL //清空所有数据库
OK
127.0.0.1:6379[3]> keys *
(empty list or set)

redis是单线程的

  • redis是基于内存操作的CPU不是redis的性能瓶颈是根据机器的内存和网络带宽

单线程为什么这么快

  • redis是c语言写的官方的数据是10万+的QPS
  • 将所有的数据放在内存中的多线程CPU会上下文切换耗时低于内存系统来说没有上下文切换效率就是最高的多次读写都是在一个CPU上在内存情况下单线程就是最好的。

5.五大数据类型

Redis-key

127.0.0.1:6379> set name liuxiang //设置key
OK
127.0.0.1:6379> EXISTS name //判断是否存在
(integer) 1
127.0.0.1:6379> EXISTS name1
(integer) 0
127.0.0.1:6379> move name 1 //移除
(integer) 1
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> get age
"1"
127.0.0.1:6379> EXPIRE age 10 //设置key过期时间 单位s
(integer) 1
127.0.0.1:6379> ttl age //查看当前key的剩余时间
(integer) 7
127.0.0.1:6379> ttl age
(integer) 0
127.0.0.1:6379> get age
(nil)
127.0.0.1:6379> type name //查看当前key的类型
string
127.0.0.1:6379> type age
string

String(字符串)

127.0.0.1:6379> set key1 v1
OK
127.0.0.1:6379> get v1
(nil)
127.0.0.1:6379> get key1
"v1"
127.0.0.1:6379> EXISTS key1
(integer) 1
127.0.0.1:6379> APPEND key1 "hello" //追加字符串 当前key不存在相当于set一个key
(integer) 7
127.0.0.1:6379> get key1
"v1hello"
127.0.0.1:6379> strlen key1 //获取字符串的长度
(integer) 16
127.0.0.1:6379> APPEND key1 ",liuxiang"
(integer) 16
127.0.0.1:6379> get key1
"v1hello,liuxiang"
    
=====================================================================
127.0.0.1:6379> set views 0 //设置浏览量
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views //增加1
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> get views
"2"
127.0.0.1:6379> decr views //减少1 
(integer) 1
127.0.0.1:6379> incrby views 10 //自增 设置步长
(integer) 11
127.0.0.1:6379> decrby views 5 //自减 设置步长
(integer) 6
    ============================================================
127.0.0.1:6379> set key1 "hello liuxaing" //设置key1值
OK
127.0.0.1:6379> get key1
"hello liuxaing"
127.0.0.1:6379> getrange key1 0 3 //截取字符串【03】
"hell"
127.0.0.1:6379> getrange key1 0 -1 //获取全部的字符串
"hello liuxaing"
  ==============================================================
127.0.0.1:6379> set key1 abcdefg
OK
127.0.0.1:6379> get key1
"abcdefg"
127.0.0.1:6379> setrange key1 1 xx //替换指定位置开始的字符串
(integer) 7
127.0.0.1:6379> get key1
"axxdefg"
    ===========================================================
127.0.0.1:6379> setex key1 30 "hello" //设置key1值 30s后过期
OK
127.0.0.1:6379> ttl key1
(integer) 26
127.0.0.1:6379> get key1
"hello"
127.0.0.1:6379> setnx mykey "redis" //设置过期时间
(integer) 1
127.0.0.1:6379> keys *
1) "mykey"
127.0.0.1:6379> setnx mykey "mongdb" //不存在再设置在分布式锁常用如果mykey存在 创建失败
(integer) 0
127.0.0.1:6379> keys *
1) "mykey"
127.0.0.1:6379> get mykey
"redis"
 =================================================================
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 //同时设置多个值
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379> mget k1 k2 k3 //同时获取多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4 //原子性操作 k4创建失败
(integer) 0
127.0.0.1:6379> get k4
(nil)
 ==================================================================
//key的设计user:{id}:{filed}
127.0.0.1:6379> mset user:1:name liuxiang user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "liuxiang"
2) "2"
 ==================================================================
 //先get后set
127.0.0.1:6379> getset db redis //如果不存在值则返回nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongodb //如果存在值 获取原来的值 并设置新的值
"redis"
127.0.0.1:6379> get db
"mongodb"

List

基本的数据类型列表

127.0.0.1:6379> lpush list one #左插值
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1 #通过区间获取具体的值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1
1) "three"
2) "two"
127.0.0.1:6379> rpush list right #右插值
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> lpop list #左弹值
"three"
127.0.0.1:6379> rpop list #右弹值
"right"
127.0.0.1:6379> lrange list 0 -1 
1) "two"
2) "one"
127.0.0.1:6379> lindex list 0 #通过下表获得list中的某个值
"two"
127.0.0.1:6379> llen list #获得list的长度
(integer) 2
127.0.0.1:6379> lrem list 1 one #移除list集合中指定个数的value
(integer) 1
=====================================================================
127.0.0.1:6379> rpush list "hello"
(integer) 1
127.0.0.1:6379> rpush list "hello1"
(integer) 2
127.0.0.1:6379> rpush list "hello2"
(integer) 3
127.0.0.1:6379> ltrim list 1 2 #通过下表截取指定的长度
OK
127.0.0.1:6379> lrange list 0 -1
1) "hello1"
2) "hello2"
=====================================================================
127.0.0.1:6379> rpush list "hello"
(integer) 1
127.0.0.1:6379> rpush list "hello1"
(integer) 2
127.0.0.1:6379> rpush list "hello2"
(integer) 3
127.0.0.1:6379> rpoplpush list otherlist #移除列表的最后一个元素移动到新的列表中
"hello2"
127.0.0.1:6379> lrange list 0 -1
1) "hello"
2) "hello1"
127.0.0.1:6379> lrange otherlist 0 -1
1) "hello2"
=====================================================================
127.0.0.1:6379> lpush list 1
(integer) 1
127.0.0.1:6379> lset list 0 okk #替换当前索引位置的值若无值会报错
OK
127.0.0.1:6379> lrange list 0 0
1) "okk"
127.0.0.1:6379> lpush list "hello"
(integer) 2
127.0.0.1:6379> linsert list before "okk" other #在指定字符位置前插入值
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "hello"
2) "other"
3) "okk"

Set集合

127.0.0.1:6379> sadd myset "hello" #添加值
(integer) 1 
127.0.0.1:6379> smembers myset #查set中的所有值
1) "hello"
127.0.0.1:6379> sismember myset "hello" #判断某个值是不是在set集合中
(integer) 1
127.0.0.1:6379> srem myset "hello" #移除set中的指定值
(integer) 1
127.0.0.1:6379> scard myset #查询set集合中值的个数
(integer) 0
127.0.0.1:6379> srandmember myset #随机抽取一个值
"hello"
127.0.0.1:6379> spop myset #随机删除set集合中的一个值
"hello"
====================================================================
127.0.0.1:6379> sdiff k1 k2 #差集
1) "b"
127.0.0.1:6379> sinter k1 k2 #交集
1) "a"
2) "c"
127.0.0.1:6379> sunion k1 k2 #并集
1) "a"
2) "b"
3) "c"
4) "e"

Hash map集合

key-map形式

127.0.0.1:6379> hset myhash field liuxiang #set一个具体的key-value
(integer) 1
127.0.0.1:6379> hget myhash field
"liuxiang"
127.0.0.1:6379> hmset myhash field hello field1 world #set多个key-value
OK
127.0.0.1:6379> hmget myhash field field1 #获取多个值
1) "hello"
2) "world"
127.0.0.1:6379> hgetall myhash #获取全部的数据
1) "field"
2) "hello"
3) "field1"
4) "world"
127.0.0.1:6379> hdel myhash field1 #删除指定字段及value值
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "field"
2) "hello"
127.0.0.1:6379> hlen myhash #获取hash表的字段数量
(integer) 1
127.0.0.1:6379> hexists myhash field #判断是否存在
(integer) 1
127.0.0.1:6379> hkeys myhash #获取所有字段
1) "field"
127.0.0.1:6379> hvals myhash #获取所有值
1) "hello"
127.0.0.1:6379> hincrby myhash field1 1 #自增
(integer) 2
127.0.0.1:6379> hsetnx myhash field hello #如果存在则不能设置
(integer) 0

Zset有序集合

127.0.0.1:6379> zadd salary 2500 xiaohong #添加用户
(integer) 1
127.0.0.1:6379> zadd salary 3000 xiaoming
(integer) 1
127.0.0.1:6379> zadd salary 500 xiaocong
(integer) 1
127.0.0.1:6379> zrangebyscore salary -inf inf #显示全部用户从小到大
1) "xiaocong"
2) "xiaohong"
3) "xiaoming"
127.0.0.1:6379> zrangebyscore salary -inf inf withscores #显示用户并带成绩
1) "xiaocong"
2) "500"
3) "xiaohong"
4) "2500"
5) "xiaoming"
6) "3000"
127.0.0.1:6379> zrem salary xiaohong #移除有序集合中的key
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "xiaocong"
2) "xiaoming"
127.0.0.1:6379> zcard salary #获取有序集合中的个数
(integer) 2

6.Geospatial地理位置

基于Zset实现

#geoadd 添加地理位置 参数 纬度 经度 名称
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijin
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqi 114.05 22.52 shenzhen
(integer) 2
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou
(integer) 1
127.0.0.1:6379> geoadd china:city 108.96 34.26 xian
(integer) 1
#geopos 获取指定地点的经纬度
127.0.0.1:6379> geopos china:city beijin chongqi
1) 1) "116.39999896287918"
   2) "39.900000091670925"
2) 1) "106.49999767541885"
   2) "29.529999579006592"
#geodist 获取两地的直线距离
127.0.0.1:6379> geodist china:city hangzhou shenzhen
"1052108.2563"
# georadius 获取指定经纬度半径范围内的地点
127.0.0.1:6379> georadius china:city 110 30 1000 km
1) "chongqi"
2) "xian"
3) "shenzhen"
4) "hangzhou"
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord
1) 1) "chongqi"
   2) 1) "106.49999767541885"
      2) "29.529999579006592"
2) 1) "xian"
   2) 1) "108.96000176668167"
      2) "34.2599996441893"
#georadiusbymember 获取指定地方周围半径的城市
127.0.0.1:6379> georadiusbymember china:city shenzhen 500 km
1) "shenzhen"

7.Hyperloglog

传统的方式set保存用户的id可用统计set中的元素数量作为判断标准这个方式如果保存大量的用户id就会比较麻烦。

Redis Hyperloglog基数统计法。

127.0.0.1:6379> pfadd mykey a b c d e f g h i j k
(integer) 1
127.0.0.1:6379> pfcount mykey #统计mykey元素的基数数量
(integer) 11
127.0.0.1:6379> pfadd mykey2 i z x g h o l
(integer) 1
127.0.0.1:6379> pfmerge mykey3 mykey mykey2 #合并两组
OK
127.0.0.1:6379> pfcount mykey3 #查看并集的数量
(integer) 15

8.Bitmaps

两个状态的均可用如打卡和登录等

127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 1
(integer) 0
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 5
(integer) 0
127.0.0.1:6379> bitcount sign #统计打卡记录
(integer) 4

9.事务操作

redis单条命令保持原子性但事务不保证原子性没有隔离性

事务本质一组命令的集合

所有命令在事务中并没有直接执行只有发起执行命令的时候才会执行

redis事务

  • 开启事务multi
  • 命令入队
  • 执行事务exec
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> discrad #取消事务
OK
127.0.0.1:6379> exec #取消的事务不会被执行
1) OK
2) OK
3) "v1"
4) OK

编译型错误代码错误 运行时异常语法错误

10.redis乐观锁

127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money #监视
OK
127.0.0.1:6379> multi #事务正常结束数据期间没有发生变动这个时候正常启动事务
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
=================================================================
# 测试多线程修改值执行前另外一个线程修改了值事务exec执行失败
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec
(nil)
==================================================================
# 执行失败先解锁再监控再做事务操作
127.0.0.1:6379> unwatch
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec
1) (integer) 980
2) (integer) 40

11.Jedis

Jedis是Redis官方推荐的Java连接开发工具使用java操作redis中间件。

1.导入对应的依赖

<dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.8.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>
    </dependencies>

2.编码测试

  • 连接数据库
  • 操作
  • 断开连接

操作事务

public class TestPing {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1",6379);
        jedis.flushDB();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello","world");
        jsonObject.put("name","liuxiang");
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();
        try {
            multi.set("user1",result);
            multi.set("user2",result);
            int i = 1/0; //运行时异常
            multi.exec(); //执行事务
        } catch (Exception e) {
            multi.discard(); //放弃事务
            e.printStackTrace();
        } finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close();
        }
    }
}

12.SpringBoot整合

原来的Jedis被替换为了lettuce

jedis采用的直连多个线程操作不安全避免不安全可用使用jedis pool线程池

lettuce采用netty实例可用在多个线程中共享不存在线程不安全

1.导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.配置

#配置redis
spring.redis.host=127.0.0.1
spring.redis.port=6379

3.连接测试

@Autowired
    private RedisTemplate redisTemplate;
    @Test
     void contextLoads() {
        //opsForValue() 操作字符串 类似String
        //opsForList() 操作list集合
        //opsForSet() 操作set集合
//        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
//        connection.flushDb();
//        connection.flushAll();
        redisTemplate.opsForValue().set("mykey","liuxiang");
        System.out.println(redisTemplate.opsForValue().get("mykey"));

    }

自定义RedisTemplate模板

@Configuration
public class RedisConfig {
    //编写自己的redisTemplate

    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        //序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //key采用string的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        //hash的key采用string的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        //value采用jackson2JsonRedisSerializer
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //hash 的value采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

13.Redis配置文件详解

单位

大小写不敏感

在这里插入图片描述

包含

在这里插入图片描述

如spring中的importinclude将其他配置文件加进来

网络

bind 127.0.0.1  //绑定的ip
protected-mode yes  //保护模式
port 6379  //端口设置

通用

windows不支持

NOT SUPPORTED ON WINDOWS daemonize no //默认是no 以守护进程的方式运行
NOT SUPPORTED ON WINDOWS pidfile /var/run/redis.pid //如果以后台的方式运行需要指定一个pid文件
//日志
# 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
logfile "" //日志的输出文件名
databases 16 //数据库的数量 默认16个

快照SNAPSHOTTING

在规定的时间内执行多少次操作则会持久化到文件.rdb .aof持久化文件

redis是内存数据库如果没有持久化那么数据断电即失

save 900 1 //如果900s内如果有一个key修改就进行持久化操作
save 300 10//如果300s内如果有十个key修改就进行持久化操作
save 60 10000//如果900s内如果有一万个key修改就进行持久化操作

stop-writes-on-bgsave-error yes //持久化出错是否还需要继续工作
rdbcompression yes //是否压缩rdb文件需要消耗一些CPU资源
rdbchecksum yes //保存edb文件时候进行错误的校验
dbfilename dump.rdb //rdb文件保存的目录

主从复制REPLICATION

在主从复制一节当中

安全

设置密码

config set requirepass "123456" #设置密码
auth 123456 #登录
ping
config get requirepass #获得密码
requirepass foobared

限制 客户端

maxclients 10000 #设置连接上redis的最大客户端数量
maxmemory <bytes> #最大的内存容量
maxmemory-policy noeviction #内存到达上限的处理策略 
 1.volatile-lru 只对设置了过期时间的key进行lru
 2.allkeys-lru 删除lru算法的key
 3.volatile-random 随机删除过期的key
 4.allkeys-random 随机删除
 5.volatile-ttl 删除即将过期的
 6.noeviction 永不过期 返回错误

APPEND ONLY MODE

appendonly no #默认不开启aof模式默认使用rdb方式持久化大部分情况够用
appendfilename "appendonly.aof" #持久化文件
# appendfsync always 每次修改都会sync 消耗性能
appendfsync everysec  #每秒执行一次sync 可能会丢失数据
# appendfsync no 不执行同步操作系统自己同步数据

14.redis持久化

redis是内存数据库不保存就会断电即失

RDBRedis DateBase

Redis会单独创建(fork)一个子进程来进行持久化会先将数据写入一个临时文件中等持久化过程结束后替换上次持久化的文件。

进行大规模的数据恢复RDB比AOF更加高效因为不进行IO操作

缺点最后一次持久化的数据可能会丢失默认是RDB

rdb保存的文件是dump.rdb

127.0.0.1:6379> config get dir
1) "dir"
2) "D:\\Environment\\Redis-x64-3.2.100" #如果在这个目录下存在dump.rdb文件启动就自动恢复数据

优点

1、适合大规模的数据恢复

2、对数据的完整性要求不高

缺点

1、需要一定的时间间隔进程操作redis意外宕机了最后一次数据就没有

2、fork进程的时候会占用一定的空间

AOFAppend Only File

aof文件大于64Mfork新进程重写文件将所有命令都记录下来。

在这里插入图片描述

默认不开启

appendonly no #改为yes就开启
appendfilename "appendonly.aof" #持久化的文件名
appendfsync always #每次修改都会sync 消耗性能
appendsync everysec #每秒执行一次sync
appendsync no #不执行sync操作系统自己同步数据

如果aof文件有错误redis是启动不起来利用

redis-check-aof --fix appendonly.aof修复文件

缺点

1、相对于数据文件来说aof远远大于rdb修复速度比rdb慢

2、aof运行效率慢有IO操作

扩展

  1. 只做缓存可以不适用任何持久化
  2. 同时开启两种持久化方式优先载入AOF因为数据更完整

15.Redis订阅发布

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

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

在这里插入图片描述

在这里插入图片描述

SUBSCRIBE liuxiang #订阅一个频道
PUBLISH liuxiang "hello,man" #发布者发布消息到频道

原理

使用C实现

在这里插入图片描述

16.Redis主从复制

概念

主从复制指将一台redis服务器的数据复制到其他redis服务器(主机到从机)

主机以写为主从机以读为主

主从复制的作用

  1. 数据冗余实现了数据的热备份是持久化之外的另一种数据冗余方式
  2. 故障恢复当主节点出现问题时由从节点提供服务实现快速的故障恢复
  3. 负载均衡在主从复制的基础上配合读写分离可以由主节点提供写服务从节点提供读服务分担服务器负载。
  4. 高可用基石主从复制是哨兵集群能够实施的基础

主从复制复制配置文件修改pid、dump文件、log日志以及端口即可

基本的要求一主二从默认情况下每台redis服务器都是主节点

配置从机即可(命令)

SLAVEOF 127.0.0.1 6379

如果重启后会变成主机再次变成从机还是会获取所有信息!

在配置文件中永久配置

在这里插入图片描述

Redis最大使用内存不应该超过20G

127.0.0.1:6379> info replication #查看当前库的信息
# Replication
role:master #主机
connected_slaves:0 #从机数
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379>

复制原理

  1. slave启动成功连接到master后会发送一个sync同步命令
  2. master接到命令启动后台的存盘进程同时收集所有接收到的用于修改数据集命令在后台进程执行完毕后master将传送整个数据文件到slave并完成一次同步
  3. 全量复制slave服务接收到数据库文件后存盘并加载到内存中
  4. 增量复制master继续将新的修改命令依次传给slave完成同步

层层递进模式

中间的服务器既当从节点又当主节点当主节点崩了此时变成主节点

slaveof no one #自己成为主节点

17.哨兵模式

哨兵是一个独立的进程独立运行原理是哨兵通过发送命令等待redis服务器响应从而监控运行的多个redis实例

在这里插入图片描述

两个作用

  1. 通过发送命令让redis服务器返回监控其运行状态包括主服务器和从服务器
  2. 当哨兵监测到master宕机自动切换slaver到master通过发布订阅模式通知其他从服务器修改配置文件

多哨兵模式除了监控redis服务器哨兵之间也互相监控

在这里插入图片描述

1.配置哨兵配置文件sentinel.conf

sentinel monitor myredis 127.0.0.1 6379 1 #1代表主机宕机投票选举主机

2.启动哨兵

redis-sentinel kconfig/sentinel.conf

主机宕机临时选举变成主机如果主机恢复只能继续变回从机

缺点

1.扩容麻烦

2.实现哨兵模式的配置复杂

哨兵模式的配置

端口26379如果有哨兵集群需要配置多个端口

18.缓存穿透和雪崩

缓存穿透查不到

用户想要查询一个数据发现redis内存数据库没有。也就是缓存没有命中于是向持久层数据库查询发现也没有查询失败。当用户很多的时候缓存都没有命中于是都去持久层数据库查询带来很大压力就会出现缓存穿透。

解决

1.布隆过滤器

一种数据结构对所有可能查询的参数以hash形式存储在控制层先进行校验不符合则丢弃从而避免对底层存储系统的查询压力。

在这里插入图片描述

2.缓存空对象

当存储器不命中后即使返回的空对象也缓存起来同时设置一个过期时间之后再访问这个数据将会从缓存中获取保护后端数据源。

存在问题

  1. 如果空值被缓存无意义
  2. 对空值设置过期时间还是会存在不一致

缓存击穿量太大

一个key非常热点在不停的扛着大并发集中对一个点进行访问当缓存失效缓存过期的瞬间持续的大并发穿破缓存直接请求数据库。

解决

  1. 设置缓存不过期
  2. 加互斥锁
    • 分布式锁保证每个key同时只有一个线程去查询后端服务其他线程没有获得分布式锁的权限

缓存雪崩

指在某一个时间段缓存集中过期失效redis宕机

解决方案

  1. redis高可用多增加redis服务器
  2. 限流降级在缓存失效后通过加锁或者队列来控制读数据库写缓存的线程数量对某一个key只允许一个线程查询数据和写缓存
  3. 数据预热在正式部署之前先把可能的数据先访问一遍设置不同的过期时间让缓存失效的时间点尽量均匀。
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: redis