redis详解

 

参考b站狂神说的视频

https://www.bilibili.com/video/BV1S54y1R7SB?p=36&share_source=copy_web

非关系型数据库:nosql数据库

非关系型数据库的优点

1。方便扩展(数据之间没有关系,很好扩展)

2。大数据高性能(nosql的缓存记录数)

3。数据类型是多样型的 (不需要事先设计数据库 随去随用)

 

高性能 高可用  高可扩

 

NOSQL的四大分类

K V键值对.      ————->缓存 日志

美团:redis tait

阿里 百度:redis memcache

 

文档型数据库    —————>分布式文件系统

MongoDB(必须掌握) 

基于分布式文件存储的数据库 C++编写,主要用来处理大量的文档

mongoDB是一个介于关系型数据库和非关系型数据库的中间的产品。MongoDB是非关系型数据库中功能最丰富 最像非关系型数据库的

ConthDB

 

列存储数据库—-》web应用    HBase

图关系数据库——-〉社交网络  Neo4j

 

2.Redis是什么

1.redis远程字典服务c语言编写,支持网络的,基于内存的可持久化 日志型,key value 数据库。支持多种语言的API

免费和开源。 结构化数据库。

 

作用:

内存存储 持久化 (内存中是断电即失)(持久化 rdb aof)

效率高 可以用于高速缓存

发布订阅系统(简单消息队列)

地图信息分析

计时器 计数器

 

特性

1.多样的数据类型

2.持久化

3.集群

4,事务

 

测试性能

Redis-benchmark 压力测试工具

测试 100个并发 100000请求

Refis-benchmark -h localhost -p 6379 -c 100 -n 100000

 

Redis 推荐在linux上搭建

 

3.Redis基础知识

Redis默认有16个数据库  默认使用的第0个

可以使用select进行切换数据库

eg:select 3

 keys * 查看所有的key 

flushall 清空所有的数据库

flushdb 清空当前数据库

   

Redis 是单线程的

Redis是基于内存操作,CPU并不是redis的瓶颈,根据机器的内存和网络带宽

为什么单线程还这么快?

CPU>内存>硬盘

Redis是将所有的数据都存在内存中,对于内存系统来说,没有上下文切换效率就是最高的。

 

它可以用作数据库 缓存 消息中间件。

支持多种类型的数据结构 字符串 散列 列表 集合 有序集合与范围查询

redis的五大数据类型

Redis-Key
判断是否存在   exists key值
移除指定的key值 Move key值 数据库
设置过期时间,单位是秒  expire name 10
查看key的剩余时间 ttl key值
查看key的类型 type key值

string

set key值 value 赋值
get key值 获取值
APPEND key值 “hello” 拼接字符串
如果key不存在就新建
STRLEN key值 字符串长度
incr key值  对应的值加1
decr key值  对应的值减1
INCRBY key值 步长 可以设置指定增量
eg:INCRBY views 10 对应的值加10
获取范围
eg:set key1 “hello,zhangsan”
GETRANGE key1 0 3
“hell”
GETRANGE key1 0 -1 //查看全部字符串
“hello,zhangsan” 

  替换

set key2 abcdefg
SETRANGE key2 1 xx //替换指定位置开始的字符串
get key2 ->axxdefg

setex(set with expire) 设置过期时间
eg:setex key3 30 “hello”
setnx(set if not exists) 不存在设置
eg:setnx mikes  “redis”

 批量获取和批量设置

批量设置 mset
eg: mset key1 value1 key2 value2 key3 value3
批量获取 mget
eg: mget key1 key2 key3
msetnx key1 value1 key4 value4
//msetnx是个原子性操作,一起成功 一起失败

对象 设置一个user对象
user:{id}:{filed}
mset user:1:name zhangsan user:1:age 18
mget user:1:name user:1:age
1)”zhangsan”
2)”18”

 先get后set

getset  先get后set
getset db redis ->(nil)
get db —>redis
Getset db mongdb —->redis
get db—>mongdb

string类型的应用:

计数器

统计数量

统计多单位的数量

对象缓存

 

List

列表  

所有的list命令都是l开头的

添加 LPUSH list one  //每次将数据插入头部

LPUSH list two

获取 LRANGE list 0 -1
-》“two”
-》“one”

Rpush list right //每次将数据放到尾部

LRANGE list 0 -1
-》“two”
-》“one”
-》“Right

LPOP 移除尾部的值

RPOP 移除头部的值

eg: Lpop list

通过下标获取值

LRAANGE list 0 -1
-》two
-》one

Lindex list 0 —》two
 

返回列表的长度
Llen list —>2

移除指定值。list可以重复 
Lpush list three
Lpush list three

Lrem list 1 three //从list中移除一个three

 
List 截断 

Rpush mylist “hello”
Rpush mylist “hello1”
Rpush mylist “hello2”
Rpush mylist “hello3”

Ltrim mylist 1 2 //通过下标截取指定的长度

Lrange mylist 0 -1
->“hello1”
->“Hello2”

 

rpoplpush 移动list中最后一个值 并将其移动到新的列表中

rRush mylist “hello”
rRush mylist “hello1”
rRush mylist “hello2”

rpoplpush mylist myotherlist

Lrange mylist 0 -1
->“hello”
->“hello1”

Lrange myotherlist 0 -1
->“Hello2”


lset  将列表中指定下标的值替换为另外一个值 更新操作
如果列表不存在 更新会报错 如果下标的值存在更新,不存在 报错
Linset 将某个具体的value 插入到list中某个元素的前面和后面

Rpush mylist “hello”
Rpush mylist “world”

Insert mylist before world other
->“hello”
->“other”
->“World

小结

实际上是一个链表。

如果key 不存在 创建新的链表

在两边插入或者改动值 效率最高,中间元素,相对来说效率低一些 

应用:消息队列

 

Set (集合)

不能重复

所有的set命令都是s开头的 

添加

 

sadd myset “hello”
sadd myset “kuangshen”

获取所有元素 SMembers
SMembers myset
->“hello”
->“kuangshen

 

判断是否包含某个元素  Sismeber myset hello

获取set集合的元素个数  scard myset

移除某一个元素 Srem myset hello


随机抽出一个元素 Srandmember myset Srandmember myset 2 随机抽取指定个数的元素

 

随机移除set集合中的元素

eg:SMembers myset
->Kuangshen2
->Kuangshen
->Hello

Spop myset ->kuanghen2
Spop myset —>kuangshen

  

Smove 将一个指定的key 移动到另一个set 

Sadd myset “hello”

Sadd myset “world”

Sadd myset2 “set2”

Smove myset myset2 “hello

 

Sadd key1 a
Sadd key1 b
Sadd key1 c

Sadd key2 c
Sadd key2 d 
Sadd key2 e


Sdiff key1 key2 //两个set集合的差集
-> “b”
-> “a”
key1中key2没有的

Sinter key1 key2//两个set集合的交集
->“c”

SUnion key1 key2//并集

Hash(哈希)

Map 集合 key -map

本质和string 没有什么太大区别

所有的hash命令都是h开头的

set myhash field1 kuangshen
Hget myhash field1
->“Kuangshen”
//值会被覆盖
Hmset myhash field1 hello field2 world
Hmget myhash field1 field2
->“hello”
->”world”
Hgetall myhash
->“hello”
->”world”
hdel myhash field1 //删除hash指定key值
Hlen myhash //获取hash的长度
Exists myhash field1//判定hash中指定的key是否存在
Hkeys myhash//获取所有的key值
Hvals myhash//获取所有的value值
Hset myhash field3 5

Hincrby myhash field3 1//给指定key值增加指定步长

Hsetnx //如果存在不能设置 不存在就能设置

Hash 变更的数据 user name age 更适合对象的存储
Hset user:1 name “zhangsan”
Hset user:1 age “18”

应用:集合之间的操作。交集。并集  差集

 

Zset 有序集合

set的基础上,增加了一个值  

set k1 v1 

Set k1 score1 v1

 

eg:Zadd myset 1 one
Zadd myset 2 two 3 three
Zrange myset 0 -1
->”one”
 “two”
 “three"

 

 

排序


Zadd salary 2500 xiaohong
Zadd salary 5000 zhangsan
Zadd salary 500 kuangshen


ZRANGEBYSCORE salary -inf +inf
->“kuangshen”
-> “xiaohong”
-> “zhangsan"

 

 

ZRANGEBYSCORE salary -inf +inf with scores
->”kuangshen”
->“500”
->“xiaohong”
->“2500”
->“zhangsan”
->“5000

排序(上限)

ZRANGEBYSCORE salary -inf 2500
->“kuangshen”
->“xiaohong

 

Zrevrange salary 0 -1 (从大到小排序)
-》“zhangsan”
-》“Kuangshen”

移除元素 zrem
Zrem salary xiaohong

查看元素个数
Zcard salary

Zconut 获取指定区间的成员数量
Zadd myset 1 hello 2 world 3 kuangshen
Zconut myset 1 3
-> 3


Zcont myset 1 2 获取指定区间的成员数量
-〉2  

 

小结:set 排序 存储班级成绩表 工资表排序

普通消息 1,重要消息 2.带权重进行判断

 应用:排行榜应用实现

三种特殊数据类型

 1.Geospatial 地理位置

Redis  geo  可以推算地理位置的信息 两地之间的距离 方圆几里的人

Geoadd key 值(纬度 经度 名称)

规则:两极无法直接添加 一般会下载城市数据 直接java程序导入

Geoadd china:city 116.40 39.90 beijing

Geoadd china:city 121.47 31.23 Shanghai

Geoadd china:city 106.50 29.53 Chongqing 114.05 22.52 Shenzhen

有效的经度 -180 180

有效的纬度 -85 85

 

获取指定城市的经度和纬度

 

Geopos china:city beijing
->”116.3999..”
->“39.90000

 

Geodist 获取指定两个位置的距离(直线距离) 

Geodist china:city beijing Shanghai km

 

我附近的人?(获得所有附近的人的地址 定位)

Georadius 以给定的经纬度为中心 找出某一半径内的元素

Georadius China:city 110 30 500 km
->”chongqin”

 

Georadius China:city 110 30 500 km withdist 直线距离

Georadius China:city 110 30 500 km withcoord 经纬度

 

获得指定数量的人

Georadius China:city 110 30 500 km count 1

 

北京周围的城市

Georadiusbymember china:city beijing 100 km

 

Geohash 返回一个或者多个位置元素的geohash表示

将返回11个字符串的geohash字符串(将二维的经纬度转换为一维的字符串)

 

Geo 底层的实现原理就是zset ,可以使用zset 命令来操作

 

 2.Hyperloglog PF开头的命令

什么是基数?不重复的元素

A135787

B13578

基数(不重复的元素)     网页的浏览量(一个人访问一个网站多次,但是还是算作一个人)

占用的内存是固定的,2^64 不同的元素,只需要占用128kB内存

0.81%错误率

eg:PFADD mykey a b c d e f g h I j
PFCOUNT mykey
->10

PFADD mykey2 i j z x c v b n m
PFCOUNT Mykey2
->9


PFMEGRE mykey3 mykey mykey2
PFCOUNT mykey3
->15

如果允许有一定错误率,就可以用hyperloglog

 3.Bitmaps

位存储 操作二进制来记录 

统计用户信息 两个状态的 都可以使用Bitmaps

周一到周六的打卡

Setbit sign 0 1
Setbit sign 1 0
Setbit sign 2 0
Setbit sign 3 1
Setbit sign 4 1
Setbit sign 5 0
Setbit sign 6 0

Getbit sign 6
->0


Getbit sign 3
->1

bitcount sign //统计打卡的天数
->3 

 

Redis基本的事务操作

事务

一组命令的集合 一个事务中的所有命令都会被序列化 会按照顺序执行,

一次性 顺序性 排他性 执行一些列的命令

Redis事务没有隔离级别的概念 单条命令出错不会影响前面的任务 也不会影响后面的命令

Redis 单条命令

开启事务(multi

命令入队()

执行事务(exec)

Multi //开启事务

Set k1 v1

Set k2 v2

Get k2

Set k3 v3

exec //执行事务

->ok

->ok

->v2

->ok

每个事务执行完就没有了

放弃事务 discard 在事务执行之前可以放弃事务

 

编译型异常(代码有问题,命令有错)事务中所有的命令都不会被执行

运行型异常(如果事务队列中存在语法性错误,)执行事务的时候,其他命令会正常执行,错误命令抛出异常。

 

乐观锁

悲观锁:无论做什么都会加锁

乐观锁:更新数据的时候去判断一下,在此期间,是否有人修改过这个数据,

Mysql 获取version,更新的时候比较version

 

Redis监视测试

Set money 100

Set out 0

Watch money 监视money对象

Muiti

Decrby money 20

Incrby out 20

Exec  执行命令

->80

->20

  

测试多线程修改值,使用watch可以当作redis的乐观锁操作

Watch money
Multi
Decrby money 10
Incrby out 10

Exec 执行之前,另外一个线程,修改了我们的值,这个时候,就会导致我们事务执行失败
->(nil)

如何解决?释放锁,重新写事务

Unwatch

Watch money

  

Jedis

使用java操作Redis

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

导入包

2.编码测试:

连接数据库

操作命令

断开连接

 

Jedis jedis = new Jedis("127.0.0.1",6379);

System.out.println(jedis.ping());

方法和之前的api完全相同

Jedis.close();//关闭连接

事务:
public static void main(String[] args) {
    //
    Jedis jedis = new Jedis("127.0.0.1",6379);
    System.out.println(jedis.ping());
    jedis.flushDB();
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("hello","world");
    jsonObject.put("name","zhangsan");
    Transaction multi = jedis.multi();//开启事务
    String result = jsonObject.toJSONString();
    try {
        multi.set("user1",result);
        multi.set("user2",result);
        multi.exec();//执行事务
    }catch (Exception e){
        multi.discard();//销毁事务
        e.printStackTrace();
    }finally {
        System.out.println(jedis.get("user1"));
        System.out.println(jedis.get("user2"));
        jedis.close();
    }
}

   

springboot整合

Springboot2.x之后。原来使用的jedis 被替换成了letture

Jedis:采用的直连。 多线程操作的话,不安全,避免不安全就使用jedis pool连接池 

Lettuce:采用netty,异步,实例可以在多个线程中进行共享,不存在线程不安全的情况,可以减少线程数量。

 

导入包

配置连接

测试 

传输对象需要序列化

自定义的redisTemplate

 

Redis.conf详解

1.大小写不敏感

2.可以包含其他配置文件

 

Network

Bind 127.0.0.1 绑定ip

通用 general

Demonize yes #后台运行 默认是no

Loglevel notice #生产环境

Logfile 日志文件名

Database 16 #数据库的数量

Always-show-logo yes #是否显示logo

 

 

快照

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

如果900秒至少有一个key 进行了修改,我们

Save 900 1

Save 300 10

Save 60 10000

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

Rdbcompression yes 是否压缩rdb文件,会消耗一些cpu资源

Rdbchecksum yes 保存rdb文件的时候,进行错误的检查校验

Dir ./ rdb文件保存的目录

 

Requirepass 123456 #设置密码

Config set require pass 123456

Config get require pass

->noauth authentication required

Auth 123456

->ok

 

Append only 模式

Appendonly no 默认是不开启,默认是使用rdb方式持久化,大部分所有的情况下,rdb完全够用

Appendfsync everysec 每秒执行一次

 

Redis持久化

Redis是内存数据库,如果不将内存中的数据库保存到磁盘中,一旦服务器进程退出,数据库状态也会消失,所以redis提供了持久化功能

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

保存的文件是dump.rdb

触发规则

1.save的规则满足的情况下,会自动触发rdb规则

2.Flushall命令,也会触发我们的rdb规则

3.退出redis,也会产生rdb文件

备份就会自动生成一个dump.rdb

 

如果恢复rdb文件的数据

只需要将rdb文件

放到我们的redis启动目录下,redis启动的时候会自动检查dump.rdb 恢复其中的数据

查看需要的位置

Config  get dir

->dir

->usr/local/bin如果在该目录下存在dump.rdb文件,启动就会自动回复其中的数据

优点:

适合大规模的数据恢复

如果你对数据的完整性要求不高

缺点:

需要一定时间间隔进程操作,redis意外宕机了,最后一次修改数据就没了

Fork进程的时候,会占用一定的内存空间

  

AOF

以日志的形式来记录每个写操作,将redis执行过的所有指令记录下来(读操作不记录),只许追加文件不许修改文件,redis启动之初就会读取该文件重新构建,

AOF 保存的是appendonlyaof

默认不开启 

重启redis就可以生效了

如果aof文件有错误,redis无法启动

redis修复aof工具 redis-check-aof

Redis-check-aof -fix appendonly.aof

文件正常 重启就可以恢复

 

优点:

1.每次修改都同步,文件的完整性会更好

2。每秒同步一次,可能会丢失一秒的数据

3.从不同步,效率最高的

缺点:

1.相对于数据文件来说,aof远远大于rdb,修复的速度也比rdb

2.aof运行效率也比rdb慢,所以redis默认配置是rdb

 

Rewite 重写

 

发布订阅

消息通信模式 发送者发送消息 订阅者接收消息

消息发送者

频道

消息订阅者

Subscribe 频道名 //订阅频道

Publish 频道名 消息//向某频道发布消息

Subscribe 频道名 //订阅多个频道

 

Eg:

订阅端:subscribe bilibili

->subscribe

->kuangshenshuo

->message

->bilibili

->hello,bilibili

发送端:publish bilibili hello,bilibili

 

Subscribe就是把订阅的人加入到该频道的发布链表中

使用场景:

实时消息系统

实时聊天(聊天室)

订阅 关注系统都是可以的

 

 

主从复制 读写分离 

Master -slave1

            -slave2

            -slave3

主机进行读,从机进行写。减缓服务器的压力。

数据的复制是单向的,只能由主节点到从节点。

主从复制的作用:

1。数据冗余

2。故障恢复

3。负载均衡  通过多个从节点分担负载

4,高可用 

 

 

单台redis的最大使用内存不应该超过20G

默认情况下,每台redis都是主节点

info replication 查看当前库的信息

->role:master 角色

connected_slaves:0  没有从机

 

复制3个配置文件,修改对应的信息

1。端口

2pid名字

3log文件名字

4dump.rdb名字

 

启动服务 redis-server kconfig/redis79.conf

连接        redis-cli -p 6379

一主二从

 

主从复制之复制原理

默认情况下,每台redis都是主节点

info replication //查看当前库的信息

->role:master //角色

 

在从机中配置

slaveof 127.0.0.1 6379//绑定对应的主机

 

配置了之后,从机的角色变了 主机中可以看到从机的配置信息

 

真实的主从配置  修改配置文件

replicaof 主机IP 端口号

 

主机可以写,从机不能写只能读 

主机中的所有信息和数据,都会自动被从机保存

 

测试:主机断开连接 从机依旧连接到主机的,这个时候,主机如果回来了,从机依旧可以直接获取到主机写的信息

如果使用命令行 配置的主从,重启之后,又会变成主机,只要变成从机,立马就会从主机中获取值

 

从机启动成功连接到主机后,会发送一条sync同步命令

master接到命令后,启动后台的存盘进同时收集所有接收到的用于修改数据集命令,

在后台命令执行完毕之后,master将传送整个数据文件到slave.完成一次完全同步

 

 全量复制  slave服务在接收到数据库文件数据后,将其存盘加载到内存中

 增量复制  master继续将新的所有收集到的修改命令依次传给slave,完成同步

但是只要重新连接master ,一次全量复制将会被自动执行。

 

层层链路

Master -slave1 master  -slave2

     79       80                     81

80为从节点,不能写 ,只能读

slaveof no one

如果主机断开了连接 使用slaveof no one 让自己成为主机

   

 

哨兵模式(自动选举老大的模式)sentinel

通过后台监控主机是否故障,如果故障了根据投票数自动将从库转为主库

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

多哨兵模式

主服务器不能用 并且达到一定数量,让各个哨兵爸自己监控的从服务器实现切换主机,为客观下线

 

配置哨兵配置文件 sentinel.conf

Sentinel montior 被监控的名称  host port 1

Sentinel montior myredis 127.0.0.1 6379 1

后面的数字 代表主机挂了 slave投票看让谁接替成为主机 投票最多的,就会成为主机

启动哨兵

Reidis-sentinel kconfig/sentinel.conf

如果主机节点断开了,就会从从机中随机选择一个服务器

哨兵日志

Failover 发现故障

Sdown 主机转移

如果主机此时回来。只能归并到新的主机下,当作从机

 

 

优点

1.哨兵集群,基于主从复值模式,所有的主从配置优点,它都有

2.主从可以切换,故障可以转移,系统的可用性好

3.哨兵模式就是主从模式的升级,手动到自动,更加健壮

缺点

Redis不好在线扩容,集群容量一旦到达上限,在线扩容十分麻烦

实现哨兵模式的配置其实是很麻烦的,里面有很多选择。

 

Redis缓存穿透与雪崩

缓存穿透

用户查询一个数据 redis内存数据库中没有,缓存没有命中,于是在持久层数据库查询

发现也没有,于是本次查询失败,当用户很多,缓存都没有命中(秒杀),于是都去请求了持久层数据库,这就会给持久层数据库造成很大压力,出现了缓存穿透。

 

布隆过滤器

对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,避免了对底层存储系统的压力

客户端 -bloomfilter  ->缓存层   -〉存储层

 

缓存空对象

没命中的空对象缓存起来,同时设置一个过期时间,之后再访问这个数据从缓存中获取

 

问题:

如果空值能够被缓存起来,这就意味着缓存需要更多空间,因为这当中可能有许多空值的键

即使对空值设置了一个过期时间,还是会存在缓存层和存储层的数据会有一段时间的不一致,这对于需要保持一致性的业务会有影响。

 

缓存击穿

是指一个key非常热门,在不停的扛着高并发,这个key在失效的瞬间,持续的大并发就会穿透缓存,直接请求数据库。

 

设置热点数据永不过期

加分布式锁

使用分布式锁,保证每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限

 

缓存雪崩

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

停掉一些服务

 

redis高可用

多增几台redis

限流降级

这个解决方案的思想是,缓存失效时通过加锁或者队列控制数据库写缓存的线程数量

数据预热

数据预热的含义就正式部署之前,先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中,在即将发生大并发访问前手动触发并加载缓存不同的key,设置不同的缓存过期时间,让缓存失效的时间尽可能均匀

 

posted @ 2021-10-09 08:35  下饭  阅读(73)  评论(0编辑  收藏  举报