Redis初探

redis可以很好的解决nginx反向代理时的session共享的问题。

session共享的解决方案:
Redis初探_第1张图片

NOSQL的特点

  • 不遵循SQL标准。
  • 不支持ACID。
  • 远超于SQL的性能。

NOSQL的应用场景

  • 对数据高并发的读写。
  • 海量数据的读写。
  • 对数据高可扩展性。 

key键操作

keys * : 查看当前库中所有的key。

exists key : 查看某个key是否存在。

type key : 查看key是什么类型。

del key : 删除对应的key。

unlink key : 删除对应的key。(仅将key从keyspac元数据中删除,真正的删除会在后续的异步操作中)

expire key : 设置key的过期时间。(expire key 10 表示该key会在十秒内过期,单位为S)

ttl key : 查看key 还有多久过期。(-2表示已经过期,-1表示永不过期)

select : 切换数据库。

dbsize : 查看当前数据库的key数量。

flushdb : 清空当前库。

flushall : 清空全部数据库。

String类型

get key : 查找对应的key的value值。

append key value : 在对应的key的valu后追加value值,并放回长度。

strlen key : 查询key值对应的value的长度。

setnx key value : 只有在key值不存在时才会执行set指令。

incr key : 对key的value值加1。(需要保证value为数字类型)

decr key : 对key的value值减1。(需要保证value为数字类型)

 incrby/decrby key 步长:自定义key的value值的加减步长。

mset key1 value1 key2 value2... : 同时设置多个key-value。

mget key1 key2... : 同时查询多个value值。

msetnx key1 value1 key2 value2 ... : 在key不存在时添加key-value。

getrange key  <开始索引> <停止位置> : 获取对应长度的value值。

setrange key <插入位置> value : 在key对应的value中的对应插入位置插入value值。

setex key <过期时间> value : 设置key-values时设置过气时间。

getset key value : 将旧值替换为新的key-value值。

原子性

Redis初探_第2张图片

Redis初探_第3张图片

List类型

底层就是双向链表。

它是quickList。

在数据较少时,其会创建一个连续的空间存放数据,我们将其称为zipList,当数据较大时就会创建多个zipList,然后以链表的形式将它们串起来。(其是符合双向链表的)

lpush/rpush key value1 value2 value3... : 在左边或右边插入多个value值。(符合头插法)

lrange key <开始位置> <停止位置> : 从左边的开始位置开始查询链表中的对应长度的数据。

lpop/rpop key 从左边或右边吐出一个value值。

rpoplpush key1 key2 : 从key1链表的右边吐出一个value,将此value插入key2的左边。

lindex key <索引下标> : 通过索引下标查找key链表中对应的值。(从左向右)

llen key : 获取key链表的长度。

linsert before/after key value newvalue : 在key链表中的value值的后面或前面插入新的value。

lrem key : 从key链表中删除n个值为value的value值。(从左边开始)

lset : 将key链表中对应的value进行覆盖。

set类型

  

sadd key value1 value2 value3 .... : 添加一个key-set的数据。

smembers key1 : 查询key对set中的所有值。

simember key value : 查询key对应的集合中是否存在value值,如果存在就放回1,否则返回0。

scard key : 返回key对应的集合的长度。

srem key value1 value2 .... : 删除key对应集合中的value值。

spop key : 从key对应的集合中随机吐出一个value值。

srandmember key : 从key对应的集合中随机取n个value值,且不会删除。

 smove oldkey newkey value : 将value值从oldkey对应的set转移到newkey对应的set中。

sinter key1 key2 ... : 查询key1对应set和key2对应set的交集。

sunion key1 key2... : 查询key1对应的set和key2对应的set的并集。

 sdiff key1 key2 ... : 查询key1对应的set和key2对应的set的差集。(key1中存在但key2不存在)

hash哈希类型

 可以将value值理解成C语言中的结构体。(map里套map)

 hset key field value .... : 添加一个hash表。

  hget key field ...:查询key对应的hash表中对应的value值。

  hmset key field value ... : 对key对应的hash表添加多条数据。

 hexists key field : 查询在key对应的hash表中是否存在field。

 hkeys key : 查询在key对应的hash表中所有的field。

 hvals key : 查询在key对应的hash表中所有的value。

hincrby key field increment : 对key对应的hash表的field加increment。

hsetnx key field value : 对key对应的hash表添加field-value,且在不存在时添加。

Zset有序集合

它是一个没有重复元素的有序集合。 

结构类型
跳跃表:

Redis初探_第4张图片

优化链表为跳表。

Redis初探_第5张图片

Redis初探_第6张图片

 zadd key score1 value1 ..... : 添加zset集合。

zrange key (withscore): 查询对应长度的有序结果。(有withcores时会附带score)

 

zincrby key increment value : 对key对应的集合的score加increment。

zrem key value : 删除key对应的集合的value值。

zcounnt key min max : 统计集合,在对应范围内的元素个数。

zrank key value : 查询value在集合中的排名。(从0开始) 

Bitmaps类型

Redis初探_第7张图片

 setbit key value : 在key对应的bitmaps对应的偏移量的位置时设置值为value。

getbit key : 取出key对应的bitmap对应的偏移量对应的value。

bitcount key  (开始位置) (结束位置): 统计key对应的bitmap中value为1的数量。

bitop and(or/not/xor) destkey key1 key2 ... : 将key1和key2的复合操作结果存放到destkey对应的bitmaps中。

其特点: 可以极大减少存储空间。(在存在大量数据时其特点最为突出)

 HyperLogLog类型

作用:用于解决基数问题。

pfadd key value1 value2 ... : 添加指定的元素到key对应的HyperLoglog中。(且在添加重复数据时不会进行添加,此特点可以很好的解决基数问题)

pfcount key : 查询key对应的HyperLoglog中基数的数量。

pfmerge key1 key2 ... : 将多个HLL合并,并将结果存放到destkey对应的HLL中。(可以解决对应期间浏览量的基数问题)

Geospatial类型

geoadd key <经度> <纬度> 名称 ... : 对key对应的geo添加一条数据。

geopos key 名称 :从key对应的geo中查询名称对应的经纬度。

geodist key 名称1 名称2 (单位) : 从key对应的geo中查询名称1和名称2的直线距离。

Redis初探_第8张图片

georadius key <经度> <纬度> radius (单位) : 以给定的经纬度为中心,找出某一半径内的元素。

Redis的发布和订阅

Redis初探_第9张图片

Redis初探_第10张图片

订阅指令:

SUBSCRIBE <对应的频道>

发布指令:
publish <对应的频道> <对应的内容>

例子:

Redis初探_第11张图片

Redis初探_第12张图片

 jedis


    redis.clients
    jedis
    3.3.0

Jedis对象 :Redis初探_第13张图片

该对象创建需要对应的ip地址和redis的端口号。

可通过jedis.ping()查看链接的状态。

jedis中的指令和redis中是相同的。

jedis实例-手机验证码:

Redis初探_第14张图片  

对应例子代码为下:

import redis.clients.jedis.Jedis;

import java.util.Random;

public class verifyCode {
    private static Jedis jedis = new Jedis("47.104.194.172", 6379);
    //生成验证码
    //在点击发送验证码时执行
    public static String createVerifyCode(int length, String phoneNumber){
        Random random = new Random();
        String result = "";
        for(int i = 0; i < length; i++)
            result  += random.nextInt(10);
        //验证码对应的key
        String verifyKey = "verify" + phoneNumber;
        //将验证码插入数据库
        jedis.set(verifyKey, result);
        //设置周期
        jedis.expire(verifyKey, 5);
        System.out.println(result);
        return result;
    }
    //判断输入的验证码是否正确
    public static String searchVerifyCode(String phoneNumber, String inputCode){
        String verifyKey = "verify" + phoneNumber;
        String verifyCode = jedis.get(verifyKey);
        System.out.println(verifyCode);
        if(verifyCode == null){
            return "验证次数已用光!";
        }
        if(verifyCode.equals(inputCode))
            return "成功";
        return "错误";
    }
    //判断手机号的属性
    //手机号验证
    public static void searchPhone(String phoneNumber, int length){
        String key = "usr:" + phoneNumber;
        String value = "";
        String count = jedis.get(key);
        if(count == null){
            value = "1";
            jedis.set(key, value);
        }else if(Integer.valueOf(count) < 3){
            jedis.incrBy(key, 1);
        }else{
            return ;
        }
        createVerifyCode(length, phoneNumber);
    }

    public static void main(String[] args) {
        System.out.println(jedis.ping());
    }
}

redis-springBoot

对应依赖:


 
     org.springframework.boot
     spring-boot-starter-data-redis

application配置文件中的配置:

spring:
  redis:
    #Redis 服务器的地址
    host: 11.111.111.111
    #Redis的端口
    port: 6379
    #Redis默认的数据库索引(默认为0)
    database: 0
    #连接超时时间(毫秒)
    connect-timeout: 1800000
    #连接池最大的连接数(使用负值表示没有限制)
    lettuce:
      pool:
        max-active: 20
        #最大阻塞等待时间(负数表示没有限制)
        max-wait: -1
        #连接池中最大空闲连接数
        max-idle: 5
        #连接池中最大空闲连接数
        min-idle: 0

通过自动装配RedisTemplate,完成对应的数据库操作。

事务和锁机制

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

Redis事务的主要作用是串联多个命令防止别的命令插队。

Redis初探_第15张图片

 multi :将多条语句组队。

Exec : 按顺序执行队伍中的语句。(类似mysql中的提交)

discard : 停止执行队伍中的语句。(类似mysql中的回滚)

对应例子:

组队执行

Redis初探_第16张图片

 放弃组队:

Redis初探_第17张图片

 事务错误情况处理:

 1.组队中某个命令出现了报告错误,执行时整个的所有队列都会被取消。

  Redis初探_第18张图片

2.在执行阶段某个命令报出错误,则只有报错的命令不会执行,而其他的命令都会执行,不会回滚。

Redis初探_第19张图片

 事务冲突问题:

事务冲突解决方案:

1.乐观锁

定义:每次进行操作时添加版本号,通过判断版本号来解决事务冲突问题。

优点:乐观锁适用于多读的应用类型,这样可以提高吞吐量,Redis就是利用这种check-and-set机制实现事务的。

乐观锁的使用:

watch key1 key2 ....  :在执行multi之前,先执行watch key1 key2 ...,可以监视一个或多个key,如果在事务执行之前这个或这些key被其他命令所改动,那么事务就会被打断。

unwatch : 取消watch命令对所有key的监视。

Redis初探_第20张图片

2.悲观锁 

定义 : 每次在操作时就会先进行上锁。

缺点: 效率低下,无法实现多人同时操作。

Redis事务三特性

1.单独的隔离操作

事务中的所有命令都会被序列化,按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的请求所打断。

2.没有隔离级别的概念

队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行。

3.不保证原子性

事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。

解决秒杀案例的问题

超卖问题解决 

 解决方法:使用乐观锁解决超卖问题。

通过watch监听对应的库存key和秒杀成功的用户组key,然后追加事务操作。

连接超时问题解决

 使用理解池解决:在application文件中配置连接池的连接超时的参数。

库存遗留问题

因为乐观锁的机制,在高并发时,假设有100个人同时抢,但最后只会有一个成功,其他的事务会全部取消。

使用LUA解决超卖问题

redis持久化操作

RDB(Redis DataBase) 方式

 定义:在指定的时间间隔内将内存中的数据集快照写入磁盘。(redis默认就存在RDB操作)

Redis初探_第21张图片

先将数据集存入temp文件的原因: 如果直接将redis内存存入dump .rdb中的话,当在传输过程中如果突然断电就是导致存入的数据集不完整,所以先将数据集存入temp文件就是为了保证数据的完整性和数据的一致性。

优点:

1. 适合大规模的数据恢复。

2.对数据完整性和一致性要求不高更适合使用。

3.节省磁盘空间。

4.恢复速度快。

RDB的缺点:

1.Fork的是时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑。

2.虽然redis在fork时使用了写时拷贝技术,但是如果数据庞大时还会比较消耗性能。

3.在备份周期在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改内容。

RDB备份

 所谓的备份就是将dump.rdb 复制一份,并将对其更改名字,在有使用备份时将dump.rdb删除,并将备份的文件改名为dump.rdb。

       stop-writes-on-bgsave-error : 当硬盘满的时候就关闭redis的操作。

rebcompression : 是否将持久化的文件进行压缩。

 rdbchecksum : 检查数据集是否完整。(它会有大约10%的性能消耗,如果希望获取最大的性能提升,可以关闭此功能)

Save

格式: save 秒钟 写操作次数

默认是一分钟内改了1万次, 或5分钟内改了10次,或配置复合的快照触发条件。

Save和bgsave

save: save时只管保存,其他不管,全部堵塞,手动保存,不建议。

bgsave : redis会在后台异步进行快照操作,快照同时还可以响应客户端的请求。

可以通过lastsave命令获取最后一次成执行快照的时间。

AOF(Append Only File)

Redis初探_第22张图片 AOF默认不开启。

AOF和 RDB同时开启,系统默认取AOF的数据(数据不会存在丢失)。

AOP持久化流程:

1. 客户端的请求写命令会被append追加到AOF缓冲区。

2.AOF缓冲区根据AOF持久化策略[always, everysec, no]将操作sync同步到磁盘的AOF文件中。

3.AOP文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量。

4. redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的。

AOP的优点:

1.备份机制更稳定,丢失数据概率更低。

2.可读的日志文本,通过操作AOF稳健,可以处理误操作。

AOF缺点:

1.比起RDB占用更多的磁盘空间。

2.恢复备份速度慢。

3.每次读写都同步的话,有一定的性能压力。

AOF的恢复

AOF的恢复 : 执行过程和RDB的备份过程一模一样。

AOF异常恢复: 如果遇到AOF文件损坏,通过redis-check-aof--fix 对appendonly.aof进行恢复。

AOP同步频率设置:

appendfsync always

始终同步,每次redis的写入都会立刻记录到日志,性能较差但数据完整性比较好。

appendfsync everysec

每秒同步,每秒记录日志一次,如果宕机,本秒的数据可能会丢失。

appendfsync no

redis不主动进行同步,把同步时机交给操作系统。

Rewrite压缩

通过一个指令将多个指令包含在一起。

Redis主从复制

 符合一主多从机制。

在从表中不能做写操作。 

作用:

读写分离,性能扩展。

容灾分离快速恢复。

Redis初探_第23张图片

info replication : 查看redis的信息。

通过更改redis.conf文件中的端口号,来启动多个redis。

配从不配主 

slaveof <主机的ip> <主机的端口号>

设置密码了需要在从机配置文件中修改 masterauth 选项 

常用三招

一主二仆
    当从服务器挂掉以后,需要重新加入主从关系中,在此过程中,从服务器会将主服务器中的数据全部复制到从服务器中。

    当主服务器挂掉时,从服务器还是会记住主服务器的状态和地址。(主服务器在重启的时候还会记录从服务器的数据)

薪火相传

Redis初探_第24张图片

 Redis初探_第25张图片

反客为主:

  

复制原理:

Redis初探_第26张图片

在第一次从服务器连接到主服务器时(全量复制),从服务器会主动找主服务器获取rdb文件并进行读取,而在后续会由主服务器对从服务器进行数据的同步(增量复制)。

Redis初探_第27张图片

 哨兵模式

新建sentinel.conf文件,名字绝对不能错。

在此文件中编写配置:

sentinel monitor <哨兵的名字> <对应ip> n 

n为至少有多少个哨兵同意迁移的数量。

当主机挂掉了,从机选举中产生新的主机。

且当主机再次重启时,此主机会变为从机。

Redis初探_第28张图片

Redis初探_第29张图片

在reids6中的配置文件中配置权重的参数为下:

 

 replica-priority的直越小,优先级就越高。

偏移量: 从机与主机数据的同步值。

集群

Redis初探_第30张图片

设置集群 

 开启daemonize yes

Redis初探_第31张图片

进入redis/src中,执行下面指令:
 

redis-cli --cluster-replicas 1(1代表一个主机有一个从机) <对应的ip加端口号> <对应的ip加端口号> <对应的ip加端口号> ...

 在redis中通过cluster nodes 查看集群信息。

查询集群中的值

cluster getkeysinslot : 返回count个slot槽中的键。

Jedis操作redis集群

SpringBoot自动使用的lettuce客户端操作redis,可以在配置文件直接添加两个配置就能做集群。

redis应用问题

 缓存穿透

Redis初探_第32张图片

解决方案1:

对空值进行缓存  如果查询的结果为空(不管数据是否存在),我们仍然把此空值存入redis中,设置结果的过期时间会很短,最长不会超过五分钟。

解决方案2:

设置可以访问的名单(白名单): 使用bitmaps类型定义一个可以访问的名单, 名单id作为bitmaps的偏移量,每次访问和bitmaps里面的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问。(每次访问之前都要访问bitmaps,所以效率不会很高)

解决方案3:

采用布隆过滤器:(布隆过滤器(bloom Filter))是1970年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)

解决方案4:

进行实时监测:当发现redis的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务。

缓存击穿

 缓存击穿是缓存中某些数据过期了,但是访问该数据的量特别大期,缓存击穿是该数据在数据库和缓存中都没有,导致每次请求都要缓存,数据库都查询一遍,拉跨数据库。

 某个key过期了,但是这个期间大量访问使用这个key。

Redis初探_第33张图片

 解决方案1:

预先设置热门数据:在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大这些热门数据key的时长。

 解决方案2:

事实调整:现场监控哪些热门,实时调整key的过期时长。

 解决方案3:

使用锁:

(1)就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db。

(2)先使用缓存工具的某些带成功操作的返回值(比如redis的SETNX),去set一个mutex key。

(3)当操作返回成功时,再进行load db的操作,并回设缓存,最后删除mutex key。

(4)当操作返回失败时,证明有线程再load db, 当前线程睡眠一段时间再重试整个get缓存的方法。

Redis初探_第34张图片

缺点:效率低下。

缓存雪崩

缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。

Redis初探_第35张图片

 解决方案1:

构建多级缓存框架:nginx缓存 + redis缓存 + 其他缓存(ehcache等)

 解决方案2:

使用锁或队列:用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层储存系统上,不适用高并发情况。

解决方案3:

设置过期标志更新缓存:记录缓存数据是否过期(设置提前量), 如过期回触发通知另外的线程再后台去更新实际key的缓存。

 解决方案4:

将缓存失效时间分散开:比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每个缓存的过期时间重复率就会降低,就很难引发集体失效的事件。

分布式锁

分布式锁应用的方面:单纯的Java API 并不能提供分布式锁的能力,为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题。 

 在集群中能够共享的锁。

redis命令

EX second : 设置键的过期时间为second秒。SET key value EX second 效果等同于SETEX key second value。

setnx key value :对key-value上锁。(只有当锁被释放时才可以进行操作,可以提供删除对应的key-value就等于释放)

我们可以提供对应key设置有效时间来防止死锁问题

分布式锁原子性冲突问题

在对key设置有效时间时突然断电,因为redis的原子性这时候就会出现为设置key有效时间的问题。

解决此问题方法: 上锁时候同时设置过期时间。

指令: set key value nx ex

误删问题

Redis初探_第36张图片

 redis6的新特性

acl list :  查看用户信息。

 

 acl cat : 查看添加权限指令类别。

Redis初探_第37张图片

 设置用户名,密码,ACL权限,并启用的用户。

 切换用户。

auth 用户名 password

springBoot 整合 redis

RedisTemplate的使用

对应依赖:


    org.springframework.boot
    spring-boot-starter-data-redis

 propries的配置

spring:
  redis:
    host: xx.xxx.xxx.xxx
    port: 6379
    password: xxxxxx

测试redisTemplate

@SpringBootTest
class BlogApplicationTests {
    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
        redisTemplate.boundValueOps("string1").set("how");
        Object string1 = redisTemplate.opsForValue().get("string1");
        System.out.println(string1);
    }

}

result:

Redis初探_第38张图片

Redis初探_第39张图片 

 

你可能感兴趣的:(redis,redis,缓存)