Redis学习笔记

发布于:2025-02-10 ⋅ 阅读:(82) ⋅ 点赞:(0)

目录

Nosql概述

为什么用Nosql

什么是Nosql

Nosql四大分类

Redis入门

概述

Windows安装

Linux安装

 测试性能 

 基础知识

五大数据类型

Redis-Key

String(字符串)

List(列表)

Set(集合)

Hash(哈希)

Zset(有序集合)

命令汇总

三大特殊数据类型

geospatial(地理位置)

hyperloglog(基数统计) 

bitmap(位图)

命令汇总 

事务

Jedis

常用API测试

事务测试 

Springboot整合

Redis.cong详解

Redis持久化*****

RDB(Redis Database)

AOF(Append Only File) 

Redis订阅发布

Redis主从复制

 环境配置

 一主二从

哨兵模式

Redis缓存穿透和雪崩


Nosql概述

为什么用Nosql

单机时代

瓶颈 

1,数据量太大,一台机器放不下了

2,数据的索引太大了(B+Tree),一台机器也放不下

3,访问量太大(读写混合),一个服务器承受不了

缓存时代(读写分离)+垂直拆分

发展过程:优化数据结构索引---->文件缓存(IO)----->Memcached(缓存)

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

本质:数据库(读写)

早些年MyISAM:表锁,十分影响效率,高并发会出现严重锁问题

转战Innodb:行锁

分库分表,解决写的压力

一个互联网基本架构模型

什么是Nosql

Not Only SQL(不仅仅是SQL)

泛指非关系型数据库

特点:

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

2,大数据量,高性能(Redis一秒可以写8W次,可以读取11W次,nosql的缓存是记录级,是一种细粒度的缓存)

3,数据类型是多样型的(不需要事先设计数据库,随取随用,量很大的表就无法设计)

4,传统的RDBMRDBMSS和NoSQL区别

        传统的:结构化组织;SQL;数据和关系都存在单独的表;操作,数据定义语言;严格的一致性;基础事务。。。。

        NoSQL:不仅仅是数据;没有固定查询语言;价值对存储,列存储,文档存储,图形存储(社交关系);最终一致性;CAP定理和BASE(异地多活);高性能,高可用,高可扩展性。。。。

Nosql四大分类

1,KV键值对

        Redis

2,文件型数据库(bson格式和json格式)

  • MongoDB
    • 基于分布式文件存储的数据库,C++编写,主要用来处理大量文档
    •  是介于关系和非关系型数据库中间的产品,是非关系型数据库功能最丰富,最像关系型数据库的

3,列存储数据库

        HBase

        分布式文件系统

4,图关系数据库

        他不是存图形,放的是关系,比如:朋友圈社交网络,广告推荐

        Neo4j,InfoGrid

Redis入门

概述

Redis是什么?

Remote Dictionary Server,即远程字典服务,C语言编写的、支持网络、可基于内存亦可持久化的日志型、key-value数据库,并提供多种语言的API

redis会周期性的把个更新的数据写入测i盘或者把修改操作写入追加的记录文件,并且在次基础上实现了master-slave(主从)同步。

免费开源,是当下热门的nosql技术之一,也被称之为结构化数据库

Redis能干吗?

1,内存存储,持久化,内存中式断电即失,所以持久化很重要(rdb、aof)

2,效率高,可以用于高速缓存

3,发布订阅系统

4,地图信息分析

5,计时器、计数器(浏览量)

.......

Redis特性

 1,多样的数据类型

2,持久化

3,集群

4,事务

.......

需求

官网 Redis - The Real-time Data Platform

中文网 Redis中文网

下载地址:

Windows安装

1,下载

2,解压:解压到自己的环境目录

3,双击启动服务即可:默认端口6379

4,使用redis客户端连接redis

ping之后显示pong就连接成功了

测试加入数据k-v

Redis 简介_redis教程

推荐使用linux开发

Linux安装

1,下载安装包

2,解压安装包,程序放/opt目录下 

 3,进入解压后的文件,可以看到配置文件

4, 安装gcc-c++

yum install gcc-c++
gcc -v
make
make
make install
yum install gcc-c++
#查看版本
[root@xhm redis-6.0.4]# gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/8/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --with-isl --disable-libmpx --enable-offload-targets=nvptx-none --without-cuda-driver --enable-gnu-indirect-function --enable-cet --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
gcc version 8.5.0 20210514 (Red Hat 8.5.0-4) (GCC) 

#配置环境  第一次执行会下载很多环境
[root@xhm redis-6.0.4]# make
[root@xhm redis-6.0.4]# make
cd src && make all
make[1]: Entering directory '/opt/redis-6.0.4/src'
    CC Makefile.dep

Hint: It's a good idea to run 'make test' ;)

make[1]: Leaving directory '/opt/redis-6.0.4/src'
[root@xhm redis-6.0.4]# make install
cd src && make install
make[1]: Entering directory '/opt/redis-6.0.4/src'

Hint: It's a good idea to run 'make test' ;)

    INSTALL install
    INSTALL install
    INSTALL install
    INSTALL install
    INSTALL install
make[1]: Leaving directory '/opt/redis-6.0.4/src'
[root@xhm redis-6.0.4]# 

5,redis默认安装路径  /usr 

6,将redis配置文件复制到当前目录 

7, 默认不是后台启动,我们需要后台启动,修改配置文件

redis.conf 配置项说明:Redis 配置 - 菜鸟教程

[root@xhm xhmcopyredisconfig]# vim redis.conf 

8,启动,通过指定配置文件启动

9,使用redis客户端连接,测试set

[root@xhm bin]# redis-cli -p 6379
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set name xhm  #设置
OK
127.0.0.1:6379> get name   #获取
"xhm"
127.0.0.1:6379> keys *   #查看所有key
1) "name"
127.0.0.1:6379> 

 10,查看redis进程是否开启


11,如何关闭redis服务 shutdowm

开启进入流程

[root@xhm ~]# cd /usr/local/bin
[root@xhm bin]# ls
chardetect  cloud-init-per    jemalloc-config  jsondiff     jsonschema        luajit-2.1.0-beta3  redis-benchmark  redis-cli       xhmcopyredisconfig
cloud-id    dump.rdb          jemalloc.sh      jsonpatch    libmcrypt-config  mcrypt              redis-check-aof  redis-sentinel
cloud-init  easy_install-3.8  jeprof           jsonpointer  luajit            mdecrypt            redis-check-rdb  redis-server
[root@xhm bin]# redis-server xhmcopyredisconfig/redis.conf 
366374:C 24 Dec 2024 20:46:30.481 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
366374:C 24 Dec 2024 20:46:30.481 # Redis version=6.0.4, bits=64, commit=00000000, modified=0, pid=366374, just started
366374:C 24 Dec 2024 20:46:30.481 # Configuration loaded
[root@xhm bin]# redis-cli -p 6379
127.0.0.1:6379> 

 测试性能 

redis-benchmark可选参数:Redis 性能测试 - 菜鸟教程

 redis-benchmark是一个压力测试工具,官方自带测试性能

简单测试:如上图默认是50个并发,10000个请求,我想做100个并发,100000个请求

开启服务
[root@xhm bin]# redis-server xhmcopyredisconfig/redis.conf 

    
测试
[root@xhm bin]# redis-benchmark -h localhost -p 6379 -c 100 -n 100000

查看分析如下图: 

 基础知识

有16个数据库,默认使用第0个(0号数据库)

vim xhmcopyredisconfig/redis.conf 

可以使用select进行切换:select 3

[root@xhm bin]# redis-cli -p 6379
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> 

查看数据库大小 :  dbsize

127.0.0.1:6379[3]> dbsize
(integer) 0
127.0.0.1:6379[3]> set name xhm
OK
127.0.0.1:6379[3]> get name
"xhm"
127.0.0.1:6379[3]> dbsize
(integer) 1
127.0.0.1:6379[3]> 

查看所有的key: keys *

127.0.0.1:6379[3]> keys *
1) "name"

清空全部:flushall

127.0.0.1:6379[3]> select 0   #进去0号数据库,查看所有key
OK
127.0.0.1:6379> keys *
1) "key:{tag}:__rand_int__"
2) "myhash:{tag}:__rand_int__"
3) "mylist:{tag}"
4) "counter:{tag}:__rand_int__"
127.0.0.1:6379> select 3   #进入3号数据库,清除所有
OK
127.0.0.1:6379[3]> flushall
OK
127.0.0.1:6379[3]> select 0    #返回0号数据库,数据就已经被清干净了
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> 

清空当前库:flushdb

127.0.0.1:6379[3]> flushdb
OK
127.0.0.1:6379[3]> keys *
(empty array)
127.0.0.1:6379[3]> 

redis是单线程 ,为什么还那么快?

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

核心:redis是把所有数据全部放在内存中的,所以说使用单线程取操作效率就是最高的,多线程(CPU上下文会切换:这是个耗时的操作!!),对于内存来说,没有上下文切换效率就是最高的,多次读写都在cpu上,在内存情况下,这个就是最佳方案。

误区:不是高性能的服务器就一定是多线程的;不是多线程(CPU会上下文切换)一定比单线程效率高

五大数据类型

官方命令文档:redis命令手册

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存消息队列代理。它支持字符串哈希表列表集合有序集合位图hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区

Redis-Key

设置值:set name xhm

查看所有key:keys *

判断key是否存在:exists name

127.0.0.1:6379> set name xhm
OK
127.0.0.1:6379> set age 18
OK
127.0.0.1:6379> exists name 
(integer) 1
127.0.0.1:6379> exists name1 
(integer) 0

移除key:move name 1

127.0.0.1:6379> move name 1
(integer) 1
127.0.0.1:6379> keys *
1) "age"

设置过期时间:expire name 10

查看多久过期时间:ttl name

127.0.0.1:6379> expire name 10    #设置10秒过期
(integer) 1
127.0.0.1:6379> ttl name   #还有6秒过期
(integer) 6
127.0.0.1:6379> ttl name
(integer) 3
127.0.0.1:6379> ttl name  #已经过期
(integer) -2
127.0.0.1:6379> keys *   #查看已经没有name了
1) "age"

查看key的类型:type key 

127.0.0.1:6379> type name
string

String(字符串)

给某个键值里面最追加字符串:append key str,如果没有key会新建

127.0.0.1:6379> get name
"xhm"
127.0.0.1:6379> append name wp
(integer) 5
127.0.0.1:6379> get name
"xhmwp"

#如果不存在key则相当于set key
127.0.0.1:6379> append key 01
(integer) 2
127.0.0.1:6379> keys *
1) "key"
2) "name"
3) "age"

字符串长度:strlen key

127.0.0.1:6379> strlen name
(integer) 5

i++/i--

自增:incr key

自减:decr key

127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> incr views
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> incr views
(integer) 3
127.0.0.1:6379> decr views
(integer) 2
127.0.0.1:6379> decr views
(integer) 1
127.0.0.1:6379> decr views
(integer) 0

步长:i+=

一次自增n:incrby key n

一次自减n:decrby key n

127.0.0.1:6379> get views
"8"
127.0.0.1:6379> incrby views 10
(integer) 18
127.0.0.1:6379> incrby views 10
(integer) 28
127.0.0.1:6379> incrby views 10
(integer) 38
127.0.0.1:6379> decrby views 2
(integer) 36
127.0.0.1:6379> decrby views 2
(integer) 34
127.0.0.1:6379> decrby views 2
(integer) 32

字符串范围range

getrange key start end  #(下标默认从0开始)

127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set key1 xhmandwp,helloword
OK
127.0.0.1:6379> get key1
"xhmandwp,helloword"
#获取下标0-7的字符
127.0.0.1:6379> getrange key1 0 7
"xhmandwp"

#获取所有字符
127.0.0.1:6379> getrange key1 0 -1
"xhmandwp,helloword"
127.0.0.1:6379> 

 替换:setrange key offset value #(offset 从第几位开始(0开始的下标),value替换字符串)

127.0.0.1:6379> set key2 abcdefgh
OK
127.0.0.1:6379> get key2
"abcdefgh"
127.0.0.1:6379> setrange key2 1 xxx  #从下标1开始,替换2个字符为xx
(integer) 8
127.0.0.1:6379> get key2
"axxxefgh"

setex (set with expire)  #设置过期时间

        setex key time value

setnx (set if not exist) #如果不存在,再设置,再分布式锁中会常常使用

        setnx key value

127.0.0.1:6379> setex key01 20 qwe
OK
127.0.0.1:6379> ttl key01
(integer) 14
127.0.0.1:6379> keys *
1) "key01"
127.0.0.1:6379> ttl key01
(integer) 1
127.0.0.1:6379> ttl key01
(integer) -2
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set key1 dfg
OK
127.0.0.1:6379> keys *
1) "key1"
127.0.0.1:6379> setnx key gfd   #不存在,则创建
(integer) 1
127.0.0.1:6379> setnx key1 gfd    #存在,则创建失败
(integer) 0
127.0.0.1:6379> keys *
1) "key"
2) "key1"
127.0.0.1:6379> 

 批量设置值

mset

        mset k1 v1 [k2 v2 k3 v3]

msetnx:原子性,要么一起成功,要么一起失败

mget

        mset k1 k2 k3

127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"

#k1已经存在,则创建失败,k4也创建不成功,原子性,要么一起成功,要么一起失败
127.0.0.1:6379> msetnx k1 v1 k4 v4
(integer) 0

设置对象:set user 1 {name:xhm,age 1}  # 设置一个user:1对象值为json字符串来保存一个对象

user:{id}:{filed}

比如设置浏览量,set article:100:views 0

127.0.0.1:6379> mset user:1:name xhm user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "xhm"
2) "2"

 getset :先get再set

 #如果不存在则,返回nil

#如果存在值,获取原来的值,并设置新的值

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"
127.0.0.1:6379> 
  •  String类型使用场景:字符串和数字
  • 计数器
  • 统计多单位数量
  • 粉丝数
  • 对象缓存

Redis Setnx 命令_只有在 key 不存在时设置 key 的值。

List(列表)

redis里面,可以把list弄成栈,队列,阻塞队列

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

添加元素:lpush list 值   #将一个值或多个值插入列表头部(左边)

添加元素:rpush list 值   #将一个值或多个值插入列表尾部(右边边)

获取所有元素:lrange list 0 -1

获取指定元素:lrange 0 1   #这里的0是从最后一个插入元素往前的顺序来的(最后插入的在头部)

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) "tow"
3) "one"
4) "right"

 移除元素:lpop list 元素/rpop list 元素

127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "tow"
3) "one"
4) "right"
127.0.0.1:6379> lpop list    #移除第一个元素
"three"
127.0.0.1:6379> lrange list 0 -1
1) "tow"
2) "one"
3) "right"
127.0.0.1:6379> rpop list   #移除最后一个元素
"right"
127.0.0.1:6379> lrange list 0 -1
1) "tow"
2) "one"

移除指定值: lrem list  1 one   #移除1个值为One

                      lrem list  2 three   #移除2个值为three

127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "tow"
4) "one"
5) "one"
127.0.0.1:6379> lrem list 1 one
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "tow"
4) "one"
127.0.0.1:6379> lrem list 2 three
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "tow"
2) "one"
127.0.0.1:6379> 

获取元素:lindex list 0   #通过下标获取值 

127.0.0.1:6379> lrange list 0 -1
1) "tow"
2) "one"
127.0.0.1:6379> lindex list 1
"one"
127.0.0.1:6379> lindex list 0
"tow"

获取长度:llen list

127.0.0.1:6379> llen list
(integer) 2

trim 修剪 --->list截断

通过下标截取指定长度:ltrim list start end   #下标为start-end的元素;截取之后,就是新下标了

127.0.0.1:6379> lrange list 0 -1
1) "h1"
2) "h2"
3) "h3"
4) "h4"
127.0.0.1:6379> ltrim list 0 2  #截取从0-2的元素
OK
127.0.0.1:6379> lrange list 0 -1
1) "h1"
2) "h2"
3) "h3"
127.0.0.1:6379> ltrim list 1 2
OK
127.0.0.1:6379> lrange list 0 -1
1) "h2"
2) "h3"
127.0.0.1:6379> 

移除列表最后一个元素,并且添加到新的列表中:rpoplpush  原列表 新列表

127.0.0.1:6379> lrange list 0 -1
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> rpoplpush list mylist
"3"
127.0.0.1:6379> lrange list 0 -1
1) "1"
2) "2"
127.0.0.1:6379> lrange mylist 0 -1
1) "3"

判断是否存在列表:exists 列表名

指定下标值替换为另外一个值(只是修改原列表,不可新增元素):lset 列表名 下标 值

127.0.0.1:6379> exists lits
(integer) 0
127.0.0.1:6379> lpush list item
(integer) 1
127.0.0.1:6379> lrange list 0 0
1) "item"
127.0.0.1:6379> lset list 0 newitem
OK
127.0.0.1:6379> lrange list 0 0
1) "newitem"
#如果不存在这个下标则报错,没有这个列表也会报错
127.0.0.1:6379> lset list 1 newitem2
(error) ERR index out of range

插入值:linsert list before/after 原元素 新增元素  #在某个元素之前或之后插入新元素 

127.0.0.1:6379> lrange list 0 -1
1) "hello"
2) "world"
127.0.0.1:6379> linsert list before world new
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "hello"
2) "new"
3) "world"
127.0.0.1:6379> 

 小结

  •  它实际上是一个链表,before node after,left right都可以插入
  • 如果key不存在,创建新的链表
  • 如果key存在,新增内容
  • 如果移除了所有值,空链表,也代表不存在
  • 在两边插入或者改动值,效率最高;中间元素,相对效率低些

消息排队,消息队列(lpush,rpop),栈(lpush lpop)

Set(集合)

set中的值不能重复,set的命令都是以s开头的

添加元素:sadd 集合名 元素值

查看元素:smembers 集合名

是否存在元素:sismember 集合名 元素名  #存在返回1  不存在fan

127.0.0.1:6379> sadd set hello
(integer) 1
127.0.0.1:6379> sadd set world
(integer) 1
127.0.0.1:6379> sadd set happy
(integer) 1
127.0.0.1:6379> smembers set
1) "hello"
2) "happy"
3) "world"
127.0.0.1:6379> sismember set hello
(integer) 1
127.0.0.1:6379> sismember set my
(integer) 0

 获取set集合中元素个数:scard 集合名

127.0.0.1:6379> scard set
(integer) 3

 移除元素:srem 集合名 元素值

127.0.0.1:6379> srem set hello
(integer) 1
127.0.0.1:6379> scard set
(integer) 2
127.0.0.1:6379> smembers set
1) "happy"
2) "world"

 set是无序不重复集合,抽随机

127.0.0.1:6379> SMEMBERS set
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"
127.0.0.1:6379> SRANDMEMBER set  #随机抽取一个
"3"
127.0.0.1:6379> SRANDMEMBER set   #随机抽取一个
"3"
127.0.0.1:6379> SRANDMEMBER set    #随机抽取一个
"4"
127.0.0.1:6379> SRANDMEMBER set 3   #随机抽取3个
1) "6"
2) "7"
3) "2"
127.0.0.1:6379> SRANDMEMBER set 3
1) "7"
2) "2"
3) "1"

随机删除key:spop 集合名

127.0.0.1:6379> SMEMBERS set
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"
127.0.0.1:6379> spop set
"6"
127.0.0.1:6379> spop set
"4"
127.0.0.1:6379> SMEMBERS set
1) "1"
2) "2"
3) "3"
4) "5"
5) "7"
6) "8"

将一个指定的值移动到另一个set集合中:smove 原集合 新集合 元素

127.0.0.1:6379> SMEMBERS set
1) "1"
2) "2"
3) "3"
4) "5"
5) "7"
6) "8"
127.0.0.1:6379> SMEMBERS set2
1) "b"
2) "a"
127.0.0.1:6379> smove set set2 8
(integer) 1
127.0.0.1:6379> SMEMBERS set2
1) "8"
2) "b"
3) "a"

场景:共同关注(并集)

数字集合类

差集:sdiff 集合1 集合2   #以集合1为参照,去掉跟集合2相同的元素,剩余的集合1中的元素

并集:sunion 集合1 集合2   

交集:sinter 集合1 集合2   #共同好友

127.0.0.1:6379> SMEMBERS set
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> SMEMBERS set2
1) "3"
2) "4"
3) "5"
127.0.0.1:6379> SDIFF set set2
1) "1"
2) "2"
127.0.0.1:6379> SUNION set set2
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> SINTER set set2
1) "3"

Hash(哈希)

map集合,key-Map  这个时候的值就是map集合

所有命令以h开头,本质和String类型没有太大区别,还是简单的key-value

设置值:hset 集合名 字段 值

获取值:hget 集合 字段

设置多个值:hmset 集合 字段1 值1 字段2 值2

获取多个值:hmget 集合 字段1 字段2

获取所有值+字段:hgetall 集合    #key和value都会获取

获取所有字段:hvals 集合名

获取所有值:hkeys 集合名

127.0.0.1:6379> hset hash filed1 value1  #设置单个值
(integer) 1
127.0.0.1:6379> hget hash filed1   #获取单个值
"value1"
127.0.0.1:6379> hmset hash filed2 value2 filed3 value3   #设置多个值
OK
127.0.0.1:6379> hmget hash filed1 filed2 filed3  #获取多个值
1) "value1"
2) "value2"
3) "value3"
127.0.0.1:6379> hgetall hash   #获取所有 键+值
1) "filed1"
2) "value1"
3) "filed2"
4) "value2"
5) "filed3"
6) "value3"
127.0.0.1:6379> hkeys hash   #获取所有键
1) "filed1"
2) "filed2"
3) "filed3"
127.0.0.1:6379> hvals hash   #获取所有值
1) "value1"
2) "value2"
3) "value3"

删除值:hdel 集合名 字段名   #删除hash指定的key字段,对应的value值就没有了

127.0.0.1:6379> hgetall hash
1) "filed1"
2) "value1"
3) "filed2"
4) "value2"
5) "filed3"
6) "value3"
127.0.0.1:6379> hdel hash filed1
(integer) 1
127.0.0.1:6379> hgetall hash
1) "filed2"
2) "value2"
3) "filed3"
4) "value3"

获取长度:hlen 集合名

127.0.0.1:6379> hlen hash
(integer) 3

判断key存在:hsxists 集合名 字段名

127.0.0.1:6379> hgetall hash
1) "filed2"
2) "value2"
3) "filed3"
4) "value3"
127.0.0.1:6379> HEXISTS hash filed1
(integer) 0
127.0.0.1:6379> HEXISTS hash filed2
(integer) 1
127.0.0.1:6379> 

指定自增:hincrby 集合 字段 加的数值(可正可负,负就相当于减了)

指定自减:hdecrby

127.0.0.1:6379> hmset hash num1 1 num2 2
OK
127.0.0.1:6379> HINCRBY hash num1 2    #num的值+2=3
(integer) 3
127.0.0.1:6379> hgetall hash
1) "filed2"
2) "value2"
3) "filed3"
4) "value3"
5) "num1"
6) "3"
7) "num2"
8) "2"
127.0.0.1:6379> HINCRBY hash num1 -3  #num1的值减3=0
(integer) 0
127.0.0.1:6379> hgetall hash
1) "filed2"
2) "value2"
3) "filed3"
4) "value3"
5) "num1"
6) "0"
7) "num2"
8) "2"

判断存在:hsetnx 集合 字段 值 #如果不存在则可以设置,如果存在则不能设置

127.0.0.1:6379> HSETNX hash num1 10   #存在则无法设置
(integer) 0
127.0.0.1:6379> hgetall hash
1) "filed2"
2) "value2"
3) "filed3"
4) "value3"
5) "num1"
6) "0"
7) "num2"
8) "2"
127.0.0.1:6379> HSETNX hash num3 10  #不存在则设置成功
(integer) 1
127.0.0.1:6379> hgetall hash
 1) "filed2"
 2) "value2"
 3) "filed3"
 4) "value3"
 5) "num1"
 6) "0"
 7) "num2"
 8) "2"
 9) "num3"
10) "10"
127.0.0.1:6379> 

应用:存变更数据   user-name:xhm age:18   尤其用户信息保存,经常变动的信息,更适合直接存对像

Zset(有序集合)

在set的基础上加了一个值,所有命令以z开头

set k v    ------>  zset k score v      zset多了一个计数位

新增:zadd 集合名 排序字段(scores) 值

查看:zrange 集合名 0 -1

127.0.0.1:6379> zadd salary 1000 xhm
(integer) 1
127.0.0.1:6379> zadd salary https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0 wp
(integer) 1
127.0.0.1:6379> zadd salary 50 zs
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "zs"
2) "xhm"
3) "wp"

排序:zrangebyscore 集合 最小值(-inf) 最大值(+inf正无穷)   #就按中间值排序

排序:zrangebyscore 集合 最小值(-inf) 最大值(+inf正无穷)  withscores   #带上中间的值

排序:zrangebyscore 集合 最小值(-inf) 2500 withscores   #只差2500之内的

排序:zrevrange 集合 0 -1  #从高到底 大到小排序

127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf  #从小到大排序查看
1) "zs"
2) "xhm"
3) "wp"
4) "ls"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0   #从小到大,查https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0以内的
1) "zs"
2) "xhm"
3) "wp"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0 withscores  #从小到大,查https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0以内的,加上工资
1) "zs"
2) "50"
3) "xhm"
4) "1000"
5) "wp"
6) "https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0"
127.0.0.1:6379> ZREVRANGE salary 0 -1   #从大到小排序查看
1) "ls"
2) "wp"
3) "xhm"
4) "zs"
127.0.0.1:6379> 

移除:zrem 集合 元素

127.0.0.1:6379> zrem salary ls
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores
1) "zs"
2) "50"
3) "xhm"
4) "1000"
5) "wp"
6) "https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0"
127.0.0.1:6379> zrem salary ls   #删掉李四
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores
1) "zs"
2) "50"
3) "xhm"
4) "1000"
5) "wp"
6) "https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0"

获取集合中的个数:zcard 集合名

区间变量:zcount 集合 最小 最大    #在这个区间的元素个数  #包含了边界值

127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores
1) "zs"
2) "50"
3) "xhm"
4) "1000"
5) "wp"
6) "https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0"
7) "ls"
8) "3000"
127.0.0.1:6379> zcard salary
(integer) 4
127.0.0.1:6379> ZCOUNT salary 100 https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0  #包含了边界值
(integer) 2
127.0.0.1:6379> ZCOUNT salary 50 https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0
(integer) 3

使用场景:

  • set排序,成绩表,工资排序 
  • 普通/紧急消息,带权重判断紧急程度
  • 排行榜实现

命令汇总

redis-key

设置值:set name xhm

查看所有key:keys *

判断key是否存在:exists name

移除key:move name 1

设置过期时间:expire name 10

查看多久过期时间:ttl name

查看key的类型:type key 

String

给某个键值里面最追加字符串:append key str,如果没有key会新建

字符串长度:strlen key

i++/i--

自增:incr key

自减:decr key

步长:i+=

一次自增n:incrby key n

一次自减n:decrby key n

字符串范围range

getrange key start end  #(下标默认从0开始)

替换:setrange key offset value #(offset 从第几位开始(0开始的下标),value替换字符串)

setex (set with expire)  #设置过期时间

        setex key time value

setnx (set if not exist) #如果不存在,再设置,再分布式锁中会常常使用

        setnx key value

批量设置值

mset

        mset k1 v1 [k2 v2 k3 v3]

msetnx:原子性,要么一起成功,要么一起失败

mget

        mset k1 k2 k3

设置对象:set user 1 {name:xhm,age 1}  # 设置一个user:1对象值为json字符串来保存一个对象

user:{id}:{filed}

比如设置浏览量,set article:100:views 0

 getset :先get再set

 #如果不存在则,返回nil

#如果存在值,获取原来的值,并设置新的值

list

添加元素:lpush list 值   #将一个值或多个值插入列表头部(左边)

添加元素:rpush list 值   #将一个值或多个值插入列表尾部(右边边)

获取所有元素:lrange list 0 -1

获取指定元素:lrange 0 1   #这里的0是从最后一个插入元素往前的顺序来的(最后插入的在头部)

移除元素:lpop list 元素/rpop list 元素

移除指定值: lrem list  1 one   #移除1个值为One

                      lrem list  2 three   #移除2个值为three

获取元素:lindex list 0   #通过下标获取值 (0 -1代表全部)

获取长度:llen list

trim 修剪 --->list截断

通过下标截取指定长度:ltrim list start end   #下标为start-end的元素;截取之后,就是新下标了

移除列表最后一个元素,并且添加到新的列表中:rpoplpush  原列表 新列表

判断是否存在列表:exists 列表名

指定下标值替换为另外一个值(只是修改原列表,不可新增元素):lset 列表名 下标 值

插入值:linsert list before/after 原元素 新增元素  #在某个元素之前或之后插入新元素 

set

添加元素:sadd 集合名 元素值

查看元素:smembers 集合名

是否存在元素:sismember 集合名 元素名  #存在返回1  不存在fan

获取set集合中元素个数:scard 集合名

移除元素:srem 集合名 元素值

set是无序不重复集合,抽随机 SRANDMEMBER set num(随机抽取,不写默认1,写了默认num个)

随机删除key:spop 集合名

将一个指定的值移动到另一个set集合中:smove 原集合 新集合 元素

场景:共同关注(并集)

数字集合类

差集:sdiff 集合1 集合2   #以集合1为参照,去掉跟集合2相同的元素,剩余的集合1中的元素

并集:sunion 集合1 集合2   

交集:sinter 集合1 集合2   #共同好友

hash

设置值:hset 集合名 字段 值

获取值:hget 集合 字段

设置多个值:hmset 集合 字段1 值1 字段2 值2

获取多个值:hmget 集合 字段1 字段2

获取所有值+字段:hgetall 集合    #key和value都会获取

获取所有字段:hvals 集合名

获取所有值:hkeys 集合名

删除值:hdel 集合名 字段名   #删除hash指定的key字段,对应的value值就没有了

获取长度:hlen 集合名

判断key存在:hexists 集合名 字段名

指定自增:hincrby 集合 字段 加的数值(可正可负,负就相当于减了)

指定自减:hdecrby

判断存在:hsetnx 集合 字段 值 #如果不存在则可以设置,如果存在则不能设置

zset

新增:zadd 集合名 排序字段(scores) 值

查看:zrange 集合名 0 -1

排序:zrangebyscore 集合 最小值(-inf) 最大值(+inf正无穷)   #就按中间值排序

排序:zrangebyscore 集合 最小值(-inf) 最大值(+inf正无穷)  withscores   #带上中间的值

排序:zrangebyscore 集合 最小值(-inf) 2500 withscores   #只差2500之内的

排序:zrevrange 集合 0 -1  #从高到底 大到小排序

移除:zrem 集合 元素

获取集合中的个数:zcard 集合名

区间变量:zcount 集合 最小 最大    #在这个区间的元素个数  #包含了边界值

三大特殊数据类型

geospatial(地理位置)

朋友定位,附近的人,打车距离计算

 添加地理位置:geoadd key 经度 纬度 member

规则:两极无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入

有效的经度从-180度到180度。

有效的纬度从-85.05112878度到85.05112878度。

当坐标位置超出上述指定范围时,该命令将会返回一个错误。

127.0.0.1:6379> geoadd china:city 104.04 30.39 chengdu
(integer) 1

获取指定城市的经纬度:geopos key member   

127.0.0.1:6379> geopos china:city chengdu
1) 1) "104.03999894857406616"
   2) "30.39000005171638463"

返回俩位置之间的距离:geodist key member1 member2 单位(km/m)   #

127.0.0.1:6379> GEODIST china:city chengdu suining km
"125.1850"

给定经纬度为中心,找某一般半径以内的地址:georadius key 经度 纬度 半径 单位(必须在当前集合中)

[WITHCOORD]:显示他人的定位信息

[COUNT count]:获取多少个

[WITHDIST]:显示到中心的距离

。。。参数如下图

附近的人?获得附近人的定位,通过半径查询

127.0.0.1:6379> GEORADIUS china:city 104.04 30.39 https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt km
1) "chengdu"
2) "suining"
3) "nanchong"
4) "zigong"
127.0.0.1:6379> GEORADIUS china:city 104.04 30.39 https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt km withcoord  #带经纬度
1) 1) "chengdu"
   2) 1) "104.03999894857406616"
      2) "30.39000005171638463"
2) 1) "suining"
   2) 1) "105.34000128507614136"
      2) "30.29999970751173777"
3) 1) "nanchong"
   2) 1) "106.04999810457229614"
      2) "30.47000092094743451"
4) 1) "zigong"
   2) 1) "104.46000069379806519"
      2) "29.21000117152468789"
127.0.0.1:6379> GEORADIUS china:city 104.04 30.39 https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt km withdist  #到中心的距离
1) 1) "chengdu"
   2) "0.0001"
2) 1) "suining"
   2) "125.1849"
3) 1) "nanchong"
   2) "192.9710"
4) 1) "zigong"
   2) "137.3643"
127.0.0.1:6379> GEORADIUS china:city 104.04 30.39 https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt km count 2  #获取两个
1) "chengdu"
2) "suining"

找出位于指定范围内的元素:GEORADIUSBYMEMBER key member radius 单位  #中心点是有给定的位置决定的

127.0.0.1:6379> GEORADIUSBYMEMBER china:city chengdu https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt km
1) "chengdu"
2) "suining"
3) "nanchong"
4) "zigong"

返回一个或多个位置元素的geohash:geohash key menmber1 menber2

将二维经纬度转换为以为的11位字符串,如果俩字符串越接近,距离越近

127.0.0.1:6379> geohash china:city chengdu suining
1) "wm3uzkrded0"
2) "wm6um5fnrc0"

Geo底层实现原理: Zset,我们可以使用Zset命令来操作Geo

比如

查看:zrange china:city 0 -1

 移除:zrem china:city zigong

127.0.0.1:6379> zrange china:city 0 -1
1) "huili"
2) "zigong"
3) "chengdu"
4) "suining"
5) "nanchong"
127.0.0.1:6379> zrem china:city zigong
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "huili"
2) "chengdu"
3) "suining"
4) "nanchong"

hyperloglog(基数统计) 

简介

基数?一个集合中不重复的元素个数

A{1,3,5,7,8,7}  基数=5         B{1,3,5,7,8}

hyperloglog是基数统计的算法,优点:占用内存是固定的,放2^64不同的元素的基数,只需要费12kb内存,所以如果从内存角度比较的,此为首选

网页的UV(页面访问量),一个人访问一个网站多次,算一个人

传统方式,可使用set存用户id,然后统计set元素数量即可,但数量大之后会比较麻烦,我们的目的本是为了计数,不是为了存id

所有命令以p开头的

添加元素 pfadd key 元素1 元素2 。。

统计元素(不重复):pfcount key

求并集:pfmerge 生成的新集合3 原集合1 原集合2

如果允许容错,那就可以使用hyoerloglog(有0.83%的错误率),如果不允许容错,就使用set吧或者其他

127.0.0.1:6379> pfadd key1 a b c d e f g h  #添加元素
(integer) 1
127.0.0.1:6379> pfcount key1
(integer) 8
127.0.0.1:6379> pfadd key2 g h i j k l m n
(integer) 1
127.0.0.1:6379> pfcount key2
(integer) 8
127.0.0.1:6379> pfmerge key3 key1 key2  #求并集
OK
127.0.0.1:6379> pfcount key3
(integer) 14

bitmap(位图)

 场景:统计用户信息,活跃不活跃,登录未登录,打卡未打卡等

bitmap位图,数据结构, 都是操作二进制来进行记录的,只有0和1 两个状态

比如,打卡场景

添加打卡记录:setbit key offset value

查看打卡记录:getbit key offset

统计打卡天数:bitcount key

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 0
(integer) 0 
127.0.0.1:6379> getbit sign 6   #查看记录
(integer) 0
127.0.0.1:6379> getbit sign 1
(integer) 0
127.0.0.1:6379> bitcount sign    #统计记录(1出现的次数)
(integer) 3

命令汇总 

geospatial 

1,添加地理位置:geoadd key 经度 纬度 member

2,获取指定城市的经纬度:geopos key member   

3,返回俩位置之间的距离:geodist key member1 member2 单位(km/m)   #

4,给定经纬度为中心,找某一般半径以内的地址:georadius key 经度 纬度 半径 单位(必须在当前集合中)

[WITHCOORD]:显示他人的定位信息

[COUNT count]:获取多少个

[WITHDIST]:显示到中心的距离

5,找出位于指定范围内的元素:GEORADIUSBYMEMBER key member radius 单位  #中心点是有给定的位置决定的

6,返回一个或多个位置元素的geohash:geohash key menmber1 menber2

将二维经纬度转换为以为的11位字符串,如果俩字符串越接近,距离越近

Geo底层实现原理: Zset,我们可以使用Zset命令来操作Geo

比如

查看:zrange china:city 0 -1

 移除:zrem china:city zigong

hyperloglog

所有命令以p开头的

添加元素 pfadd key 元素1 元素2 。。

统计元素(不重复):pfcount key

求并集:pfmerge 生成的新集合3 原集合1 原集合2

bitmap

添加打卡记录:setbit key offset value

查看打卡记录:getbit key offset

统计打卡天数:bitcount key

事务

redis事务本质:一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行

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

-------------队列  set  set  set------------------

redis事务没有隔离级别的概念!

所有命令在事务中,并没有被执行,只有发起了执行命令(exec)才会被一次性执行

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> exec    #执行事务
1) OK                   #此处可见,此时才把上面的所有命令挨个执行
2) OK
3) "v1"
4) OK

放弃事务 discard     (放弃了,事务中的所有命令都不会被执行)

127.0.0.1:6379> multi   #开启事务
OK
127.0.0.1:6379> set k4 v4   #设置K4
QUEUED
127.0.0.1:6379> discard   #放弃事务
OK
127.0.0.1:6379> get k4   #则k4就被放弃了,未执行(事务中的所有命令都不会被执行)
(nil)

编译型异常(代码有错,命令有错):这种情况会现场报错,如果还是执行,则事务中所有命令都不会被执行

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> getset k3   #此处明显命令错误,且现场报错
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec   #执行事务,就全部报错,所有命令都不会被执行
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get key3   
(nil)
127.0.0.1:6379> get key1
(nil)

运行时异常(类似java的1/0):如果事务队列中存在语法性错误,那么执行命令的时候,只错误的命令会抛出异常,其他命令正常执行(也就是上面说的单条语句保持原子性,事务不保证原子性)

127.0.0.1:6379> set key1 "v1"   #先设置一个string类型的key1
OK
127.0.0.1:6379> multi   #开启事务
OK
127.0.0.1:6379> incr key1   #对key1进行自增,命令没错,但key1是字符串,无法自增
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> mget k2 k3
QUEUED
127.0.0.1:6379> exec  #执行事务
1) (error) ERR value is not an integer or out of range   #发现只这条命令没执行成功,
2) OK                                                    #其他的都执行了
3) OK
4) 1) "v2"
   2) "v3"

监控 watch   (面试常问)使用watch,可以当作redis的乐观锁操作

悲观锁:

        悲观,认为什么时候都会出问题,无论做啥都加锁

乐观锁:

        乐观,认为什么时候都不会出问题,所以不会上锁,更新数据的时候判断,获取version,跟新的时候比较version

正常监控 watch

127.0.0.1:6379> set money 100   #钱包https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt
OK
127.0.0.1:6379> set out 0      #支出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

多线程修改值,watch的操作

#第一个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   #此时就相当于乐观锁的查询version
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


#开启第二个redis执行如下

127.0.0.1:6379> get money
"100"
127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> 


#返回第一个redis执行如下

127.0.0.1:6379> exec    #执行失败, 此时就相当于取验证version,则执行失败
(nil)

 执行失败之后,解锁 unwatch

127.0.0.1:6379> exec   #由于中途修改了数据,导致修改失败
(nil) 
127.0.0.1:6379> unwatch   #需要先解除锁
OK
127.0.0.1:6379> get money
"1000"
127.0.0.1:6379> get out
"0"
127.0.0.1:6379> watch money   #获取新的值,再重新加锁(select version)
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=200&pos_id=CQVHWhGt&pos_id=CQVHWhGt
QUEUED
127.0.0.1:6379> incrby out https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=200&pos_id=CQVHWhGt&pos_id=CQVHWhGt
QUEUED
127.0.0.1:6379> exec   #此时中途未改变数值,对比version未变,则可执行成功
1) (integer) 800
2) (integer) https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=200&pos_id=CQVHWhGt&pos_id=CQVHWhGt
127.0.0.1:6379> 

Jedis

要使用java来操作redis

常用API测试

1,项目创建

新建一个空项目新建modules

 注意统一   

2,导入依赖 

https://mvnrepository.com/

<dependencies>
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>5.2.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 -->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.53</version>
        </dependency>
    </dependencies>

3,测试连接

//本地连接   可直接连接测试
public static void main(String[] args) {
        Jedis jedis=new Jedis("127.0.0.1",6379);
        System.out.println(jedis.ping());
    }

//远程连接   需修改配置
public static void main(String[] args) {
        Jedis jedis=new Jedis("远程服务器地址",6379);
        jedis.auth("设置的密码");
        System.out.println(jedis.ping());
    }

修改远程配置xhmcopyredisconfig/redis.config

1,注销掉bind 127.0.0.1

2,添加密码访问

3,注意端口哦

  • 远程服务器端口:
firewall-cmd --list-ports :查看防火墙端口
firewall-cmd --zone=public --add-port=6379/tcp --permanent  开放防火墙端口
systemctl restart firewalld.service 重启防火墙
  • 阿里云防火墙安全组端口

4,命令测试(跟服务器上命令一样 string set list hash zset)

就跟上面再服务器上使用的命令一样:比如

包括特殊类型的也有 

事务测试 

public static void main(String[] args) {
        Jedis jedis = new Jedis(ConstantUtil.ID_ADDRESS, ConstantUtil.PORT);
        jedis.auth(ConstantUtil.PASSWORD);

        jedis.flushDB();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello", "world");
        jsonObject.put("happy", "new year");
        //开启事务
        Transaction multi = jedis.multi();
        String result = jsonObject.toString();

        try {
            multi.set("user1", result);
            multi.set("user2", result);
         /**
             * 没有这行代码时正常执行输出
             *{"happy":"new year","hello":"world"}
             * {"happy":"new year","hello":"world"}
             * 有这行代码之后,事务放弃执行输出
             * null
             * null
             */
            int i = 1 / 0;
            multi.exec();//执行事务
        } catch (Exception e) {
            multi.discard();//如果出错就放弃事务
            e.fillInStackTrace();
        } finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));

            jedis.close();//关闭连接
        }
    }

Springboot整合

Redis.cong详解

1,单位中大小写不敏感

======================包含INCLUDES=================================

类似于spring中得import或者jsp中得include标签,把其他配置文件配置过来

===============================网络NETWORK==========================

绑定得ip:bind 127.0.0.1

保护模式:protected-mode yes

端口设置 :port 6379

========================通用GENERAL ===========================

daemonize yes   #以守护进程得方式运行,默认是no,后续自己改成得yes

pidfile /var/run/redis_6379.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个

always-show-logo yes   #是否总是显示logo

==========================快照SNAPSHOTTING==========================

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

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

#如果900s内,如果至少有1个key进行了修改,我们即进行持久化操作
save 900 1

#如果300s内,如果至少有10个key进行了修改,我们即进行持久化操作
save 300 10

#如果60s内,如果至少有10000个key进行了修改,我们即进行持久化操作
save 60 10000

#我们之后会设置自己的参数

stop-writes-on-bgsave-error yes  #   持久化如果出错,是否还需要继续工作


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

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

dir ./    #rdb文件保存到目录 

 =========================REPLICATION复制,主从复制====================

======================SECURITY安全===============================

requirepass默认没有密码,直接ping就可一显示pong 

可以通过进入redis命令行设置    config set requirepass '123456'

此时如果直接ping则会需要验证,输入auth '123456' 然后才可以ping

====================== CLIENTS客户端============================

# maxclients 10000  设置可以连接的最大客户端数

 ========================MEMORY MANAGEMENT 内存===================

# maxmemory <bytes>     #    redis配置最大的内存容量
 

# maxmemory-policy noeviction    #    内存达到上限的处理策略

  maxmemory-policy noeviction 内存淘汰策略

  1. noeviction: (永不过期返回错误)这是默认策略,当内存不足以容纳更多数据时,新的写入操作会报错。
  2. allkeys-lru: (删除lru算法的key)当内存不足时,根据最近最少使用 (Least Recently Used, LRU) 算法删除任何可能的键。
  3. volatile-lru: 在内存不足时,根据 LRU 算法删除设置了过期时间的键。
  4. allkeys-random: 在内存不足时,随机删除任何可能的键。
  5. volatile-random: 在内存不足时,随机删除设置了过期时间的键。
  6. volatile-ttl: 在内存不足时,首选删除 TTL(Time To Live) 值较小的键,即选择剩余过期时间最短的键进行删除。

====================== APPEND ONLY MODE 模式  aof配置===============

appendonly no   #   默认不开启aof模式   默认是使用rdb持久化的,在大部分所有的情况下,rdb完全够用了

appendfilename "appendonly.aof"  #持久化文件的名字,,rdb的文件是后缀.rdb文件

# appendfsync always   #每次修修改都会同步sync   速度慢  消耗性能
appendfsync everysec     #每秒执行一次同步sync   可能会丢失这一秒的数据
# appendfsync no    #不执行同步sync  这个时候操作系统自己同步数据,速度是最快的

Redis持久化*****

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

RDB(Redis Database)

 rdb保存的文件是dump.rdb   都是在配置文件中的快照( SNAPSHOTTING)中进行配置的

测试案例

1,默认情况下,先设置一个key值,然后关机,重新开启之后,数据则没有了

127.0.0.1:6379> set key v1    #  设置k   v
OK
127.0.0.1:6379> SHUTDOWN   # 关机
not connected> exit   # 退出

[root@xhm bin]# redis-server xhmcopyredisconfig/redis.conf   #重新启动

[root@xhm bin]# redis-cli -p 6379   #客户端进入

127.0.0.1:6379> get key1    # 获取值
(nil)     #不存在

2,配置持久化    60s 改变5个key则保存一次 ,如果没有dump.rdb文件则会生成dump.rdb文件

 #   设置值
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379>  set k3 v3
OK
127.0.0.1:6379> set k4 v4 
OK
127.0.0.1:6379> set k5 v5
OK

#关掉
127.0.0.1:6379> SHUTDOWN
not connected> exit

#重新启动
[root@xhm bin]# redis-server xhmcopyredisconfig/redis.conf 
537844:C 09 Jan 2025 16:53:20.932 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
537844:C 09 Jan 2025 16:53:20.932 # Redis version=6.0.4, bits=64, commit=00000000, modified=0, pid=537844, just started
537844:C 09 Jan 2025 16:53:20.932 # Configuration loaded
[root@xhm bin]# redis-cli -p 6379

#获取值依旧存在
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> 

 dump.rdb

触发规则

1,save的规则满足的情况下,会自动触发rdb规则,生成dump.rdb文件

2,执行flashall命令,也会触发rdb规则,产生文件

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

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

如何恢复rdb文件

 1,只需要将rdb文件放在我们的redis启动目录就可以了,redis启动的时候会自动检查dump.rdb恢复其中的数据

2,查看需要存放的位置

127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/bin"   #如果在这个目录下存在dump.rdb文件,启动就会在自动回复其中的数据

rdb优缺点

优点:

1,适合大规模得到数据恢复

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

缺点:

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

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

AOF(Append Only File) 

Aof保存得是appendonly.aof文件 都是在配置文件中的APPEND ONLY MODE中进行配置的

将我们的所有命令都记录下来(以日志的形式记录),相当于history,恢复的时候就把这个文件全部在执行一遍

appendonly no   #   默认不开启aof模式   默认是使用rdb持久化的,在大部分所有的情况下,rdb完全够用了

appendfilename "appendonly.aof"  #持久化文件的名字,,rdb的文件是后缀.rdb文件

# appendfsync always   #每次修修改都会同步sync   速度慢  消耗性能
appendfsync everysec     #每秒执行一次同步sync   可能会丢失这一秒的数据
# appendfsync no    #不执行同步sync  这个时候操作系统自己同步数据,速度是最快的

把appendonly no  改为yes就开启了,重启生效

 操作测试

#  生成了appendonly.aof文件之后,重启操作如下
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> set k3 v3

 查看这个文件appendonly.aof里面,就可以看到记录

如何恢复

如果aof文件(比如我手动去改了)有问题,redis是启动不了的 

此时就可以用检测文件(redis-check-aof --fix)去修复aof文件

破坏 appendonly.aof文件

修复appendonly.aof文件

[root@xhm bin]# redis-check-aof --fix appendonly.aof    #输入此命令即可修复文件
0x              6a: Expected \r\n, got: 6764
AOF analyzed: size=122, ok_up_to=81, diff=41
This will shrink the AOF from 122 bytes, with 41 bytes, to 81 bytes
Continue? [y/N]: y   #y继续
Successfully truncated AOF    #修复成功

重写规则说明   了解

aof默认的是文件的无限追加,文件会越来越大 

如果aof文件大于64m,太大了,fork一个新的进程来将我们的文件进行重写

优缺点

# appendfsync always   #每次修修改都会同步sync   速度慢  消耗性能
appendfsync everysec     #每秒执行一次同步sync   可能会丢失这一秒的数据
# appendfsync no    #不执行同步sync  这个时候操作系统自己同步数据,速度是最快的

优点:

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

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

3,从不同步,效率最高

缺点 :

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

2,aof运行效率也比rdb慢,所以redis默认的配置是使用rdb持久化

Redis订阅发布

简介

redis发布/订阅(pus/sub)是一种消息通信模式,发送则发送消息,订阅者接受消息

三个角色:发送者,频道,订阅者

 频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:

当有新消息通过 publish 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

订阅/发布命令

#连接一个cli客户端,订阅xhm频道
[root@xhm bin]# redis-cli -p 3679
127.0.0.1:6379> SUBSCRIBE xhm
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "xhm"
3) (integer) 1
#等待读取推送信息
1) "message"        #消息
2) "xhm"            #哪个频道的消息
3) "hello,world"    #消息的具体内容
1) "message"
2) "xhm"
3) "goodjob"

#再连接一个cli客户端,发送信息,这边发送之后,订阅端就有消息了
[root@xhm bin]# redis-cli -p 3679
#发布者发布消息到xhm频道
127.0.0.1:6379> publish xhm "hello,world"
(integer) 1
127.0.0.1:6379> publish xhm "goodjob"
(integer) 1

原理

底层通过字典实现:pubsub_channels是一个字典类型,保存订阅频道的信息:字典的key为订阅的频道, 字典的value是一个链表, 链表中保存了所有订阅该频道的客户端

频道订阅:订阅频道时先检查字段内部是否存在;不存在则为当前频道创建一个字典且创建一个链表存储客户端id;否则直接将客户端id插入到链表中。

取消频道订阅:取消时将客户端id从对应的链表中删除;如果删除之后链表已经是空链表了,则将会把这个频道从字典中删除。

发布:首先根据 channel 定位到字典的键, 然后将信息发送给字典值链表中的所有客户端

 使用场景

1,实时消息系统

2,实时聊天室

3,订阅,关注系统

稍微复杂的就会使用消息中间件来做(mq,kafka)

Redis主从复制

将一台redis服务(主机:master),复制到另一台redis服务(从机:slave);数据的复制是单向的,只能由主节点到从节点,master以写为主,slave以读为主

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

且一个主节点可以有多个从节点或者没有从节点,但一个从从节点只能有一个主节点

主从复制的主要作用:

1,数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式

2,故障恢复:当主节点出现问题,可以由从节点提供服务,实现快速的故障恢复,实际上一种服务的冗余

3,负载均衡:读写分离,可以由主机欸但提供写,从节点提供读,分担服务器的负载,尤其是在写少读多的场景下,通过多个节点分担读负载,可以大大提高redis服务器的并发量

4,高可用(集群)基石:是哨兵和集群实现的基础

一台redis运用项目缺点:

结构上,单个redis会发生单点故障,且一台服务器需要处理的请求负载,压力较大

容器上,单个redis内存有限;单台redis最大使用内存不应该超过20G

 环境配置

先打开redis查看一下主从信息

127.0.0.1:6379> info replication
# Replication    
role:master       #角色   master       
connected_slaves:0    #没有从机
master_replid:04d0aaa661e0be0b5a8903a5feebd3170acc56e9
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

环境搭建配置,一个服务器上搭建3个redis服务,修改出3个redis.config,分别启动

#分别复制三个配置文件出来
[root@xhm xhmcopyredisconfig]# cp redis.conf redis79.conf 
[root@xhm xhmcopyredisconfig]# cp redis.conf redis80.conf 
[root@xhm xhmcopyredisconfig]# cp redis.conf redis81.conf 
[root@xhm xhmcopyredisconfig]# ls
redis79.conf  redis80.conf  redis81.conf  redis.conf

三个配置文件做对应的端口,以及生成文件名的修改 

 

 修改完毕之后,启动三个redis服务,通过进程信息查看如下

#分别打开之后,查看进程
[root@xhm xhmcopyredisconfig]# ps -ef|grep redis
root      602227       1  0 15:13 ?        00:00:00 redis-server *:6379
root      602238       1  0 15:14 ?        00:00:00 redis-server *:6380
root      602266       1  0 15:14 ?        00:00:00 redis-server *:6381
root      602273  602138  0 15:14 pts/1    00:00:00 grep --color=auto redis

 一主二从

默认情况下,每台redis都是主节点,我们默认配置从机即可

设置主79   从 80   81

1,此处使用命令修改主从,是暂时的

#设置主从

127.0.0.1:6380> slaveof 127.0.0.1 6379

#查看主从信息

127.0.0.1:6380> info replication

#没有配置之前,80端口的redis信息
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:0
master_replid:069203c0cdf19f925c9a4f07ef424c46d7a27644
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

#配置从机,主机ip 和端口如下
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_repl_offset:1
master_link_down_since_seconds:1736925662
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:069203c0cdf19f925c9a4f07ef424c46d7a27644
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
127.0.0.1:6380> 


#回到79端口的服务查看信息
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=28,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=28,lag=0
master_replid:4368cc041b0daa733bd4956f438cc1d54055f87b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:42
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:42

问题:

        此处可能存在的问题:80和81端配置了从机,但从79上看不到从机连接

         80和81的master_link_status显示为down

        79的connected_slaves:0

原因:

        因是在redis.conf文件中指定了自己的密码,在进行主从复制时,主机master会要求密码验证。

解决:

        1、将redis.conf配置文件中的密码部分requirepass注掉。

        2、如需保留密码:配置从机的masterauth。

​                 修改从机配置文件,在配置文件末尾加上:masterauth 密码。杀掉进程重启生效

2,直接修改配置,永久的

 如下图配置好了之后,此服务开启即从机

3,主从细节

主机负责写,从机负责读!主机中的所有信息和数据,都会自动被从机保存(即你在主机写了东西后,去从机可查看),从机如果去写,会报错(不可写)

默认情况下,如果主机断开之后,但从机没有改配置,仍旧是从机,无法写,主机如果又回来了,仍旧连接到主机,可获取主机写的东西

命令行配置的主从,如果从机断掉,从机重启会变回主机,此时如果再连上主机,主机之前的数据也会获取回来

4,复制原理

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

master接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据的命令,再后台进程执行完毕之后,master将传送整个数据文件到slave,并完成一次完全同步

全量复制:slave服务再接收到数据库文件数据之后,将其存盘并加载到内存中(只要是重新连接master,就会自动执行一次全量复制,将之前的数据全部复制到从机)

增量复制:master后续继续写的新的,也依次同步给slave

解除主从,自己当老大

127.0.0.1:6380> SLAVEOF NO ONE

如果是层层链路主从,则从机1 变主机后,从机2就跟从机1了,就算原主机回来了也是跟从机1

哨兵模式

概念

redis哨兵模式是一种用于确保redis集群高可用的机制,他通过一组专门的进程(哨兵进程)来监督主节点和从节点的状态,并在检测到故障的时候自动进行故障转移

核心组件和功能

1,监控:哨兵进程会周期性的向主节点或从节点发送ping命令来检测他们的状态

2,提醒:当哨兵检测到主节点不可用时,他会通过投票机制决定是否执行故障迁移

3,自动故障转移:一旦决定进行故障迁移,哨兵会选择一个节点晋升为主节点,并通过消息订阅模式通知其他从节点切换到新的主节点,并修改配置文件,从而实现故障转移

工作机制

哨兵进程使用流言协议来讲二手关于主服务器是否下线的信息,并通过投票协议来决定是否执行古装迁移,以及选择哪个服务器作为新的主服务器,这种机制确保了决策的可靠性和一致性

如上图所示一个哨兵对服务器进行监控,仍旧可能会出现问题,所以有了多哨兵模式

多哨兵模式,各个哨兵不仅对服务器进行监视,哨兵之间还会相互监视

此时故障切换的过程:如果主服务器宕机,哨兵1检测到这个结果,系统不会马上进行faliover过程,仅仅哨兵1主管认为服务器不可用,这个现象叫主观下线。当后面更多的哨兵也检测到主服务器不可用,并且达到一定的值时,哨兵之间就会进行一次投票,投票结果有一个哨兵发起,进行failover操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从机切换为主机,这叫客观下线

测试流程

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

#sentinel monitor 被监控的名称 host post 1
#后面这个1,代表主机挂了,从机投票让谁接替成为主机

sentinel monitor myredis 127.0.0.1 6379 1

2,启动哨兵 

#启动命令     redis-sentinel xhmcopyredisconfig/sentinel.conf 

[root@xhm bin]# redis-sentinel xhmcopyredisconfig/sentinel.conf 
614528:X 16 Jan 2025 17:28:28.548 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
614528:X 16 Jan 2025 17:28:28.548 # Redis version=6.0.4, bits=64, commit=00000000, modified=0, pid=614528, just started
614528:X 16 Jan 2025 17:28:28.548 # Configuration loaded
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 6.0.4 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
 |    `-._   `._    /     _.-'    |     PID: 614528
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

614528:X 16 Jan 2025 17:28:28.552 # Sentinel ID is 7f9f3085b79920f334345bb882438e5459d1021c
614528:X 16 Jan 2025 17:28:28.552 # +monitor master myredis 127.0.0.1 6379 quorum 1
614528:X 16 Jan 2025 17:28:28.553 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
614528:X 16 Jan 2025 17:28:28.555 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379

测试让主机宕掉 ,关掉主机,当哨兵会发送一些心跳包机制,检测到主机连不上了,就会有如下信息

如果主节点断掉了,就会从从机中投票一个出来当主机

后续如果主机又回来了,被检测到之后,会被归并为80的从机,不会再回归主机,而是自动归为从机

优缺点 

优点:

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

2,主从可以切换,故障可以转移,系统可用性就会更好

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

缺点:

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

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

# Example sentinel.conf
 
# 哨兵sentinel实例运行的端口 默认26379   集群,需加端口配置
port 26379
 
# 哨兵sentinel的工作目录
dir /tmp
 
# 哨兵sentinel监控的redis主节点的 ip port 
# master-name  可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 1
 
# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
 
 
# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000
 
# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,
#这个数字越小,完成failover所需的时间就越长,
#但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
#可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1
 
 
 
# 故障转移的超时时间 failover-timeout 可以用在以下这些方面: 
#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
#3.当想要取消一个正在进行的failover所需要的时间。  
#4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
# 默认三分钟
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000
 
# SCRIPTS EXECUTION
 
#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
#对于脚本的运行结果有以下规则:
#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
#若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
#一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
 
#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,
#这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,
#一个是事件的类型,
#一个是事件的描述。
#如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
#通知脚本
# sentinel notification-script <master-name> <script-path>
  sentinel notification-script mymaster /var/redis/notify.sh
 
# 客户端重新配置主节点参数脚本
# 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
# 以下参数将会在调用脚本时传给脚本:
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
# 目前<state>总是“failover”,
# <role>是“leader”或者“observer”中的一个。 
# 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

Redis缓存穿透和雪崩

学习视频:【狂神说Java】Redis最新超详细版教程通俗易懂_哔哩哔哩_bilibili