Redis

NoSql入门和概述

入门概述

1.互联网时代背景下大机遇, 为什么用nosql

单机MySQL的美好年代

在这里插入图片描述

Memcached(缓存)+MYSQL+垂直拆分

在这里插入图片描述

MySQL主从读写分离

写的操作放在主库, 读的操作放在从库
在这里插入图片描述

分表分库+水平拆分+mysql集群

在这里插入图片描述
在这里插入图片描述

MySQL的扩展性瓶颈

在这里插入图片描述

今天是什么样子

在这里插入图片描述
在这里插入图片描述

为什么用NoSQL

在这里插入图片描述

2.是什么

在这里插入图片描述

3.能干嘛

易扩展

在这里插入图片描述

大数据量高性能

redis: 一秒读11万写8万
在这里插入图片描述

多样灵活的数据模型

在这里插入图片描述

传统RDBMS VS NOSQL

在这里插入图片描述
在这里插入图片描述

4.去哪下

5.怎么玩

KV: 键值对
Cache: 缓存
Persistemce: 持久化

3V + 3高

大数据时代的3V

海量Volume
多样Variety
实时Velocity

互联网需求的3高

高可扩(横向扩展, 加服务器进行扩展)
高并发
高性能(高可用,避免单点故障等)

当下的NoSQL经典应用

当下的应用是sql和nosql一起使用

NoSQL数据模型简介

以一个电商客户, 订单, 订购, 地址模型来对比下关系型数据库和非关系型数据库

关系型ER图

在这里插入图片描述
在这里插入图片描述

NOSQL(MongoDB)的BSON模型

在这里插入图片描述

聚合模型

  1. KV键值对

  2. Bson(mongodb中类似于json的串)

  3. 列族
    在这里插入图片描述
    在这里插入图片描述

  4. 图形
    在这里插入图片描述

NoSQL数据库的四大分类

KV键值对: 典型介绍

  1. 新浪: BerkeleyDB + redis
  2. 美团: redis + tair
  3. 阿里, 百度: memcache + redis

文档型数据库(bson格式比较多):典型介绍

CouchDB
MongoDB
在这里插入图片描述

列存储数据库

Cassandra, HBase
分布式文件系统

图关系数据库

他不是放图形的, 放的是关系, 比如: 朋友圈社交网络, 广告推荐系统
社交网络, 推荐系统等. 专注于构建关系图谱
Neo4J,InfoGrid

四者对比

在这里插入图片描述
在这里插入图片描述

分布式数据库中CAP原理CAP+BASE

传统的ACID分别是什么

A(Atomicity)原子性
C(Consistency)一致性
I(Isolation)独立性
D(Durability)持久性
在这里插入图片描述
在这里插入图片描述

CAP

C: Consistency(强一致性)
A: Availability(可用性)
P: Partition tolerance(分区容错性)

CAP的3进2

  CAP理论就是说在分布式存储系统中, 最多只能实现上面的两点,

  而由于当前的网络硬件肯定会出现延迟丢包等问题, 所以分区容忍性是我们必须要实现的

  所以我们只能在一致性和可用性之间进行权衡, 没有NoSQL系统能同时保证这三点

C: 强一致性 A: 高可用性 P:分布式容忍性

CA: 传统Oracle数据库
AP: 大多数网站架构的选择
CP: Redis MongoDB
注意: 分布式架构的时候必须做出取舍


一致性和可用性的抉择

对于web2.0网站来说, 关系数据库的很多主要特征却往往无用武之地

数据库事务一致性需求
  很多web实时系统不要求严格的数据库事务, 对读一致性的要求很低, 有些场合对写一致性要求并不高, 允许实现最终一致性
数据库的写实时性和读实时性需求
  对关系数据库来说, 插入一条数据之后立刻查询, 是肯定可以读出来这条数据的, 但是对于很多web应用来说, 并不要求这么高的实时性, 比如说发一条消息之后, 过几秒乃至十几秒后, 我的订阅者才看到这条动态是完全可以接受的

对复杂的SQL查询, 特别是多表关联查询的需求
  任何大数据量的web系统, 都非常忌讳多个大表的关联查询, 以及复杂的数据分析类型的报表查询, 特别是SNS类型的网站, 从需求以及产品设计角度, 就避免了这种情况的发生. 往往更多的只是单表的主键查询, 以及单表的简单条件分页查询, SQL的功能被极大的弱化了

经典CAP图

CAP理论的核心是: 一个分布式系统不可能同事很好的满足一致性, 可用性和分区容错性这三个需求, 最多只能同时较好的满足两个.

因此, 根据CAP原理将NoSQL数据库分成了满足CA原则, 满足CP原则和满足AP原则三类
CA: 单点集群, 满足一致性, 可用性的系统, 通常在可扩展性上不太强大
CP: 满足一致性, 分区容忍性的系统, 通常性能不是特别高
AP: 满足可用性, 分区容忍性的系统, 通常可能对一致性要求低一些
在这里插入图片描述

Base是什么

在这里插入图片描述

分布式+集群简介

在这里插入图片描述

Redis入门介绍

入门概述

1.是什么

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.能干嘛

在这里插入图片描述

3.去哪儿下

在这里插入图片描述

4.怎么玩

在这里插入图片描述

Redis是单线程的, 为什么单线程还这么快

在这里插入图片描述
在这里插入图片描述

Redis的安装

Windows版安装

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

重要提示

在这里插入图片描述

Linux版安装

redis安装参考这篇博客
https://www.cnblogs.com/heqiuyong/p/10463334.html
需要在linux环境中安装好gccyum install gcc-c++在这里插入图片描述
make命令时故障解析
在这里插入图片描述
在这里插入图片描述
再cd /opt/redis5.0.5下把redis.conf拷贝到/myredis/目录下(需要先建立一下)
再vim编辑这个复制的文件
把daemonize设置成yes 表示以后台进程方式运行
再cd /usr/local/bin(redis的默认安装目录)
启动redis-server redis-server /myredis/redis.conf指定配置文件启动redisserver
再启动redis客户端连接redisserverredis-cli -p 6379
在这里插入图片描述
在这里插入图片描述
在redis-cli中终止redis-server服务SHUTDOWN
退出redis-cli exit

Redis启动后杂项基础知识讲解

在这里插入图片描述
对单进程的说明
在这里插入图片描述

Redis数据类型

Redis的五大数据类型

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

那里去获得redis常见数据类型操作命令

http://redisdoc.com

Redis键(key)

在这里插入图片描述
在这里插入图片描述

案例

  1. keys * 查询所有key
  2. exists key的名字 判断某个key是否存在
  3. move key db 剪切, 当前库就没有了, 被移动到另外的库了
  4. expire key 秒数 为给定的key设置过期时间
  5. ttl key 查看还有多少秒过期, -1表示永不过期, -2表示已过期
  6. type key 查看你的key是什么类型
  7. del key 删除key
  8. randomkey随机返回key空间中的一个key

设置过期时间常用于保存cookie session以及单点登录

Redis字符串(String) 单值单value

常用

在这里插入图片描述
在这里插入图片描述

案例

  1. set/get/append/strlen
    set k1 v1设置值
    get k1获取值
    append k1 12345追加,如果k1不存在, 相当于新建 返回的是追加以后的字符串长度
    strlen k1获取k1对应的值的长度

  2. Incr/decr/incrby/decrby 一定要是数字才能进行加减,
    incr k2k2加1
    decr k2 k2减1
    incrby k2 3k2加3
    decrby k2 2 k2减2

  3. getrange/setrange 截取/替换字符串 getrange key start end注意 start和end都是闭区间的
    在这里插入图片描述
    在这里插入图片描述

  4. setex(set with expire) 键秒值/setnx(set if not exist) 常用于分布式锁
    setex k4 10 v4设置k4=v4 并设置10秒过期 setnx k1 v11如果k1不存在则设置成v11 如果存在, 不会覆盖其值

  5. mset/mget/msetnx
    mset k1 v1 k2 v2 k3 v3一次性设置多个键值对 mget k1 k2 k3一次性取多个值
    msetnx k1 v1 k2 v2 k11 v11一次性设置多个值, 不存在才设置, 但是只要有一个存在, 则全部不生效了,保证原子性操作, 要么一起成功, 要么一起失败
    在这里插入图片描述
    在这里插入图片描述

  6. getset(先get再set)
    在这里插入图片描述

常见使用场景

在这里插入图片描述

Redis列表(List)单值多value

常用

在这里插入图片描述
在这里插入图片描述

案例, 在redis里面, 可以把list玩成栈, 队列, 阻塞队列

  1. lpush/rpush/lrange
    lpush list01 1 2 3 4 5从左边依次给list01入栈5个value 现在集合从做到右的顺序是5 4 3 2 1
    lrange list01 0 -1从左边获取list01全部的value 显示的数据是
    rpush list02 1 2 3 4 5 从右边压栈12345 现在list02从左到右的顺序是 12345 再lrange 0 -1 一下出现的是12345
    在这里插入图片描述
  2. lpop/rpop
    lpop list01去掉左数第一个value
    rpop list02去掉右数第一个
  3. lindex 按照索引下标获得元素(从上到下即从左到右)
  4. llen
    llen list01获取长度
  5. lrem key 删N个value
    lrem list03 2 3从左边删除list03 2个value=3的数据
  6. ltrim key 开始index 结束index截取指定范围的值后再赋值给key,闭区间
    ltrim list01 0 4 截取list01的0到4一共5个value 重新赋值给list01
  7. rpoplpush 源列表 目的列表
    rpoplpush list01 list02 从list01的最右边出栈, 并入栈到list02的最左边
  8. lset key index value
    lset list01 1 5向list01中左数第二个数据重新赋值为5, lset一个不存在的list会报错
  9. linsert key before/after 值1 值2
    linsert list01 before 5 java向list01的数据5之前插入java字符串 after即为向后插
  10. exists list的key判断该list是否存在

性能总结

在这里插入图片描述

小结与常见使用场景

在这里插入图片描述

Redis集合(Set) 单值多value,set中的值不能重复

常用

在这里插入图片描述
在这里插入图片描述

案例

  1. sadd/smembers/sismember
    sadd set01 1 1 2 2 3 3set里面不能有重复数据, 所以sadd重复数据只会添加一次
    smembers set01查看set01的全部内容
    sismember set01 1查询1是否在set01集合中 返回1证明存在, 0代表不存在
  2. scard 获取集合里面的元素个数
    scard set01获取set01元素个数
  3. srem key value 删除集合中元素
    srem set01 3 删除集合中的元素3
  4. srandmember key 某个整数(随机出几个集合中的value)
    srandmember set01 3让set01中随机出3个元素并返回
  5. spop key 随机出栈
    spop set01 从set01中随机出栈一个元素, 元素在set中删除
  6. smove key1 key2 在key1里某个值 作用是将key1里的某个值赋给key2
    smove set01 set02 5把set01中的5移动到set02中

数学集合类

差集 sdiff
交集 sinter 可以实现查看共同好友
并集 sunion(兼顾联合去重功能)
在这里插入图片描述

常用场景

在这里插入图片描述

Redis哈希(Hash)KV模式不变, 但V是一个键值对

常用

在这里插入图片描述
在这里插入图片描述

案例

  1. hset/hget/hmset/hmget/hgetall/hdel
    hset user id 11设置key为user 值为一个键值对 键值对的key为id 值为11
    hget user id 获取user的id的值
    hmset customer id 11 name li4 age 26 把value的kv一起set进去
    hmget customer id name age一口气读取customer的多个kv的v
    hgetall customer 读取customer的全部kv
    hdel user name把user下的name对应的kv删除
  2. hlen
    hlen user 获取user的kv的个数
  3. hexists key 在key里面的某个值的key
    hexists customer id 判断该key对应的kv中的key是否存在, 1代表存在, 0代表不存在
  4. hkeys/hvals
    hkeys customer 输出customer的所有kv的key
    hvals customer输出customer的所有kv的value
  5. hincrby/hincrbyfloat
    hincrby customer age 2给customer的age的value加2 如果想要减的话, 要加上一个负数,因为没有hdecrby这个命令
    hincybyfloat customer score 0.5 对小数可以加上一个小数
  6. hsetnx
    hsetnx customer age 26 如果customer下的age不存在就创建, 存在的话就不做任何操作

常见使用场景

在这里插入图片描述

Redis有序集合Zset(sorted set)

在这里插入图片描述

常用

在这里插入图片描述
在这里插入图片描述

案例

  1. zadd/zrange
    zadd zset01 60 v1 70 v2 80 v3 90 v4 100 v5新建zset key 分数 值 分数 值…
    zrange zset 0 -1查询全部的值
    zrange zset01 0 -1 withscores查询全部的值带score
  2. zrangebyscore key 开始score 结束score
    zrangebyscore zset01 60 90查询zset01 60到90分之间(包括60和90)的值
    zrangebyscore zset01 -inf +inf inf代表无穷, 这个命令是zset01中的数据从小到大排序
    zrevrangebyscore zset01 +inf -inf从大到小排序
    zrangebyscore zset01 60 (90包括60但不包括90查找zset01的值
    zrangebyscore zset01 60 90 limit 2 2如果查找出来的数据太多, 可以再次进行截取 从查询出的所有数据中的下标为2(即第三个)开始, 截取两个 即截取了下标为2和3的两个数据
  3. zrem key 某score下对应的value值 作用是删除元素
    zrem zset01 v5 删除zset01中的v5
  4. zcard/zcount key score区间/zrank key values值 作用是获得下标值/zscore key 对应分
    zcard zset01获取个数,注意分数和value是一体的
    zcount zset01 60 80 统计60分到80分的个数
    zrank zset01 v4 获取该元素的下标
    zscore zset01 v4 获得v4元素对应的分数
  5. zrevrank key values值 作用是逆序获得下标值
    zrevrank zset01 v4v4对应的下标从左向右数应该是3 但是此命令是返回逆序的下标, 所以返回0
  6. zrevrange
    zrevrange zset01 0 -1倒序输出所有值
  7. zrevangebyscore key 结束score 开始score
    zrevrangebyscore zset01 90 60 获取小于等于90 大于等于60的值做倒序输出

常用场景

在这里插入图片描述

三种特殊数据类型

geospatial 地理位置

朋友的定位, 附近的人, 打车距离计算?
Redis的Geo在Redis3.2版本推出! 这个功能可以推算地理位置的信息, 两地之间的距离, 方圆几里的人!
可以查询一些测试数据:查询城市经纬度
只有6个命令

  1. GEOADD
  2. GEODIST
  3. GEOHASH
  4. GEOPOS
  5. GEORADIUS
  6. GEORADIUSBYMEMBER

geoadd

# geoadd key 经度 纬度 城市名
# 规则: 两极(南北极)无法直接添加, 我们一般会下载城市数据, 直接通过java程序一次性导入
# 参数 key 值(经度, 纬度, 名称)
> geoadd china:city 116.40 39.90 beijing
> geoadd china:city 121.47 31.23 shanghai
# 可以一次性添加多个
# geoadd china:city 121.47 31.23 shanghai 116.40 39.90 beijing

在这里插入图片描述

geopos 获得当前定位: 一定是一个坐标值

# 获取指定城市的经度和纬度
GEOPOS china:city beijing 获取北京的经度纬度
GEOPOS china:city beijing shanghai 获取多个

GEODIST 获取两个城市之间的距离

在这里插入图片描述

> GEODIST china:city beijing shanghai # 查看北京到上海的直线距离

# 获取单位为km的距离
> GEODIST china:city beijing shanghai km 

georadius 以给定的经纬度为中心, 找出某一半径内的元素
我附近的人? (获取所有附近的人的地址, 定位!)通过半径来查询范围

# georadius key 经度 纬度 半径 单位 以给定的经度和纬度为中心, 给的半径找到key中存的元素
GEORADIUS china:city 110 30 1000 km
# 找到元素并显示到中心点的直线距离
GEORADIUS china:city 110 30 1000 km withdist 
# 找到元素并显示经纬度
GEORADIUS china:city 110 30 1000 km withcoord 
# 设置查询的个数
GEORADIUS china:city 110 30 500 km withdist withcoord  count 1

在这里插入图片描述

GEORADIUSBYMEMBER 根据给定的元素确定中心点, 再根据半径进行查询, 如果两个字符串越接近, 那么距离越近

GEORADIUSBYMEMBER china:city beijing 1000 km

GEOHASH 该命令将返回11个字符的GeoHash字符串,把经纬度通过算法转换成字符串

在这里插入图片描述

Geo底层的实现原理其实就是Zset! 我们可以使用Zset命令来操作geo

在这里插入图片描述

Hyperloglog

什么是基数? 集合中不重复元素的个数

A{1,3,5,7,8,7} B{1,3,5,7,8}
A集合中有两个7 所以基数是5 B集合的基数也是5

Redis2.8.9版本中就更新了Hyperloglog数据结构
RedisHyperloglog基数统计的算法!

优点: 占用的内存是固定的, 2^64不同的元素的基数, 只需要费12kb内存! 如果要从内存角度来比较的话, Hyperloglog首选 0.81%错误率! 统计UV任务是可以忽略不计的

网页的UV(一个人访问一个网站多次, 但是还是算作一个人的访问量)
传统的方式, set保存用户的id, 然后就可以统计set中的元素数量作为标准判断!
这个方式如果保存大量的用户id , 就会比较麻烦, 我们的目的是为了计数, 而不是保存用户id

在这里插入图片描述

Bitmap

位存储 0101

统计用户信息, 活跃, 不活跃! 登录, 未登录! 打卡,365打卡! 这些两种状态的, 都可以使用Bitmap位图数据结构
都是操作二进制位来进行记录, 就只有个0和1两个状态!
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解析配置文件redis.conf

它在哪儿

默认在/opt/redisxxx下面 备份一份, 然后拷贝到别的地方

Units单位

在这里插入图片描述

INCLUDES包含

在这里插入图片描述

NETWORK 网络

bind 127.0.0.1 默认只能通过本机进行连接, 可以设置为bind *或者直接注释掉 可以通过外部进行访问
protected-mode yes 开启保护模式, 一般会配置为yes 但是我配置成yes后使用Jedis进行访问会报错, 所以这里我写成no
port 6379 访问的端口号

GENERAL通用

  1. daemonize 出厂时为no 表示redisserver是否以后台方式启动, 设置为true时表示后台启动

  2. pidfile 进程pid文件路径, 如果以后台方式运行, 就需要指定一个pid文件

  3. port 端口号

  4. tcp-backlog 默认511
    在这里插入图片描述

  5. timeout 默认0 客户端空闲多少秒后会被关闭 0代表不会关闭

  6. bind 绑定ip 默认为本机 搭建集群时会用到

  7. tcp-keeplive 每隔多少秒检测一下连接是否断开 在这里插入图片描述

  8. loglevel 日志级别 默认notice redis有4个日志级别 debug> verbose>notice>warning

  9. logfile 默认为空串 日志文件位置, 可以设置为"/opt/redis/logs/6379.log", 也可以不设置走默认配置

  10. syslog-enabled 系统日志是否启用 默认为关闭

  11. syslog-ident 系统日志以什么开头 默认为redis

  12. syslog-facility
    在这里插入图片描述

  13. databases redis存在的数据库 默认16个

SNAPSHOTTING快照(执行rdb持久化操作会用到, 执行了多少次操作会触发持久化配置)

Save

save 秒数 写操作次数 配置rdb触发方式, 多少秒内写了多少个key 才触发rdb持久化
在这里插入图片描述

save的禁用

在这里插入图片描述
如果有一个数据特别重要, 想要告诉redisserver立即备份, 不要等着触发条件了, 直接使用save指令
在这里插入图片描述

Stop-writes-on-bgsave-error 持久化如果出错, 是否要继续工作

写入某一条数据到rdb文件失败后, 是否继续, 默认是yes表示继续
在这里插入图片描述

rdbcompression 是否压缩rdb文件, 需要消耗一些cpu资源

在这里插入图片描述

rdbchecksum 保存rdb时是否进行错误校验

在这里插入图片描述

dbfilename

生成rdb文件的名字, 默认为dump.rdb

dir

生成rdb文件的位置 默认为./ 默认为redis安装位置

REPLICATION复制

SECURITY安全

可以在配置文件中指定访问密码, 不需要通过命令指定
在这里插入图片描述

访问密码的查看, 设置和取消
通过redis-cli连接上redisserver后 使用
config get requirepass获取访问密码
config get dir查看当前启动redis-server的路径, 很大可能为配置文件路径, 因为修改完配置文件就要启动redis
config set requirepass "123456" 设置redis连接密码
这时要输入密码
auth 123456再输入其他命令
在这里插入图片描述

CLIENTS

maxclients 10000 最大客户端连接数
Maxmemory最大内存使用
Maxmemory-policy 缓存过期策略 默认noeviction 永不过期
LRU算法, 最近最少使用的key
在这里插入图片描述

LIMITS限制

Maxclients

最大客户端连接数默认10000

Maxmemory

最大内存使用

Maxmemory-policy

缓存过期策略
默认noeviction 永不过期
在这里插入图片描述

Maxmemory-samples

在这里插入图片描述
默认选取5个样本

APPEND ONLY MODE 追加

appendonly 开启aof持久化

appendfilename 保存aof的文件名

Appendfsync

在这里插入图片描述
默认是everysec

No-appendfsync-on-rewrite

重写时是否可以运用Appendfsync, 用默认no即可, 保证数据安全性

Auto-aof-rewrite-min-size设置重写的基准值

Auto-aof-rewrite-percentage 设置重写的基准值

常见配置redis.conf介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Redis的持久化

RDB(Redis DataBase)

是什么

在这里插入图片描述
恢复时是从dump.rdb文件直接读取数据, 大规模数据恢复时, 对数据精度要求不高的时候, 建议使用

Fork

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

Rdb保存的是dump.rdb文件

最好把备份的rdb文件复制到备用linux服务器上. 以免主服务器宕机

配置位置

在这里插入图片描述
详细设置见上边

如何触发RDB快照

  1. 配置文件中默认的快照配置

  2. 命令save或者是bgsave
    在这里插入图片描述

  3. 执行flushall命令,也会产生dumo.rdb文件, 但里面是空的, 无意义

  4. 在redis-cli执行SHUTDOWN操作关闭RedisServer时, 会立即触发生成RDB文件,并覆盖之前的
    同样kill 和 kill -9 强制关闭redis-server时也会触发

如何恢复

将备份文件(dump.rdb)移动到redis安装目录并启动服务即可
可以使用redis-cli客户端输入config get dir获取目录

优势

适合大规模的数据恢复
对数据完整性和一致性要求不高

劣势

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

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

如何停止RDB备份

动态所有停止RDB保存规则的方法: redis-cli 中config set save ""

小总结

在这里插入图片描述

AOF(Append Only File)

是什么

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

AOF保存的是appendonly.aof文件

配置位置

见上面配置文件,首先需要把appendonly设置成yes(现在默认是no) 来开启aof持久化

AOF启动/修复/恢复

启动, 设置配置文件appendonly 改成yes
正常恢复: 将有数据的aof文件复制一份保存到对应目录下, 查看目录用config get dir
恢复: 重启redis后自动重新加载aof文件

异常恢复:
启动配置文件: 设置appendonly为yes
修复: 执行redis-check-aof --fix appendonly.aof
恢复: 重启redis然后自动重新加载

Rewrite

是什么

在这里插入图片描述

重写原理

在这里插入图片描述

触发机制

在这里插入图片描述

优势

每修改同步: appendfsync always 同步持久化 每次发生数据变更会被立即记录到磁盘, 性能较差但数据完整性比较高

每秒同步: appendfsync everysec 异步操作, 每秒记录 如果一秒内宕机, 有数据丢失

不同步: appendfsync no 从不同步

劣势

相同数据集的数据而言aof文件要远大于rdb文件, 恢复速度慢于rdb

aof运行效率要慢于rdb, 每秒同步策略效率较好, 不同步效率和rdb相同

小总结

在这里插入图片描述

总结(Which One)

在这里插入图片描述
在这里插入图片描述

Redis的事务

是什么

可以一次执行多个命令, 本质是一组命令的队列, 一个事务中的所有命令都会序列化(串行化), 按顺序地串行化执行而不会被其他命令插入, 不许加塞, Redis单条命令保存是原子性的, 但是事务不保证原子性且Redis事务没有隔离级别的概念 Redis的命令在事务的队列中, 并没有直接被执行! 只有发起执行命令(Exec)后才会被执行

能干嘛

事务的本质相当于一个命令队列
一个队列中, 一次性, 顺序性, 排他性的执行一系列命令

怎么玩

开启事务(multi)
命令入队(set k1 v1 …多条命令)
执行事务(exec)

锁: Redis 可以实现乐观锁 watch命令

常用命令

在这里插入图片描述

Case1: 正常放行

在这里插入图片描述

Case2: 放弃事务

在这里插入图片描述

Case3: 全体连坐, 命令还没加入到queue就报错,会全体连坐, 类似于编译报错

在这里插入图片描述

Case4: 冤头债主,命令已经成功加入到队列中, 执行时报错, 会冤头债主, 类似于运行报错

在这里插入图片描述

Case5: watch监控

悲观锁/乐观锁/CAS(Check And Set)
悲观锁

认为一定会出事儿, 一定会有人改
在这里插入图片描述

乐观锁

认为不一定会出事儿, 每条记录后面加一个version版本号, 每个人修改时, 修改之前要读一次, 修改过后要提交的时候, 还要读一次, 如果两次读取的版本号不一致, 则不能提交, 要把最新的数据读出来以后再这个基础上进行修改后, 才能再次提交(也要进行version比较,成功后才能提交)
在这里插入图片描述

CAS check and set

比较并设置, 先比较是否相同, 相同了再进行设置

初始化信用卡可用余额和欠额

在这里插入图片描述
余额-20 欠额要+20

无加塞篡改, 先监控再开启multi, 保证两笔金额变动在同一个事物内

在这里插入图片描述

有加塞篡改

在这里插入图片描述

unwatch, 取消对所有key的监视

如果监控到的变量被改变了, 先unwatch解锁后 再重新watch加锁, 直到事务被正常执行为止

一旦执行了exec之前加的监控锁都会被取消掉了
小结

在这里插入图片描述

3阶段

在这里插入图片描述

3特性

在这里插入图片描述

Redis的发布订阅

是什么

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

订阅/发布消息图
在这里插入图片描述
在这里插入图片描述

命令

在这里插入图片描述

案例

在这里插入图片描述

Redis的复制(Master/Slave)

是什么

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

能干嘛

读写分离
容灾恢复

怎么玩

1.配从(库)不配主(库)

2.从库配置: slaveof 主库ip 主库端口

启用redis-cli 后 slaveof 主库ip 主库端口, 但是每次与master断开以后, 都需要重新连接, 除非你配置进redis.conf文件

连接上redis-cli后使用info replication查看主从复制状态

3.修改配置文件细节操作

拷贝多个redis.conf文件
在这里插入图片描述
开启daemonize yes
Pid文件名字
指定端口
log文件名字
dump.rdb名字

分别修改3个conf文件对应的daemonize, pidname, logname, dump.rdb为不同的, 在一台虚拟机上模拟3个redis-server
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
其余两台机器修改方式同上, 但是要加以区分

4.常用3招

一主二仆(一台主机, 两台备机)

Init
在这里插入图片描述

一个master 两个slave
在这里插入图片描述
在这里插入图片描述
日志查看
在linux中tail -100f 6379.log

主从问题演示
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

薪火相传

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

反客为主

首先恢复成一主二仆配置, 主 down的情况下, 在从机上执行slaveof no one使当前数据库停止与其他数据库的同步, 转成主数据库在这里插入图片描述

复制原理

在这里插入图片描述

哨兵模式(sentinel)

简单一句话, 反客为主的自动版

是什么

反客为主的自动版, 能够后台监控主机是否故障, 如果故障了根据投票数自动将从库转换为主库

怎么玩

  1. 调整结构, 6379带着80, 81
  2. 自定义的 /myredis目录下新建sentinel.conf文件, 名字绝不能错
  3. 配置哨兵, 添写内容vim sentinel.conf
    1. sentinel monitor 被监控redis-server名字(自己起名字) 127.0.0.1 6379 1
    2. 上面最后一个数字1, 表示主机挂掉后slave投票看让谁接替成为新主机, 得票数多少后成为主机
  4. 启动哨兵
    1. redis-sentinel /myredis/sentinel.conf
    2. 上述目录依照各自的实际情况配置, 可能目录不同
  5. 正常主从演示
  6. 原有的master挂了
  7. 投票新选(哨兵工作,主库切换)
    在这里插入图片描述
  8. 重新主从继续开工, info replication查看
    在这里插入图片描述
  9. 问题: 如果之前的master重启回来, 会不会双master冲突 不会, 它会被设置成slave
    在这里插入图片描述

一组sentinel能同时监控多个master

复制的缺点

在这里插入图片描述

Redis的Java客户端Jedis

是什么?

  Jedis是Redis官方推荐的java连接开发工具, 在使用java操作Redis的中间件,如果要使用java操作redis, 那么一定要对Jedis十分的熟悉

Jedis测试联通

  1. 导入依赖
<dependencies>
        <!--导入Jedis的jar包-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.2.0</version>
        </dependency>
        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>
  1. 编码测试
  • 连接数据库
public class TestPing {
    public static void main(String[] args) {
        // 1. new Jedis对象即可
        Jedis jedis = new Jedis("192.168.0.5",6379);
        // jedis所有的命令就是我们之前学习的所有指令
        System.out.println(jedis.ping());
    }
}

出现问题1: Connection refused
ip地址值正确, 端口正确, 访问连接异常, 原因是在redis的conf文件中指定了bind 127.0.0.1了, 注释这一行即可
出现问题2: DENIED Redis is running in protected mode because protected mode is enabled
因为redis的conf文件中设置了protected-mode yes开启受保护模式, 把他设置成no即可

  • 操作命令
  • 断开连接
    jedis.close();

Jedis常用API

Key

public class TestPing {
    public static void main(String[] args) {
        // 1. new Jedis对象即可
        Jedis jedis = new Jedis("192.168.0.5",6379);
        System.out.println("清空数据: "+ jedis.flushDB());
        System.out.println("判断某个键是否存在: "+ jedis.exists("username"));
        System.out.println("新增String类型的k-v username-j1的键值对" + jedis.set("username","j1"));
        System.out.println("新增String类型的k-v password-hh的键值对" + jedis.set("password","hh"));
        System.out.println("获取所有的key");
        Set<String> keys = jedis.keys("*");
        System.out.println(keys);
        System.out.println("删除键password: "+ jedis.del("password"));
        System.out.println("判断password是否存在"+ jedis.exists("password"));
        System.out.println("查看键username所存储的值的类型" + jedis.type("username"));
        System.out.println("随机返回key空间的一个" + jedis.randomKey());
        System.out.println("重命名key" + jedis.rename("username","name"));
        System.out.println("取出改名后的name对应的值" + jedis.get("name"));
        System.out.println("根据索引选择数据库" + jedis.select(0));
        System.out.println("删除当前选择库中的所有key" + jedis.flushDB());
        System.out.println("返回当前库中key的数目" + jedis.dbSize());
        System.out.println("删除所有数据库中的所有key" + jedis.flushAll());
    }
}

String

public class TestPing {
    public static void main(String[] args) {
        // 1. new Jedis对象即可
        Jedis jedis = new Jedis("192.168.0.5", 6379);
        System.out.println("========新增数据========");
        System.out.println(jedis.set("key1","value1"));
        System.out.println(jedis.set("key2","value2"));
        System.out.println(jedis.set("key3","value3"));
        System.out.println("删除键key2 "+ jedis.del("key2"));
        System.out.println("获取键key2" + jedis.get("key2"));
        System.out.println("修改key1的值" + jedis.set("key1","value1Changed"));
        System.out.println("获取key1的值" + jedis.get("key1"));
        //append返回追加后生成字符串的长度
        System.out.println("在key3后面加入值" + jedis.append("key3","End"));
        System.out.println("获取key3的值" + jedis.get("key3"));
        System.out.println("增加多个键值对" + jedis.mset("key01","value01","key02","value02","key03","value03"));
        System.out.println("获取多个键值对" + jedis.mget("key01","key02","key03"));
        System.out.println("获取多个键值对" + jedis.mget("key01","key02","key03","key04"));
        System.out.println("删除多个键值对" + jedis.mget("key01","key02"));
        System.out.println("获取多个键值对" + jedis.mget("key01","key02","key03"));

        jedis.flushDB();
        System.out.println("========新增键值对防止覆盖原先值========");
        System.out.println("========不存在再创建setnx===================");
        System.out.println(jedis.setnx("key1","value1"));
        System.out.println(jedis.setnx("key2","value2"));
        System.out.println(jedis.setnx("key2","value2-new"));
        System.out.println(jedis.get("key1"));
        System.out.println(jedis.get("key2"));

        System.out.println("========新增键值对并设置有效时间========");
        System.out.println(jedis.setex("key3",2,"value3"));
        System.out.println(jedis.get("key3"));

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(jedis.get("key3"));

        System.out.println("==========获取原值, 更新为新值==========");
        System.out.println(jedis.getSet("key2","key2GetSet"));
        System.out.println(jedis.get("key2"));

        System.out.println("获得key2的值的子串" + jedis.getrange("key2",2,4));


    }
}

List

在这里插入图片描述

Set

在这里插入图片描述
在这里插入图片描述

Hash

在这里插入图片描述

Jedis事务

正常执行事务

// 1. new Jedis对象即可
        Jedis jedis = new Jedis("192.168.0.5", 6379);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("j1","handsome");
        jsonObject.put("hh","beautiful");
        String str = jsonObject.toJSONString();
        //可以在这里加一个监控
        jedis.watch("name1");
        //开启事务
        Transaction multi = jedis.multi();
        try {
            multi.set("name1",str);
            multi.set("name2",str);
            //执行事务
            multi.exec();
        } catch (Exception e) {
            //放弃事务
            multi.discard();
            e.printStackTrace();
        } finally {
            //查询一下是否set进去了
            System.out.println(jedis.get("name1"));
            System.out.println(jedis.get("name2"));
            //关闭连接
            jedis.close();
        }

Jedis主从复制

JedisPool

SpringBoot集成Redis

  1. 导入依赖
  2. 配置连接
  3. 测试
    springboot使用redisTemplate

说明: 在SpringBoot2.x之后, 原来使用的jedis被替换成了lettuce
jedis: 采用的是直连, 多个线程操作的话, 是不安全的, 如果想要避免不安全的, 使用jedis pool连接池,像是BIO模式
lettuce: 采用netty, 实例可以在多个线程中进行共享, 不存在线程不安全的情况, 可以减少线程数据了, 更像NIO模式

pom文件

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
    </parent>

    <dependencies>
        <!--导入Jedis的jar包-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.2.0</version>
        </dependency>
        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>

源码分析

@ConditionalOnMissingBean(name = “redisTemplate”)代表如果自定义了redisTemplate类的话, 就不用下面的类了

默认的RedisTemplate 没有过多的设置redis对象都是需要序列化!
两个反省都是Object, Object的类型, 我们后续使用需要强制转换
我们可以自己定义一个redisTemplate来替换这个默认的
由于string 类型是redis中最常使用的类型, 所以单独提出了一个Bean叫stringRedisTemplate

@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
			//默认的RedisTemplate 没有过多的设置redis对象都是需要序列化!
			//两个反省都是Object, Object的类型, 我们后续使用需要强制转换<String, Object>
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

yml

spring:
  redis:
    host: 192.168.0.5
    port: 6379
    # 配置连接池需要配置lettuce连接池

测试

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestPing {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void test1(){

        //redisTemplate都是opsForxxx例如opsForValue就是操作k-v类型的,
        //opsForSet就是操作Set类型

        //除了基本的操作, 我们常用的方法都可以直接redisTemplate.出来, 比如事务和基本的CRUD
        redisTemplate.opsForValue().set("myKey","杨俊毅");
        System.out.println(redisTemplate.opsForValue().get("myKey"));

        //获取连接, 可以清空库
//        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
//        connection.flushDb();
//        connection.flushAll();


    }
}

RedisTemplate的序列化(自定义RedisTemplate)

在这里插入图片描述
在这里插入图片描述
不添加序列化的错误
在这里插入图片描述
在PoJo类中一般都会实现序列化, 加入implements Serializable后 再用redisTemplate直接set对象再取就不会报错了
在这里插入图片描述

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
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
  @SuppressWarnings("all")
  public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
      RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
      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序列化方式采用jackson
      template.setValueSerializer(jackson2JsonRedisSerializer);
      // hash的value序列化方式采用jackson
      template.setHashValueSerializer(jackson2JsonRedisSerializer);
      template.afterPropertiesSet();

      return template;
  }


}

RedisTemplate工具类RedisUtil

package com.kuang.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

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

@Component
public final class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // =============================common============================
    /**
     * 指定缓存失效时间
     * @param key  键
     * @param time 时间(秒)
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据key 获取过期时间
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }


    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 删除缓存
     * @param key 可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }


    // ============================String=============================

    /**
     * 普通缓存获取
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */

    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 普通缓存放入并设置时间
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */

    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 递增
     * @param key   键
     * @param delta 要增加几(大于0)
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }


    /**
     * 递减
     * @param key   键
     * @param delta 要减少几(小于0)
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }


    // ================================Map=================================

    /**
     * HashGet
     * @param key  键 不能为null
     * @param item 项 不能为null
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取hashKey对应的所有键值
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     * @param key 键
     * @param map 对应多个键值
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * HashSet 并设置时间
     * @param key  键
     * @param map  对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 删除hash表中的值
     *
     * @param key  键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }


    /**
     * 判断hash表中是否有该项的值
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }


    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     *
     * @param key  键
     * @param item 项
     * @param by   要增加几(大于0)
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }


    /**
     * hash递减
     *
     * @param key  键
     * @param item 项
     * @param by   要减少记(小于0)
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }


    // ============================set=============================

    /**
     * 根据key获取Set中的所有值
     * @param key 键
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 根据value从一个set中查询,是否存在
     *
     * @param key   键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 将数据放入set缓存
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * 将set数据放入缓存
     *
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0)
                expire(key, time);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * 获取set缓存的长度
     *
     * @param key 键
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * 移除值为value的
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 移除的个数
     */

    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    // ===============================list=================================

    /**
     * 获取list缓存的内容
     *
     * @param key   键
     * @param start 开始
     * @param end   结束 0 到 -1代表所有值
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 获取list缓存的长度
     *
     * @param key 键
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * 通过索引 获取list中的值
     *
     * @param key   键
     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 将list放入缓存
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }


    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }


    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 根据索引修改list中的某条数据
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return
     */

    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 移除N个值为value
     *
     * @param key   键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */

    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }

    }

}

Redis各个数据类型的使用

在这里插入图片描述
在这里插入图片描述
sortedSet做延时任务
https://www.cnblogs.com/catcher1994/p/12496319.html

Redis的过期策略以及内存淘汰机制

在这里插入图片描述
在这里插入图片描述

Redis的渐进式ReHash

在这里插入图片描述
渐进式ReHash相关文章
https://blog.csdn.net/belalds/article/details/93713491

Redis的缓存穿透和缓存雪崩

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值