当前位置: 首页 > news >正文

Redis的入门与应用

Redis概述_什么是NoSQL

img

什么是NoSQL

NoSQL(NoSQL = Not Only SQL),意即“不仅仅是SQL”,泛指非关系型的数据库。随着互联网web2.0网站的兴起,传统的关系数据库在应付特别是超大规模和高并发类型纯动态网站已经显得力不从心,暴露了很多难以克服的问题。

结构化数据和非结构化数据

img

  • 结构化数据指的是由二维表结构来逻辑表达和实现的数据,严格遵循数据格式与长度规范,也称作为行数据。
  • 非结构化数据,指的是数据结构不规则或不完整,没有任何预定义的数据模型,不方便用二维逻辑表来表现的数据,例如办公文档(Word)、文本、图片、HTML、各类报表、视频音频等。

NoSQL的四大分类

image-20211216164502292

KV型NoSql(代表----Redis)

KV型NoSql顾名思义就是以键值对形式存储的非关系型数据库,是最简单、最容易理解也是大家最熟悉的一种NoSql,因此比较快地带过。

特点:

  • 数据基于内存,读写效率高
  • KV型数据,时间复杂度为O(1),查询速度快

注意:

KV型NoSql最大的优点就是高性能,利用Redis自带的BenchMark做基准测试,TPS可达到10万的级别,性能非常强劲。

列式NoSql(代表----HBase)

列式NoSql,大数据时代最具代表性的技术之一了,以HBase为代表。

关系行数据库数据

img

注意:

看到每行有name、phone、address三个字段,这是行式存储的方式,且可以观察id = 2的这条数据,即使phone字段没有,它也是占空间的。

列式数据库数据

img

img

注意:

  • 查询时只有指定的列会被读取,不会读取所有列
  • 列数据被组织到一起,一次磁盘IO可以将一列数据一次性读取到内存中
文档型NoSql(代表----MongoDB)

什么是文档型NoSql呢,文档型NoSql指的是将半结构化数据存储为文档的一种NoSql,文档型NoSql通常以JSON或者XML格式存储数据。

img

注意:

关系型数据库是按部就班地每个字段一列存,在MongDB里面就是一个JSON字符串存储。

搜索型NoSql(代表----ElasticSearch)

传统关系型数据库主要通过索引来达到快速查询的目的,但是在全文搜索的场景下,索引是无能为力的,like查询一来无法满足所有模糊匹配需求,二来使用限制太大且使用不当容易造成慢查询,搜索型NoSql的诞生正是为了解决关系型数据库全文搜索能力较弱的问题,ElasticSearch是搜索型NoSql的代表产品。

img

关系型数据库和非关系型数据及其区别

关系型数据库

img

关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织 优点:

  • 易于维护:都是使用表结构,格式一致;
  • 使用方便:SQL语言通用,可用于复杂查询;
  • 复杂操作:支持SQL,可用于一个表以及多个表之间非常复杂的查询。

缺点:

  • 读写性能比较差,尤其是海量数据的高效率读写;
  • 固定的表结构,灵活度稍欠;
非关系型数据库

img

优点:

  • 格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型。
  • 速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
  • 高扩展性;
  • 成本低:nosql数据库部署简单,基本都是开源软件。

缺点:

  • 不提供sql支持,学习和使用成本较高;

  • 无事务处理;

  • 数据结构相对复杂,复杂查询方面稍欠。

实时学习反馈

1. 为什么使用NoSQL技术说法正确的是_____。

A 架构安全性

B 解决数据量大,种类繁多出现性能问题

C 项目可靠性

D 项目稳定性

2. 如下针对NoSQL特点不正确的是_____。

A 易拓展

B 大数据量高性能

C 多样灵活的数据模型

D 支持事务

答案

1=>B 2=>D

Redis概述_为什么要用NoSQL

image-20211216144753973

单机MySql的美好年代

在90年代,一个网站的访问量一般都不大,用单个数据库完全可以轻松应付。在那个时候,更多的都是静态网页,动态交互类型的网站不多。

image-20211210150346390

遇到问题:

随着用户数的增长,Tomcat和数据库之间竞争资源,单机性能不足以支撑业务。

Tomcat与数据库分开部署

Tomcat和数据库分别独占服务器资源,显著提高两者各自性能。

image-20211213143423727

新的问题:

随着用户数的增长,并发读写数据库成为瓶颈。

引入本地缓存和分布式缓存

通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。其中涉及的技术包括:使用memcached作为本地缓存,使用Redis作为分布式缓存。

image-20211216145450698

注意:

缓存抗住了大部分的访问请求,随着用户数的增长,并发压力主要落在单机的Tomcat上,响应逐渐变慢。

引入反向代理实现负载均衡

在多台服务器上分别部署Tomcat,使用反向代理软件(Nginx)把请求均匀分发到每个Tomcat中。

image-20211213144000910

新的挑战:

反向代理使应用服务器可支持的并发量大大增加,但并发量的增长也意味着更多请求穿透到数据库,单机的数据库最终成为瓶颈。

数据库读写分离

由于数据库的写入压力增加,Memcached只能缓解数据库的读取压力。读写集中在一个数据库上让数据库不堪重负,大部分网站开始使用主从复制技术来达到读写分离,以提高读写性能和读库的可扩展性。Mysql的master-slave模式成为这个时候的网站标配了。

image-20211213144547424

新的挑战:

业务逐渐变多,不同业务之间的访问量差距较大,不同业务直接竞争数据库,相互影响性能。

数据库按业务分库

把不同业务的数据保存到不同的数据库中,使业务之间的资源竞争降低,对于访问量大的业务,可以部署更多的服务器来支撑。

image-20211213145030832

为什么用NoSQL

用户的个人信息,社交网络,地理位置,用户生成的数据和用户操作日志已经成倍的增加。我们如果要对这些用户数据进行挖掘,那SQL数据库已经不适合这些应用了, NoSQL数据库的发展也却能很好的处理这些大的数据。

实时学习反馈

1. 单机Mysql的架构,随着用户数的增长,并发读写数据库成为瓶颈如何解决。

A 引入本地缓存和分布式缓存

B 引入反向代理实现负载均衡

C 引入NoSQL数据库

D 数据库读写分离

2. 单机Tomcat压力大,响应逐渐变慢如何解决。

A 引入本地缓存和分布式缓存

B 引入反向代理实现负载均衡

C 引入NoSQL数据库

D 数据库读写分离

答案

1=>A 2=>B

Redis概述_Redis是什么

image-20211213174107060

Redis是什么

Redis 是由 Salvatore Sanfilippo 用C语言开发的一款开源的、高性能的键值对存储数据库,它采用 BSD 协议,为了适应不同场景下的存储需求,提供了多种键值数据类型。到目前为止,Redis 支持的键值数据类型有字符串、列表、有序集合、散列及集合等。

官网:Redis官网

Redis的优缺点

优点缺点
数据存取速度快存储容量受限
多种数据结构支持磁盘存储成本相对较高
高并发处理能力数据一致性需要应用程序保证
数据存储在内存中,读写速度极快访问安全性需要应用程序保证
使用方便,支持多种语言集群配置建设和维护相对复杂

Redis 的特性

成功的人各有所长,一个成功的人必然是在某个领域比较成功的,而不可能面面俱到。NoSQL 数据库也是一样的,一款成功的 NoSQL 数据库必然特别适合某些业务领域。

  • 支持多种计算机编程语言,如 Java、C、C++、Python、PHP、Lua、Ruby、Node.js、C#、GoLand 等。
  • 具有丰富的数据类型,如 String、List、Set、Hash、Sorted Set 等。
  • 读/写速度快,性能高。(Redis 能读的速度是 110 000次/s,写的速度是 81 000次/s)
  • 支持持久化
  • 简单且功能强大
  • 实现分布式集群和高可用

Redis 的使用场景

Redis 是一款功能强大的数据库,在实际应用中,不管是什么架构的网站或系统,我们都可以将 Redis 引入项目,这样就可以解决很多关系型数据库无法解决的问题。

  • 做缓存
  • 做计数器应用
  • 实现消息队列系统
  • 做实时系统、消息系统
  • 实现排行榜应用
  • 做数据过期处理
  • 做大型社交网络

实时学习反馈

1. Redis是____

A 分布式缓存

B 单机键值数据库

C 关系型数据库

D 高性能数据库

2. Redis采用的是_____的存储形式。

A 键值对

B 二维表

C 一维表

D 磁盘

答案

1=>A 2=>A

Redis安装_Linux下安装Redis

img

下载地址

Redis官方网址:Redis - The Real-time Data Platform

下载Redis

image-20240123101836379

redis-7.2.4.tar.gz上传至CentOS并解压,解压后得到redis-6.2.4目录

解压命令:

tar -zxvf redis-7.2.4.tar.gz

安装GCC

安装C语言编译环境

yum install -y gcc

通过使用gcc --version命令打印 GCC 版本,来验证 GCC 编译器是否被成功安装:

gcc --version

安装Redis

编译Redis

在redis-7.2.4目录下执行:

make

image-20211213142328522

安装Redis

在redis-6.2.4目录下执行:

make install

image-20211213142619364

安装目录: /usr/local/bin

image-20210628170407557

注意:

redis-benchmark:Redis自带的基准性能测试工具

redis-check-aof:对有问题的 AOF 文件进行修复,AOF和RDB文件后面会说明

redis-check-rdb:对有问题的 RDB文件进行修复

redis-sentinel:Redis集群使用

redis-cli:客户端

redis-server:服务器启动

服务启动

前台启动:/usr/local/bin下执行

./redis-server

后台启动

修改redis.conf文件

daemonize yes #由no改为yes

启动服务

./redis-server ../redis.conf

客户端启动

/usr/local/bin下执行

./redis-cli

ping命令可以检测服务器是否正常(服务器返回PONG)

127.0.0.1:6379> ping

PONG

Redis安装_Docker下安装Redis

image-20211213175050730

下载最新Redis镜像

docker pull redis

注意:

可以用docker pull redis命令下载最新版本的Redis镜像,也可 以用“docker pull redis:标签”命令下载指定版本的Redis。

启动Redis容器

docker run -it -d --name myFirstRedis -p 6379:6379

redis:latest

观察Redis启动效果

docker logs myFirstRedis

注意:

如果直接在Linux等环境上启动Redis服务器,就能直接看到启动后的效果。

查看Redis的版本

先确保myFirstRedis容器处于Up状态。进入容器的命令行交互窗口。

docker exec -it myFirstRedis /bin/bash

 

redis-server --version

Redis服务器和客户端

Redis是基于键值对存储的NoSQL数据库,其中的数据是存储在 Redis服务器里的。和传统的MySQL数据库服务器相似,一个Redis服务器可以同多个客户端创建连接。

docker exec -it myFirstRedis /bin/bash

 

redis-cli

Redis安装_基础知识

image-20211216141705062

默认16数据库

Redis是一个字典结构的存储服务器,一个Redis实例提供了多个用来存储数据的字典,客户端可以指定将数据存储在哪个字典中。

这与在一个关系数据库实例中可以创建多个数据库类似(如下图所示),所以可以将其中的每个字典都理解成一个独立的数据库。

image-20211215101050456

Redis默认支持16个数据库,可以通过调整Redis的配置文件redis/redis.conf中的databases来修改这一个值,设置完毕后重启Redis便完成配置。

image-20211215102103246

Redis 使用的到底是多线程还是单线程?

因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。

IO多路复用技术

image-20211215104413902

Redis 采用网络IO多路复用技术来保证在多连接的时候, 系统的高吞吐量。

大白话解释

假设你是一个机场的空管, 你需要管理到你机场的所有的航线, 包括进港,出港, 有些航班需要放到停机坪等待,有些航班需要去登机口接乘客。

image-20211215104604006

最简单的做法,就是你去招一大批空管员,然后每人盯一架飞机, 从进港,接客,排位,出港,航线监控,直至交接给下一个空港,全程监控。

遇到的问题:

  • 很快你就发现空管塔里面聚集起来一大票的空管员,交通稍微繁忙一点,新的空管员就已经挤不进来了。
  • 空管员之间需要协调,屋子里面就1, 2个人的时候还好,几十号人以后 ,基本上就成菜市场了。
  • 空管员经常需要更新一些公用的东西,比如起飞显示屏,比如下一个小时后的出港排期,最后你会很惊奇的发现,每个人的时间最后都花在了抢这些资源上。

怎么解决

这个东西叫flight progress strip. 每一个块代表一个航班,不同的槽代表不同的状态,然后一个空管员可以管理一组这样的块(一组航班),而他的工作,就是在航班信息有新的更新的时候,把对应的块放到不同的槽子里面。

结论

img

这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),且Redis在内存中操作数据的速度非常快(内存内的操作不会成为这里的性能瓶颈),主要以上两点造就了Redis具有很高的吞吐量。

切换数据库

语法结构:

select number

示例:

# 默认使用 0 号数据库
redis 127.0.0.1:6379> SET db_number 0
OK
# 使用 1 号数据库
redis 127.0.0.1:6379> SELECT 1
OK

清空当前库

Redis Flushdb 命令用于清空当前数据库中的所有 key。

语法结构:

127.0.0.1:6379> FLUSHDB

示例:

127.0.0.1:6379> FLUSHDB

通杀全部库

Redis Flushall 命令用于清空整个 Redis 服务器的数据(删除所有数据库的所有 key )。

语法结构:

redis 127.0.0.1:6379> FLUSHALL

示例:

# 清空所有数据库的所有 key
redis 127.0.0.1:6379>flushall
OK

为什么默认端口6379

意大利的一位广告女郎名字叫Merz全名Alessia Merz。

image-20210701165421318

6379 = Merz

image-20210701105257599

实时学习反馈

1. Redis采用____模型。

A 阻塞IO

B 异步非阻塞IO

C 同步非阻塞IO

D 多路I/O复用

2.Redis默认_____数据库。

A 10

B 15

C 16

D 20

答案

1=>D 2=>C

Redis数据类型_String

img

简介

String是Redis最基本的类型,一个key对应一个value。String是二进制安全的,意味着String可以包含任何数据,比如序列化对象或者一张图片。String最多可以放512M的数据。

常用命令

set

用于设置给定 key 的值。如果 key 已经存储其他值, set 就重写旧值,且无视类型。

语法格式:

set key value

示例:

127.0.0.1:6379> set k1 v1
OK
get

用于获取指定 key 的值。如果 key 不存在,返回 nil 。

语法格式:

get key

示例:

127.0.0.1:6379> get k1
"v1"
append

将给定的value追加到key原值末尾。

语法格式:

append key value

示例:

127.0.0.1:6379> APPEND k1 k1
(integer) 4
127.0.0.1:6379> APPEND k1 k2
(integer) 6

注意:

  • 如果 key 已经存在并且是一个字符串, append 命令将 value 追加到 key 原来的值的末尾。
  • 如果 key 不存在, append 就简单地将给定 key 设为 value ,就像执行 set key value 一样。
strlen

获取指定 key 所储存的字符串值的长度。当 key 储存的不是字符串值时,返回一个错误。

语法格式:

strlen key

示例:

127.0.0.1:6379> strlen k1
(integer) 6
setex

给指定的 key 设置值及time 秒的过期时间。如果 key 已经存在, setex命令将会替换旧的值,并设置过期时间。

语法格式:

setex key time value

示例:

#向Redis中设置一个k1的键值对并且10秒后过期
127.0.0.1:6379> setex k1 10 v1
OK
setnx

只有在key不存在时设置key的值

语法格式:

setnx key value

示例:

127.0.0.1:6379> setnx k1 v1
(integer) 0
127.0.0.1:6379> setnx k4 v4
(integer) 1
getrange

获取指定区间范围内的值,类似between........and 的关系

语法格式:

getrange key start end

示例:

127.0.0.1:6379> set k5 abcd123xxx
OK
127.0.0.1:6379> getrange k5 2 4
"cd1"
setrange

指定的字符串覆盖给定 key 所储存的字符串值,覆盖的位置从偏移量 offset 开始。

语法结构:

setrange key offset value

示例:

127.0.0.1:6379> set k6 abcd1234
OK
127.0.0.1:6379> setrange k6 1 xxx
(integer) 8
127.0.0.1:6379> get k6
"axxx1234"
incr

将 key 中储存的数字值增一。

语法格式:

incr key

示例:

#因为Redis中不存在k1,所以先初始化为0,再递增,值为1
127.0.0.1:6379> incr k1
(integer) 1
# incr k1 存在k1,递增后k1的值为2
127.0.0.1:6379> incr k1
(integer) 2
# 如果value不是数字就会报错
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> INCR k2
(error) ERR value is not an integer or out of range

注意:

  • 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 incr 操作。
  • 如字符串类型的值不能表示为数字、或者是其他类型,那么返回一个错误。
decr

将 key 中储存的数字值减一。

语法格式:

decr key

示例:

127.0.0.1:6379> decr k1
(integer) 1
127.0.0.1:6379> decr k1
(integer) 0
127.0.0.1:6379> decr k1
(integer) -1
127.0.0.1:6379> decr k1
(integer) -2
#如果
set k2 v2
decr k2 因为k2不为数值,Redis返回一个错误

注意:

  • 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 decr 操作。
  • 如字符串类型的值不能表示为数字、或者是其他类型,那么返回一个错误。
incrby/decrby key step

将key存储的数字值按照step进行增减。

127.0.0.1:6379> incrby k1 10
(integer) 20

注意:

  • 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 incrby/decrby 命令。
  • 如字符串类型的值不能表示为数字、或者是其他类型,那么返回一个错误。
mset

同时设置一个或多个 key-value 。

语法格式:

mset key1 value1 key2 value2

示例:

127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
mget

返回所有(一个或多个)给定 key 的值。

语法格式:

mget key1 key2

示例:

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

注意:

如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil 。

getset

将给定key值设为value,并返回key的旧值(old value),简单一句话(先get然后立即set)。

语法格式:

getset key value

示例:

127.0.0.1:6379> getset k1 wcc
"v1"
127.0.0.1:6379> get k1
"wcc"

使用场景

value 除了是字符串以外还可以是数字。

  • 计数器
  • 统计多单位的数量
  • 粉丝数
  • 对象缓存存储
  • 分布式锁

实时学习反馈

1.Redis技术String数据类型中如何设置给定 key 的值。

get

append

set

mget

2. Redis技术String数据类型中如何只有在key不存在时设置key的值。

get

append

setnx

mget

答案

1=>C 2=>C

Redis数据类型_常用命令

img

keys

查看当前库中所有的key 。

语法结构:

keys *

通配符

  • *: 通配任意多个字符
  • ?: 通配单个字符

示例:

keys *

exists

判断某个key是否存在,返回1表示存在,0不存在。

语法结构:

exists key

示例:

#查看k1是否存在,如果存在返回1
exists k1
# 查看k1 k2 k3是否存在,如果k1 k2存在,k3不存在,则返回2
exists k1 k2 k3

注意:

可以设置多个key,只返回存在的个数,但不返回哪一个存在/不存在。

type

查看当前key 所储存的值的类型。返回当前key所储存的值的类型,如string 、list等。

语法结构:

type key

示例:

type k1

del

删除已存在的key,不存在的 key 会被忽略。

语法结构:

del key

示例:

可以设置多个key,返回删除成功的个数。

# 删除k1,如果成功返回1,失败返回0
del k1
# 删除k1 k2 k3,如果k1 k2存在,k3不存在,则返回2
del k1 k2 k3

expire

给key设置time秒的过期时间。设置成功返回 1 。 当 key 不存在返回 0。

语法结构:

expire key time

示例:

# 给k1设置10秒后过期
expire k1 10

ttl

以秒为单位返回 key 的剩余过期时间。

语法结构:

ttl key

示例:

ttl k1

注意:

当 key 不存在时,返回 -2 。 当 key 存在但没有设置剩余生存时间时,返回 -1 。 否则,以秒为单位,返回 key 的剩余生存时间。

persist

移除给定 key 的过期时间,使得 key 永不过期。

语法结构:

persist key

示例:

persist k1

注意:

当过期时间移除成功时,返回 1 。 如果 key 不存在或 key 没有设置过期时间,返回 0 。

实时学习反馈

1. Redis技术中查看当前库中所有的key的命令_____。

exists

type

keys

ttl

2. Redis技术中如何删除已存在的key。

keys

del

ttl

persist

答案

1=>C 2=>B

Redis数据类型_List

image-20211214143205878

简介

List是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。底层是一个双向链表,对两段操作性能极高,通过索引操作中间的节点性能较差。

未命名文件 (24)

一个List最多可以包含 $2^{32}-1$个元素 ( 每个列表超过40亿个元素)。

常用命令

lpush/rpush

从左边(头部)/右边(尾部)插入一个或多个值。

语法结构:

lpush/rpush key1 value1 value2 value3……

示例:

#从左边放入v1 v2 v3
127.0.0.1:6379> lpush k1 v1 v2 v3
(integer) 3

lpush

#从右边放入v4 v5 v6
127.0.0.1:6379> rpush k1 v4 v5 v6
(integer) 6

rpush

lrange

返回key列表中的start和end之间的元素(包含start和end)。 其中 0 表示列表的第一个元素-1表示最后一个元素

语法结构:

lrange key start end

示例:

#取出列表里前3个值,结果为v3 v2 v1
127.0.0.1:6379> lrange k1 0 2
#取出列表里全部值,结果为v3 v2 v1 v4 v5 v6
127.0.0.1:6379> lrange k1 0 -1
lpop/rpop

移除并返回第一个值或最后一个值。

语法格式:

lpop/rpop key

示例:

lpop k1 从列表中删除v3,并返回,当前列表全部值v2 v1 v4 v5 v6
rpop k1 从列表中删除v6,并返回,当前列表全部值v2 v1 v4 v5

注意:

值在键在,值光键亡。

lindex

获取列表index位置的值(从左开始)。

语法结构:

lindex key index

示例:

lindex k1 0
llen

获取列表长度。

语法结构:

llen key

示例:

127.0.0.1:6379> llen k1
(integer) 6
lrem

从左边开始删除与value相同的count个元素。

语法结构:

lrem key count value

COUNT 的值可以是以下几种:

  • count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。
  • count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。
  • count = 0 : 移除表中所有与 VALUE 相等的值。

示例:

redis> RPUSH mylist "hello"
(integer) 1
redis> RPUSH mylist "hello"
(integer) 2
redis> RPUSH mylist "foo"
(integer) 3
redis> RPUSH mylist "hello"
(integer) 4
redis> LREM mylist -2 "hello"
(integer) 2
redis> LRANGE mylist 0 -1
1) "hello"
2) "foo"
redis>
linsert

在列表中value值的前边/后边插入一个new value值(从左开始)。

语法结构:

linsert key before/after value newvalue

示例:

linsert k1 before v1 v5 在v1前面插入一个v5
lset

将索引为index的值设置为value

语法结构:

lset key index value

示例:

lset key index value

使用场景

  • 消息队列
  • 排行榜
  • 最新列表

实时学习反馈

1. Redis技术List数据类型中如何插入一个或多个值。

lpush/rpush

llen

lset

lrem

2. Redis技术List数据类型中如何移除并返回第一个值或者最后一个值。

lpush/rpush

llen

lset

lpop/rpop

答案

1=>A 2=>D

Redis数据类型_Set

image-20211214143648429

简介

与List类似是一个列表功能,但Set是自动排重的,当需要存储一个列表数据,又不希望出现重复数据时,Set是一个很好的选择。Set是String类型的无序集合,它底层其实是一个value为null的hash表,所以添加、删除、查找的时间复杂度都是O(1)。

常用命令

sadd

将一个或多个元素添加到集合key中,已经存在的元素将被忽略。

语法结构:

sadd key value1 value2……

示例:

#向集合中添加值,最终只有v1 v2 v3 v4 v5 v6
127.0.0.1:6379> sadd k1 v1 v2 v2 v3 v4 v5 v6
smembers

取出该集合的所有元素。

语法结构:

smembers key

示例:

127.0.0.1:6379> smembers k1
sismember

判断集合key中是否含有value元素,如有返回1,否则返回0。

语法结构:

sismember key value

示例:

sismember k1 v1
scard

返回该集合的元素个数。

语法结构:

scard key

示例:

scard k1
srem

删除集合中的一个或多个成员元素,不存在的成员元素会被忽略。

语法结构:

srem key value1 value2……

示例:

# 删除v1 v2
srem k1 v1 v2
spop

随机删除集合中一个元素并返回该元素。

语法结构:

spop key

示例:

spop k1 随机删除一个元素,并返回
srandmember

随机取出集合中count个元素,但不会删除。

语法结构:

srandmember key count

示例:

#随机取出集合中的2个元素
srandmember k1 2
smove

将value元素从sourcekey集合移动到destinationkey集合中。

语法结构:

smove sourcekey destinationkey value

示例:

smove k1 k2 v5 将元素v5从集合k1中移动到集合k2

注意:

如果 sourcekey集合不存在或不包含指定的 value元素,则 smove 命令不执行任何操作,仅返回 0 。

sinter

返回两个集合的交集元素。

语法结构:

sinter key1 key2

示例:

sinter key1 key2
sunion

返回两个集合的并集元素。

语法结构:

sunion key1 key2

示例:

sunion k1 k2
sdiff

返回两个集合的差集元素(key1中的,不包含key2)

语法结构:

sdiff key1 key2

示例:

sdiff k1 k2

使用场景

  • 黑白名单
  • 随机展示
  • 好友
  • 关注人
  • 粉丝
  • 感兴趣的人集合

实时学习反馈

1. Redis技术Set数据类型中将一个或多个元素添加到集合key中。

spop

sinter

sdiff

sadd

2. Redis技术Set数据类型中如何取出该集合的所有元素。

spop

smembers

sdiff

sadd

答案

1=>D 2=>B

Redis数据类型_Hash

image-20211214143917370

简介

Hash是一个键值对的集合。Hash 是一个 String 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象

常用命令

hset

给key集合中的field赋值value。

语法结构:

hset key field value

示例:

127.0.0.1:6379> hset user name baizhan
(integer) 1
127.0.0.1:6379> hset user age 3
(integer) 1

注意:

  • 如果哈希表不存在,一个新的哈希表被创建并进行 HSET 操作。
  • 如果字段已经存在于哈希表中,旧值将被重写。
hget

从key哈希中,取出field字段的值。

语法结构:

hget key field

示例:

127.0.0.1:6379> hget user name
"baizhan"
hmset

批量设置哈希的字段及值。

语法结构:

hmset key field1 value1 field2 value2……

示例:

127.0.0.1:6379> hmset user1 name baizhan age 15
OK
hexists

判断指定key中是否存在field

语法结构:

hexists key field

示例:

127.0.0.1:6379> hexists user1 name
(integer) 1
127.0.0.1:6379> hexists user1 xxx
(integer) 0

注意:

如果哈希表含有给定字段,返回 1 。 如果哈希表不含有给定字段,或 key 不存在,返回 0 。

hkeys

获取该哈希中所有的field。

语法结构:

hkeys key

示例:

127.0.0.1:6379> hkeys user1
1) "name"
2) "age"

hvals key

获取该哈希中所有的value。

语法结构:

hvals key

示例:

127.0.0.1:6379> hvals user1
1) "baizhan"
2) "15"
hincrby

为哈希表key中的field字段的值加上增量increment。

hincrby key field increment

示例:

127.0.0.1:6379> hincrby user1 age 10
(integer) 25

注意:

  • 增量也可以为负数,相当于对指定字段进行减法操作。
  • 如果哈希表的 key 不存在,一个新的哈希表被创建并执行 hincrby 命令。
  • 如果指定的字段不存在,那么在执行命令前,字段的值被初始化为 0 。
  • 对一个储存字符串值的字段执行 hincrby 命令将造成一个错误。
hincrby user1 age 10 对user中的age字段做运算,增加10
hdel

删除哈希表 key 中的一个或多个指定字段,不存在的字段将被忽略。

语法结构:

hdel key field1 field2……

示例:

127.0.0.1:6379> hdel user1 age
(integer) 1
hsetnx

给key哈希表中不存在的的字段赋值

语法结构:

hsetnx key field value

示例:

127.0.0.1:6379> hsetnx user1 age 10
(integer) 1

注意:

  • 如果哈希表不存在,一个新的哈希表被创建并进行 hsetnx 操作。
  • 如果字段已经存在于哈希表中,操作无效。
  • 如果 key 不存在,一个新哈希表被创建并执行 hsetnx 命令。

使用场景

  • 购物车
  • 存储对象

实时学习反馈

1. Redis技术Hash数据类型中如何给key集合中的field赋值value。

hdel

hsetnx

hexists

hset

2. Redis技术Hash数据类型中如何从key哈希中,取出field字段的值。

hdel

hsetnx

hexists

hget

答案

1=>D 2=>D

Redis数据类型_Zset

image-20211214144146582

简介

Zset与Set非常相似,是一个没有重复元素的String集合。不同之处是Zset的每个元素都关联了一个分数(score),这个分数被用来按照从低分到高分的方式排序集合中的元素。集合的元素是唯一的,但分数可以重复。

注意:

因为元素是有序的,所以可以根据分数(score)或者次序(position)来获取一个范围内的元素。

常用命令

zadd

将一个或多个元素(value)及分数(score)加入到有序集key中。

语法结构:

zadd key score1 value1 score2 value2……

示例:

zadd k1 100 java 200 c++ 300 python 400 php

注意:

  • 如果某个元素已经是有序集的元素,那么更新这个元素的分数值,并通过重新插入这个元素,来保证该元素在正确的位置上。
  • 分数值可以是整数值或双精度浮点数。
  • 如果有序集合 key 不存在,则创建一个空的有序集并执行 zadd 操作。
zrange

返回key集合中的索引start和索引end之间的元素(包含start和end)。

语法结构:

zrange key start end [withscores]

示例:

zrange k1 0 -1 返回集合中所有元素
zrange k1 0 -1 withscores 返回集合中所有元素,并携带元素分数

注意:

  • 其中元素的位置按分数值递增(从小到大)来排序。 其中 0 表示列表的第一个元素,-1表示最后一个元素。
  • withscores是可选参数,是否返回分数。
zrangebyscore

返回key集合中的分数minscore 和分数maxscore 之间的元素(包含minscore 和maxscore )。其中元素的位置按分数值递增(从小到大)来排序。

语法结构:

zrangebyscore key minscore maxscore [withscores]

示例:

zrangebyscore k1 200 400 返回200-400分之间的元素递增排序
zincrby

为元素value的score加上increment的值。

语法结构:

zincrby key increment value

示例:

zincrby k1 50 java 给java元素加上50分

zrem

删除该集合下value的元素。

语法结构

zrem k1 php 删除php
zcount

统计该集合在minscore 到maxscore分数区间中元素的个数。

语法结构:

zcount key minscore maxscore

示例:

zcount k1 100 300 统计100分到300分中间元素的个数
zrank

返回value在集合中的排名,从0开始。

语法结构:

zrank key value

示例:

zrank k1 c++ 返回c++排名

使用场景

  • 延时队列
  • 排行榜
  • 限流

实时学习反馈

1. Redis技术Zset数据类型中如何给将一个或多个元素(value)及分数(score)加入到有序集key中。

zadd

zrange

zrangebyscore

zcount

2. Redis技术Zset数据类型中如何返回key集合中的索引start和索引end之间的元素。

zadd

zrange

zrangebyscore

zcount

答案

1=>A 2=>B

Redis数据类型_Bitmaps

image-20211214144620856

简介

在计算机中,用二进制(位)作为存储信息的基本单位,1个字节等于8位。

例如 "abc" 字符串是由 3 个字节组成,计算机存储时使用其二进制表示,"abc"分别对应的ASCII码是97、98、99,对应的二进制是01100001、01100010、01100011,在内存中表示如下:

abc二进制

合理地使用位能够有效地提高内存使用率和开发效率。

Redis提供了Bitmaps这个 “数据结构” 可以实现对位的操作:

bitmaps

常用命令

setbit

设置Bitmaps中某个偏移量的值。

语法结构:

setbit key offset value

示例:

redis中bitmaps可以用来统计用户信息,eg:活跃天数、打卡天数、登录天数

bitmaps位图,都是操作二进制来进行记录,就只有0和1两个状态

127.0.0.1:6379> getbit sign 1 # 获取第一天的打卡状态
(integer) 1
127.0.0.1:6379> BITCOUNT sign # 统计所有打卡天数
(integer) 4127.0.0.1:6379> setbit zhangsan:3 1 1 # 往sign中添加数据,第1天打卡
(integer) 1
127.0.0.1:6379> setbit zhangsan:3 2 0 # 第2天未打卡
(integer) 0
127.0.0.1:6379> setbit zhangsan:3 3 1 # 第3天打卡
(integer) 0
127.0.0.1:6379> setbit zhangsan:3 4 0 # 第4天未打卡
(integer) 0
127.0.0.1:6379> setbit zhangsan:3 5 1 # 第5天打卡
(integer) 0
127.0.0.1:6379> setbit zhangsan:3 6 0 # 第6天未打卡
(integer) 0
127.0.0.1:6379> setbit zhangsan:3 7 1 # 第7天打卡
(integer) 0 127.0.0.1:6379> getbit sign 1 # 获取第一天的打卡状态
(integer) 1
127.0.0.1:6379> BITCOUNT sign # 统计所有打卡天数
(integer) 4
127.0.0.1:6379> getbit sign 1 # 获取第一天的打卡状态
(integer) 1

setbit

getbit

获取Bitmaps中某个偏移量的值。

语法结构:

getbit key offset

示例:

获取key的offset 的值。

getbit sign 3 获取偏移量为1的值,结果为1

如果偏移量未设置值,则也返回0。

getbit sign 99 获取偏移量为99的值,结果为0
bitcount

统计字符串被设置为1的bit数量。一般情况下,给定的整个字符串都会被进行统计,可以选择通过额外的start和end参数,指定字节组范围内进行统计(包括start和end),0表示第一个元素,-1表示最后一个元素

语法结构:

bitcount key [start end]

示例:

bitcount sign 获取整个字符串被设置为1的bit数量,结果为3

如:当前存在一个key为k1的bitmaps存储着[00000001,00000001,00000010,00000011],分别对应[1,1,2,3]。

setbit num 7 1
setbit num 15 1
setbit num 22 1
setbit num 30 1
setbit num 31 1
bitcount num 1 2 统计索引1、2两个字节组中bit=1的数量,即统计00000001,00000010中bit=1的数量,结果为2
bitcount num 1 3 统计索引1、2、3三个字节组中bit=1的数量,即统计00000001,00000010,00000011中bit=1的数量,结果为4
bitcount num 0 -1 统计所有的字节组中bit=1的数量,结果为5

setbit设置或获取的是bit(位)的位置,bitcount计算的是byte(字节)位置。

bitop

将多个bitmaps通过求交集/并集方式合并成一个新的bitmaps。

语法结构:

bitop and/or destkey sourcekey1 sourcekey2……

示例:

bitop and k3 k1 k2 通过求交集将k1 k2合并成k3
bitop or k3 k1 k2 通过求并集将k1 k2合并成k3

bitop

使用场景

  • 活跃天数
  • 打卡天数
  • 登录天数
  • 用户签到
  • 统计活跃用户
  • 统计用户是否在线
  • 实现布隆过滤器

实时学习反馈

1. Redis技术Bitmaps数据类型中如何设置Bitmaps中某个偏移量的值。

setbit

getbit

bitcount

bitop

2. Redis技术Bitmaps数据类型中如何获取Bitmaps中某个偏移量的值。

setbit

getbit

bitcount

bitop

答案

1=>A 2=>B

Redis数据类型_Geospatia

image-20211214155824278

简介

GEO,Geographic,地理信息的缩写。该类型就是元素的二维坐标,在地图上就是经纬度。Redis基于该类型,提供了经纬度设置、查询、范围查询、距离查询、经纬度Hash等常见操作。

常用命令

geoadd

用于存储指定的地理空间位置,可以将一个或多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中。

语法结构:

geoadd key longitude latitude member

示例:

# 将北京的经纬度和名称添加到china
geoadd china 116.405285 39.904989 beijing
# 将成都和上海的经纬度、名称添加到china
geoadd china 104.065735 30.659462 chengdu 121.472644 31.231706 shanghai
geopos

从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil。

语法结构:

geopos key member [member ……]

示例:

返回china中名称为shanghai和beijing的经纬度

geopos china shanghai beijing
geodist

用于返回两个给定位置之间的距离。

image-20211220094725113

语法结构:

geodist key member1 member2 [m|km|ft|mi]

参数说明:

  • m :米,默认单位。
  • km :千米。
  • mi :英里。
  • ft :英尺。

示例:

# 返回shanghai和beijing之间的距离,结果1067597.9668,单位米
geodist chinacity shanghai beijing
# 返回shanghai和chengdu之间的距离,结果1660.0198,单位是千米
geodist chinacity shanghai chengdu km
georadius

以给定的经纬度(longitude latitude)为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离(radius )的所有位置元素。

image-20211220094913596

语法结构:

georadius key longitude latitude radius m|km|ft|mi

示例:

#获取经纬度110 30为中心,在china内1200公里范围内的所有元素。
georadius china 110 30 1200 km
使用场景
  • 附近的电影院

  • 附近的好友

  • 离最近的火锅店

实时学习反馈

1. Redis技术Geospatia数据类型中如何存储指定的地理空间位置。

geoadd

geopos

geodist

georadius

2. Redis技术Geospatia数据类型中如何计算两个给定位置之间的距离。

geoadd

geopos

geodist

georadius

1=>A 2=>C

Redis数据类型_Hyperloglog

image-20211214155454882

HyperLogLog是什么

HyperLogLog并不是一种新的数据结构(实际类型为字符串类 型),而是一种基数估算算法。所谓基数估算,就是估算在一批数据中,不重复元素的个数有多少。通过HyperLogLog可以利用极小的内存空间完成独立总数的统计,数据集可以是IP、Email、ID等。

什么是基数

比如数据集{1,3,5,7,5,7,8},那么这个数据集的基数集为{1,3,5,7,8},基数(不重复元素)为5.基数估计就是在误差可接受的范围内,快速计算基数。

常用命令

pfadd

将所有元素参数添加到 Hyperloglog 数据结构中。

语法结构:

pfadd key element1 element2……

示例:

如果至少有个元素被添加返回 1, 否则返回 0。

pfadd book1 uid1 uid2 uid3

注意:

添加元素到HyperLogLog中,如果内部有变动返回1,没有返回0。

pfcount

计算Hyperloglog 近似基数,可以计算多个Hyperloglog ,统计基数总数。

语法结构:

pfcount key1 key2……

示例:

pfcount book1 #计算book1的基数,结果为3
pfadd book2 uid3 uid4 #添加两个元素到book2中
pfcount book1 book2 #统计两个key的基数总数,结果为5
pfmerge

将一个或多个Hyperloglog(sourcekey1) 合并成一个Hyperloglog (destkey )。

语法结构:

pfmerge destkey sourcekey1 sourcekey2……

示例:

比如每月活跃用户可用每天活跃用户合并后计算。

#将book1和book2合并成book,结果为5
pfmerge book book1 book2
使用场景

基数不大,数据量不大就用不上,会有点大材小用浪费空间,有局限性,就是只能统计基数数量,而没办法去知道具体的内容是什么,和bitmap相比,属于两种特定统计情况,简单来说,HyperLogLog 去重比 bitmaps 方便很多,一般可以bitmap和hyperloglog配合使用,bitmap标识哪些用户活跃。

  • 网站PV统计
  • 网站UV统计
  • 统计访问量(IP数)
  • 统计在线用户数
  • 统计每天搜索不同词条的个数
  • 统计文章真实阅读数
实时学习反馈

1.Redis HyperLogLog 是用来做____的算法。

A 统计计算

B 分类

C 聚合统计

D 统计基数

1=>D

Redis可视化工具_安装Redis_Desktop_Manager

image-20211220155602801

下载Redis Desktop Manager

image-20211220154011871

注意:

官网Redis Insight

image-20211220154046445

选择安装路径

image-20211220154136865

连接Redis服务

关闭防火墙

systemctl stop firewalld.service

关闭保护模式(先进入到 vim redis.conf)

protected-mode no

开启远程访问

redis默认只允许本地访问,要使redis可以远程访问可以修改redis.conf。

注释掉bind 127.0.0.1 可以使所有的ip访问redis

配置连接服务

image-20211220154714245

配置信息

image-20211220154929854

Java整合Redis_Jedis操作

image-20211215143525935

什么是Jedis

Jedis是Redis官方推荐的Java连接开发工具。

引入Jedis

创建maven工程

image-20210716133637202

引入maven依赖
 

 
 

<dependency>

 

<groupId>redis.clients</groupId>

 

<artifactId>jedis</artifactId>

 

<version>3.6.0</version>

 

</dependency>

 

<dependency>

 

<groupId>junit</groupId>

 

<artifactId>junit</artifactId>

 

<version>4.12</version>

 

<scope>test</scope>

 

</dependency>

Jedis连接到redis
 

 
 

//第一个参数是ip地址,第二个参数是端口

 

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

注意:

在连接之前,需要开放redis连接服务,redis.conf中注释掉bind 127.0.0.1 ,然后 protected-mode no,关闭防火墙。

image-20210716140455720

测试相关数据类型

连接Redis服务

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

//通过ping()方法向redis发送一个ping命令,服务器返回一个Pong

String msg = jedis.ping();

System.out.println(msg);

//jedis使用完毕需要关闭

jedis.close();

Jedis-API:String

//设置一个key

jedis.set("k1","v1");

//设置一个key

jedis.set("k2","1");

//获取一个key

String res = jedis.get("k1");

//对某一个key自增

Long ires = jedis.incr("k2");

Jedis-API:Keys

//返回所有的key

Set<String> keys = jedis.keys("*");

//返回该key剩余过期时间

Long time = jedis.ttl("k1");

Jedis-API:List

//向list中添加数据

jedis.lpush("list1","v1","v2","v3");

//返回list全部数据

List<String> list = jedis.lrange("list1",0,-1 );

Jedis-API:Set

//向set中添加数据

jedis.sadd("set1" ,"v1","v2","v2","v3");

//查看该集合中有多少个元素

jedis.smembers("set1");

Jedis-API:Hash

//设置一个hash

jedis.hset("user","age","25");

//获取该key的所有value

jedis.hvals("user");

Jedis-API:Zset

//向zset中添加一条数据

jedis.zadd("zset1",100,"java");

//获取所有的值

jedis.zrange("zset1",0,-1);

Jedis-API:Bitmaps

//将b1偏移量为0的位设置为1

jedis.setbit("b1",0, "1");

//获取b1偏移量为0的位

jedis.getbit("b1",0);

Jedis-API:Geospatia

//添加一条地理信息数据

jedis.geoadd("chinacity",130,110,"beijing");

Jedis-API:Hyperloglog

//将所有元素参数添加到 Hyperloglog 数据结构中。

jedis.pfadd("book","c++","java","php");

注意:

其实jedis中的方法基本同redis命令一致。

Java整合Redis_Spring-Data-Redis

image-20240126153714410

简介

Spring-Data-Redis是Spring大家族的一部分,通过简单的配置访问Redis服务,对Reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate提供了Redis各种操作、异常处理及序列化,支持发布订阅。

Spring Data Redis 介绍

提供了一个高度封装的 “RedisTemplate” 类,里面封装了对于Redis的五种数据结构的各种操作,包括:

  • redisTemplate.opsForValue():操作字符串
  • redisTemplate.opsForHash():操作hash
  • redisTemplate.opsForList():操作list
  • redisTemplate.opsForSet():操作set
  • redisTemplate.opsForZSet():操作zset

SpringBoot2.x后RedisTemplate采用是lettuce(基于netty采用异步非阻塞式lO)进行通信,大并发下比jedis效率更高。

RedisTemplate模板使用序列化器

序列化器说明
JdkSerializationRedisSerializerPOJO对象的存取场景,使用JDK本身序列化机制,将pojo类通过ObjectInputstream/ObjectOutputstream进行序列化操作,最终redis-server中将存储字节序列。是目前最常用的序列化策略。
StringRedisSerializerKey或者value为字符串的场景,根据指定的charset对数据的字节序列编码成string,是"new String(bytes,charset)"和“string.getBytes(charset)"的直接封装。是最轻量级和高效的策略。
GenericJackson2JsonRedisSerializerjackson-json工具提供了javabean与json之间的转换能力,可以将pojo实例序列化成json格式存储在redis中,也可以将json格式的数据转换成pojo实例。

添加依赖

 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>io.projectreactor</groupId><artifactId>reactor-test</artifactId><scope>test</scope></dependency></dependencies>

RedisTemplate的配置(全部复制就行,不需要自己去写)

自定义序列化方式。

@Configuration
public class RedisConfig {/*** 创建RedisTemplate:用于执行Redis操作的方法*/@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory)   {RedisTemplate<String, Object> redisTemplate= new RedisTemplate<String, Object>();redisTemplate.setConnectionFactory(factory);//设置通用序列化器redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());return redisTemplate;}
}

在application.properties中配置

#Redis服务器连接地址
spring.data.redis.host=192.168.47.100
#Redis服务器连接端口
spring.data.redis.port=6379

操作RedisTemplate方法

redisTemplate.opsForValue();//操作字符串
redisTemplate.opsForHash();//操作hash
redisTemplate.opsForList();//操作list
redisTemplate.opsForSet();//操作set
redisTemplate.opsForZSet();//操作有序set

使用StringRedisTemplate进行各类型的CURD操作

String数据类型操作
  • 添加元素
public boolean set(String key,Object value){try{redisTemplate.opsForValue().set(key,value);return true;}catch (Exception e){log.error("redis set value exception:{}",e);return false;}}
  • 获取元素
 public Object get(String key){return  key == null ? null : redisTemplate.opsForValue().get(key);}
  • 添加元素并设置过期时间
public boolean setex(String key,Object value,long expire){try{//TimeUnit.SECONDS指定类型为秒redisTemplate.opsForValue().set(key,value,expire,       TimeUnit.SECONDS);return true;}catch (Exception e){log.error("redis set value and expire exception:{}",e);return false;}}
Hash类型的操作
  • 添加元素
public boolean hset(String key, String field, Object value,long seconds) {try {redisTemplate.opsForHash().put(key, field, value);expire(key,seconds);//调用通用方法设置过期时间return true;}catch (Exception e){log.error("redis hset and expire eror,key:{},field:{},value:{},exception:{}",key,field,value,e);return false;}}
  • 获取数据
public Object hget(String key,String field){return redisTemplate.opsForHash().get(key,field);}
set类型的操作
  • 添加元素
  public long sset(String key ,Object...values){try {return redisTemplate.opsForSet().add(key,values);}catch (Exception e){log.error("redis sset error,key:{},value:{},values:{},exception:{}",key,values,e);return 0;}}
  • 获取set的长度
public long sgetSize(String key){try {return redisTemplate.opsForSet().size(key);}catch (Exception e){log.error("redis sgetSize error,key:{},exception:{}",key,e);return 0;}}
  • 获取元素
 public Set<Object> sgetAll(String key){try {return redisTemplate.opsForSet().members(key);}catch (Exception e){log.error("redis sgetAll error,key:{},exception:{}",key,e);return null;}}
zset类型的操作
  • 添加元素
  public  boolean zadd(String key,Object member,double score){try {return  redisTemplate.opsForZSet().add(key,member,score);} catch (Exception e) {log.error("redis zadd error,key:{},value:{},score:{},exception:{}",key,member,score,e);return false;}}
  • 获取元素
  public Set<String> zrange(String key,int start,int end){try {Set<Object> range = redisTemplate.opsForZSet().range(key, start, end);if(range==null||range.size()==0) return null;return  range.stream().map(o->(String)o).collect(Collectors.toSet());} catch (Exception e) {log.error("redis zrange error,key:{},start:{},end:{},exception:{}",key,start,end,e);return null;}}
list类型的操作
  • 添加元素
 public boolean lrpush(String key, Object value) {try {redisTemplate.opsForList().rightPush(key, value);return true;} catch (Exception e) {log.error("redis lrpush error,key:{},value:{}exception:{}",key,value,e);return false;}}
  • 获取元素
  public List<Object> getList(String key,int start,int end) {try {List<Object> o = redisTemplate.opsForList().range(key,start,end);return o;} catch (Exception e) {return null;}}

Redis构建Web应用实践_环境搭建

SNAGHTML93d3856

引入依赖

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.5</version><exclusions><exclusion><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>3.0.3</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency>

编写配置文件

########################################################
### 配置连接池数据库访问配置
########################################################
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/itbaizhan?characterEncoding=utf-8&&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456

创建表

DROP TABLE IF EXISTS `user`;CREATE TABLE `user`
(id BIGINT NOT NULL COMMENT '主键ID',name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',age INT NULL DEFAULT NULL COMMENT '年龄',email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',PRIMARY KEY (id)
);
DELETE FROM `user`;INSERT INTO `user` (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

编写实体类 User.java

@Data
@TableName("`user`")
public class User {private Long id;private String name;private Integer age;private String email;
}

编写 Mapper 包下的 UserMapper接口

public interface UserMapper extends BaseMapper<User> {}

编写业务层

@Service
public class UserService {@AutowiredUserMapper userMapper;public User getUser(Long id){return userMapper.selectById(id);}}

编写控制层

@RestController
public class UserController {@AutowiredUserService userService;/*** 根据id查询用户* @param id 用户id* @return*/@GetMapping("/getById")public User getUser(Long id){return userService.getUser(id);}}

Redis构建Web应用实践_演示Redis的魅力

下载压测工具

登录官网Jmeter下载,得到压缩包jmeter-5.0.zip

img

启动Jmeter工具

D:\apache-jmeter-5.4.3\bin\jmeter.bat文件双击运行。

修改语言

image-20211227164142184

创建压测任务

image-20211227164225861

添加HTTP请求

image-20211227164320619

配置HTT请求

image-20211227164514719

添加压测结果报告

image-20211227164556998

没有加缓存的吞吐量

image-20211227164730666

添加Redis缓存

  /**** 根据用户id查询用户* @param id* @return*/@Overridepublic User getById(Long id) {// 1、判断用户id等于1 有没有缓存  user:1User user = (User) template.opsForValue().get("user:" + id);if (user != null){return user;}// 2、如果没有缓存从数据库查User u = userMapper.selectById(id);// 3、加入缓存template.opsForValue().set("user:" + id,u);return u;}

你提出的问题非常好!为什么在 Redis 缓存中使用 `"user:" + id` 作为键(key),而不是直接用 `id`?这背后有几个重要的设计考量,涉及 **命名空间、可读性、避免键冲突、便于维护和管理** 等。

---

### ✅ 为什么要用 `"user:" + id` 作为键?

#### 1. **命名空间(Namespace)隔离**
Redis 是一个**全局的键值存储系统**,如果你直接用 `id`(比如 `1`, `2`, `3`)作为键:


template.opsForValue().get("1")  // ❌ 不推荐
```

这会带来严重问题:

- 不同类型的数据可能使用相同的 ID,比如:
  - 用户 ID 为 1
  - 订单 ID 为 1
  - 商品 ID 为 1

如果都用 `"1"` 作为键,就会**发生键冲突**,导致数据覆盖或读取错误。

而使用 `"user:1"` 明确表示:“这是用户类型、ID 为 1 的数据”,起到了**命名空间**的作用。

---

#### 2. **提高可读性和调试便利性**
当你在 Redis 中查看键时,看到的是:

```
user:1
user:2
order:1001
product:88
```

这比只看 `1`, `2`, `1001`, `88` 要清晰得多,你知道每个键代表什么类型的数据。

尤其是在使用 `KEYS *` 或 Redis 可视化工具时,这种命名方式非常便于排查问题。

---

#### 3. **支持批量操作和管理**
使用统一前缀可以方便地进行:

- **批量删除某类数据**:
  ```bash
  # 删除所有用户缓存
  redis-cli KEYS "user:*" | xargs redis-cli DEL
  ```

- **设置统一过期策略**(配合脚本或程序)
- **监控和统计**:比如监控“用户缓存”的命中率

---

#### 4. **符合行业最佳实践**
这种 `"实体类型:主键"` 的命名方式是业界通用的 Redis 命名规范,例如:

- `user:1001`
- `session:abc123`
- `cart:1001`
- `post:2048:comments`

你可以在大型项目或开源系统中看到广泛使用。

---

#### 5. **便于后期扩展**
未来如果你要为同一个用户添加更多缓存项,可以自然扩展:

```text
user:1:profile      → 用户基本信息
user:1:settings     → 用户设置
user:1:permissions  → 权限信息
```

结构清晰,层级分明。

---

### ✅ 对比:带前缀 vs 不带前缀

| 键名方式 | 示例 | 优点 | 缺点 |
|--------|------|------|------|
| 直接用 ID | `"1"` | 简短 | ❌ 容易冲突,无法区分类型 |
| 加前缀 | `"user:1"` | ✅ 清晰、安全、易管理 | 键稍长(但几乎无影响) |

> 💡 在 Redis 中,键的长度对性能影响极小,清晰的语义远比节省几个字符重要。

---

### ✅ 总结

使用 `"user:" + id` 作为 Redis 的键,是因为:

✅ 避免键冲突  
✅ 提高可读性与可维护性  
✅ 实现命名空间隔离  
✅ 便于批量操作和缓存管理  
✅ 符合标准开发规范  

这是一种**强烈推荐的最佳实践**,尤其是在中大型项目中。

继续压力测试

image-20240129150645653

Redis构建Web应用实践_引入本地缓存

image-20240130100307888

为什么引入本地缓存

本地缓存因为少了网络传输环节,所以读取速度比分布式缓存要快一些。

在这里插入图片描述

本地缓存的优点

  • 减少了网络调用的开销
  • 减少了数据请求的序列化和反序列化

Redis结合本地缓存

微服务场景下,多个微服务使用一个大缓存,流数据业务下,高频读取缓存对Redis压力很大,我们使用本地缓存结合Redis缓存使用,降低Redis压力,同时本地缓存没有连接开销,性能更优。

image-20240130102506995

本地方案选择

本地缓存为什么不使用hashMap或者concurrentHashMap

原因:

HashMap、ConcurrentHashMap也能用作本地缓存,但是因为缺少必要的过期机制、容量限制、数据淘汰策略,不太合适。

实时学习反馈

1. 本地缓存主要作用_____。

A 减少了网络调用的开销

B 提高系统安全性

C 定位系统存在的慢操作

D 增强系统稳定性

2. 本地缓存为什么不使用hashMap或者concurrentHashMap描述正确的是__?。

A 缺少必要的过期机制

B 容量限制

C 数据淘汰策略

D 以上都是正确

答案

1=>A 2=>D

Redis构建Web应用实践_Google 开源工具Guava

image-20240130103004156

概述

Guava项目是 Google 公司开源的 Java 核心库,它主要是包含一些在 Java 开发中经常使用到的功能,如数据校验不可变集合、计数集合,集合增强操作、I/O、缓存、字符串操作等。

guava github 开源地址: https://github.com/google/guava

Guava开发要求 :

  • JRE风格需要JDK 1.8或更高版本。
  • 如果您需要支持Android,请使用Android风格。

Maven引入依赖

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>32.1.2-jre</version><!-- or, for Android: --><version>32.1.2-android</version>
</dependency>

字符串(Strings)

Strings是Guava提供的一组字符串工具,它提供了许多有用的方法来处理字符串。

Strings的主要方法:

  • isNullOrEmpty(String string):判断字符串是否为空或null。
  • padEnd(String string, int minLength, char padChar):在字符串末尾填充指定字符,直到字符串达到指定长度。
  • padStart(String string, int minLength, char padChar):在字符串开头填充指定字符,直到字符串达到指定长度。
  • repeat(String string, int count):重复指定字符串指定次数。

Strings的使用示例:

public class StringsDemo {public static void main(String[] args) {// 判断字符串是否为空或nullString str1 = null;String str2 = "";System.out.println(Strings.isNullOrEmpty(str1));System.out.println(Strings.isNullOrEmpty(str2));// 在字符串末尾填充指定字符,直到字符串达到指定长度String str3 = "abc";String paddedStr1 = Strings.padEnd(str3, 6, '*');System.out.println(paddedStr1);// 在字符串开头填充指定字符,直到字符串达到指定长度String str4 = "abc";String paddedStr2 = Strings.padStart(str4, 6, '*');System.out.println(paddedStr2);// 重复指定字符串指定次数String str5 = "abc";String repeatedStr = Strings.repeat(str5, 3);System.out.println(repeatedStr);
}

集合操作(Collections)

Guava提供了一些非常有用的集合操作API。

    1. ImmutableList

不可变集合是Guava的一个重要特性,它可以确保集合不被修改,从而避免并发访问的问题。ImmutabelList是不可变List的实现,下面是一个示例代码:

List<String> list = Lists.newArrayList("a", "b", "c");
ImmutableList<String> immutableList = ImmutableList.copyOf(list);
    1. Iterables
Iterable<String> iterable = Lists.newArrayList("a", "b", "c");// 判断集合是否为空
boolean isEmpty = Iterables.isEmpty(iterable);// 获取第一个元素,如果集合为空返回null
String first = Iterables.getFirst(iterable, null);// 获取最后一个元素,如果集合为空返回null
String last = Iterables.getLast(iterable, null);// 获取所有符合条件的元素
Iterable<String> filtered = Iterables.filter(iterable, new Predicate<String>() {@Overridepublic boolean apply(String input) {return input.startsWith("a");}
});
    1. Multimaps

Multimaps提供了一个非常有用的数据结构,它允许一个键对应多个值,下面是一个示例代码:

ListMultimap<Integer, String> map = ArrayListMultimap.create();
map.put(1, "a");
map.put(1, "b");
map.put(2, "c");
List<String> values = map.get(1); // 返回[a, b]
  • 4.Maps

Maps提供了一些有用的方法来操作Map,如下所示:

Map<Integer, String> map = ImmutableMap.of(1, "a", 2, "b", 3, "c");// 判断Map是否为空
boolean isEmpty = Maps.isEmpty(map);// 获取Map中的所有键
Set<Integer> keys = map.keySet();// 获取Map中的所有值
Collection<String> values = map.values();// 获取Map中的所有键值对
Set<Map.Entry<Integer, String>> entries = map.entrySet();// 根据键获取值,如果不存在则返回null
String value = Maps.getIfPresent(map, 1);

条件检查(Preconditions)

Preconditions是Guava提供的一组前置条件检查工具,它提供了一些检查参数是否符合预期的方法。

Preconditions的主要方法:

  • checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs):检查参数是否符合预期,并抛出IllegalArgumentException异常,可以包含错误信息模板和占位符。
  • checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs):检查参数是否为null,并抛出NullPointerException异常,可以包含错误信息模板和占位符。

Preconditions的使用示例:

public class PreconditionsDemo {public static void main(String[] args) {// 检查参数是否符合预期,并抛出IllegalArgumentException异常,可以包含错误信息模板和占位符String str1 = "abc";Preconditions.checkArgument(str1.length() < 3, "字符串长度必须小于3");// 检查参数是否为null,并抛出NullPointerException异常,可以包含错误信息模板和占位符String str2 = null;Preconditions.checkNotNull(str2, "字符串不能为空");
}

Redis构建Web应用实践_Guava实现本地缓存

本地缓存(CacheBuilder)

Cache是Guava提供的一个缓存工具类,它可以帮助我们在内存中缓存数据,提高程序的性能。

Cache的主要方法:

  • get(K key, Callable<? extends V> valueLoader):获取指定key的缓存值,如果缓存中没有,则调用valueLoader加载数据并存入缓存。
  • getIfPresent(Object key):获取指定key的缓存值,如果缓存中没有,则返回null。
  • getAllPresent(Iterable<?> keys):获取指定keys的缓存值,如果缓存中没有,则返回null。
  • put(K key, V value):将指定key的缓存值存入缓存。
  • putAll(Map<? extends K, ? extends V> m):将指定Map的缓存值存入缓存。
  • invalidate(Object key):将指定key的缓存值从缓存中删除。
  • invalidateAll(Iterable<?> keys):将指定keys的缓存值从缓存中删除。
  • invalidateAll():将所有缓存值从缓存中删除。
  • size():获取缓存中缓存值的数量。
  • asMap():将缓存转换成Map。

代码示例

package com.itbaizhan.springdataredisdemo.service.impl;import com.google.common.cache.*;
import com.itbaizhan.springdataredisdemo.entity.User;
import com.itbaizhan.springdataredisdemo.mapper.UserMapper;
import com.itbaizhan.springdataredisdemo.service.IUserService;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class UserServiceImpl implements IUserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate RedisTemplate<String,Object> template;/*** 本地缓存*/private LoadingCache<String, User> localCache = CacheBuilder.newBuilder()//设置并发级别为16,并发级别是指可以同时写缓存的线程数.concurrencyLevel(16)//设置缓存容器的初始容量为1000.initialCapacity(1000)//设置缓存最大容量为10000,超过10000之后就会按照LRU最近虽少使用算法来移除缓存项.maximumSize(10000)//设缓存1小时没被使用就过期.expireAfterAccess(1, TimeUnit.HOURS)//设置要统计缓存的命中率.recordStats()//设置缓存的移除通知.removalListener(new RemovalListener<Object, Object>() {@Overridepublic void onRemoval(RemovalNotification<Object, Object> notification) {System.out.println(notification.getKey() + " 被移除了,原因: " + notification.getCause());}}).build(new CacheLoader<String, User>() {@Overridepublic User load(String key) throws Exception {// 1、判断用户id等于1 有没有缓存  user:1User user = (User) template.opsForValue().get("user:" + key);if (user != null){return user;}// 2、查询数据库User users = userMapper.selectById(key);// 4、加入redis分布式缓存template.opsForValue().set("user:" + key,users);return users;}});/**** 根据用户id查询用户* @param id* @return*/@SneakyThrows@Overridepublic User getById(Long id) {// 1、从本地缓存获取User o = localCache.get(id+"");return o;}}

总结

能够高效的读取的同时,提供了大量api方便我们控制本地缓存的数据量及冷数据淘汰;我们充分的学习这些特性能够帮助我们在业务开发中更加轻松灵活,在空间与时间上找到一个平衡点。

Redis其他功能_流水线pipeline

img

1次网络命令通信模型

image-20211214160959856

经历了1次时间 = 1次网络时间 + 1次命令时间。

批量网络命令通信模型

image-20211214161239304

经历了 n次时间 = n次网络时间 + n次命令时间

什么是流水线?

image-20211214161344055

经历了 1次pipeline(n条命令) = 1次网络时间 + n次命令时间,这大大减少了网络时间的开销,这就是流水线。

案例展示

从北京到上海的一条命令的生命周期有多长?

image-20211214161651523

执行一条命令在redis端可能需要几百微秒,而在网络光纤中传输只花费了13毫秒。

注意:

在执行批量操作而没有使用pipeline功能,会将大量的时间耗费在每一次网络传输的过程上;而使用pipeline后,只需要经过一次网络传输,然后批量在redis端进行命令操作。这会大大提高了效率。

pipeline实现

没有pipeline的命令执行
    for (int i = 0; i < 10000; i++) {redisTemplate.opsForHash().put("hashkey:" + i, "field" + i, "value" + i);}

注意:

在不使用pipeline的情况下,使用for循环进行每次一条命令的执行操作,耗费的时间可能达到 1w 条插入命令的耗时为50s。

使用pipeline
  redisTemplate.execute(new RedisCallback<Long>() {@Nullable@Overridepublic Long doInRedis(RedisConnection connection) throws DataAccessException {connection.openPipeline();for (int i = 0; i < 10000; i++) {connection.hSet(("hashkey:" + i).getBytes(), ("field" + i).getBytes(), ("value" + i).getBytes());}List<Object> result = connection.closePipeline();return null;}});

实时学习反馈

1. Redis技术中Pipeline主要作用是

A 提高消息传递速度

B 加快命令的执行速度

C 网络中传递的命令的安全性

D 减少了网络时间的开销

答案

1=>D

Redis其他功能_发布与订阅

image-20211220181303520

什么是发布与订阅

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

什么时候用发布订阅

看到发布订阅的特性,用来做一个简单的实时聊天系统再适合不过了。再比如,在一个博客网站中,有100个粉丝订阅了你,当你发布新文章,就可以推送消息给粉丝们拉。

image-20211220181655048

Redis的发布与订阅

img

发布订阅命令行实现

订阅

语法格式:

subcribe 主题名字

示例:

127.0.0.1:6379> SUBSCRIBE channel-1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel-1"
3) (integer) 1


发布命令

语法格式:

publish channel-1 hello

示例:

打开另一个客户端,给channel1发布消息hello

127.0.0.1:6379> PUBLISH channel-1 hello
(integer) 1

注意:

返回的1是订阅者数量。

打开第一个客户端可以看到发送的消息

127.0.0.1:6379> SUBSCRIBE channel-1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel-1"
3) (integer) 1
1) "message"
2) "channel-1"
3) "hello"


注意:

发布的消息没有持久化,如果在订阅的客户端收不到hello,只能收到订阅后发布的消息。

实时学习反馈

1. Redis技术中如何订阅一个主题。

add

publish

subscribe

participation

答案

1=>A

Redis其他功能_慢查询

img

什么是慢查询

Redis慢查询是Redis提供的一项性能优化功能,它可以记录某个查询语句的执行时间、命令参数、执行次数等信息,从而帮助运维人员快速定位某个查询语句的性能问题。

Redis命令执行的整个过程

image-20211214162827153

两点说明:

  1. 慢查询发生在第3阶段
  2. 客户端超时不一定慢查询,但慢查询是客户端超时的一个可能因素
  3. 慢查询日志是存放在Redis内存列表中。

什么是慢查询日志

慢查询日志是Redis服务端在命令执行前后计算每条命令的执行时长,当超过某个阈值是记录下来的日志。日志中记录了慢查询发生的时间,还有执行时长、具体什么命令等信息,它可以用来帮助开发和运维人员定位系统中存在的慢查询。

如何获取慢查询日志

可以使用slowlog get命令获取慢查询日志,在slowlog get后面还可以加一个数字,用于指定获取慢查询日志的条数,比如,获取3条慢查询日志:

127.0.0.1:6379> SLOWLOG get 3
1) 1) (integer) 0
  2) (integer) 1640056567
  3) (integer) 11780
  4) 1) "FLUSHALL"
  5) "127.0.0.1:43406"
  6) ""


参数:

  1. 唯一标识ID
  2. 命令执行的时间戳
  3. 命令执行时长
  4. 执行的命名和参数

如何获取慢查询日志的长度

可以使用slowlog len命令获取慢查询日志的长度。

> slowlog len
(integer) 121


注意:

当前Redis中有121条慢查询日志。

怎么配置慢查询的参数

  • 命令执行时长的指定阈值 slowlog-log-slower-than。

slowlog-log-slower-than的作用是指定命令执行时长的阈值,执行命令的时长超过这个阈值时就会被记录下来。

  • 存放慢查询日志的条数 slowlog-max-len。

slowlog-max-len的作用是指定慢查询日志最多存储的条数。实际上,Redis使用了一个列表存放慢查询日志,slowlog-max-len就是这个列表的最大长度。

如何进行配置

查看慢日志配置

查看 redis 慢日志配置,登陆 redis 服务器,使用 redis-cli 客户端连接redis server。

127.0.0.1:6379> config get slow*
1) "slowlog-max-len"
2) "128"
3) "slowlog-log-slower-than"
4) "10000"


慢日志说明:

10000阈值,单位微秒,此处为10毫秒,128慢日志记录保存数量的阈值,此处保存128条。

修改Redis配置文件

比如,把slowlog-log-slower-than设置为1000,slowlog-max-len设置为1200:

slowlog-log-slower-than 1000
slowlog-max-len 1200


使用config set命令动态修改。

比如,还是把slowlog-log-slower-than设置为1000,slowlog-max-len设置为1200

> config set slowlog-log-slower-than 1000
OK
> config set slowlog-max-len 1200
OK
> config rewrite
OK


实践建议

slowlog-max-len配置建议
  • 线上建议调大慢查询列表,记录慢查询时Redis会对长命令做截断操作,并不会占用大量内存。
  • 增大慢查询列表可以减缓慢查询被剔除的可能,例如线上可设置为1000以上。
slowlog-log-slower-than配置建议
  • 默认值超过10毫秒判定为慢查询,需要根据Redis并发量调整该值。
  • 由于Redis采用单线程响应命令,对于高流量的场景,如果命令执行时间在1毫秒以上,那么Redis最多可支撑QPS不到1000。因此对于高QPS场景的Redis建议设置为1毫秒。

实时学习反馈

1. Redis技术中慢查询主要作用_____。

A 提高响应速度

B 提高查询速度

C 定位系统存在的慢操作

D 增强系统稳定性

2. Redis慢查询技术中通过修改_____预设阈值。

slowlog get

slowlog-log-slower-than

slowlog len

slowlog reset

答案

1=>C 2=>B

Redis数据安全_持久化机制概述

image-20211215142450329

由于Redis的数据都存放在内存中,如果没有配置持久化,Redis重启后数据就全丢失了,于是需要开启Redis的持久化功能,将数据保存到磁盘上,当Redis重启后,可以从磁盘中恢复数据。

持久化机制概述

对于Redis而言,持久化机制是指把内存中的数据存为硬盘文件, 这样当Redis重启或服务器故障时能根据持久化后的硬盘文件恢复数据。

持久化机制的意义

Redis持久化的意义,在于故障恢复。比如部署了一个redis,作为cache缓存,同时也可以保存一些比较重要的数据。

image-20211221145624459

Redis提供了两个不同形式的持久化方式

  • RDB(Redis DataBase)
  • AOF(Append Only File)

实时学习反馈

1. Redis持久化机制主要解决___问题。

A 数据重复

B 数据不可靠

C 数据丢失

D 数据量大

2. 开启Redis的持久化功能,将数据保存到____上。

A 内存

B 磁盘

C 数据库

D 云平台

1=>C 2=>B

Redis数据安全_RDB持久化机制实战

img

RDB是什么

在指定的时间间隔内将内存的数据集快照写入磁盘,也就是行话讲的快照,它恢复时是将快照文件直接读到内存里。

img

注意:

这种格式是经过压缩的二进制文件。

配置dump.rdb文件

RDB保存的文件,在redis.conf中配置文件名称,默认为dump.rdb。

 
439
440 # The filename where to dump the DB
441 dbfilename dump.rdb
442

rdb文件的保存位置,也可以修改。默认在Redis启动时命令行所在的目录下。

image-20210706095643442

rdb文件的保存路径,也可以修改。默认为Redis启动时命令行所在的目录下

 
dir ./

image-20210706095519023

触发机制-主要三种方式

RDB配置

image-20210706101241144

快照默认配置:

  • save 3600 1:表示3600秒内(一小时)如果至少有1个key的值变化,则保存。
  • save 300 100:表示300秒内(五分钟)如果至少有100个 key 的值变化,则保存。
  • save 60 10000:表示60秒内如果至少有 10000个key的值变化,则保存。
配置新的保存规则

给redis.conf添加新的快照策略,30秒内如果有5次key的变化,则触发快照。配置修改后,需要重启Redis服务。

save 3600 1
save 300 100
save 60 10000
save 30 5

flushall

执行flushall命令,也会触发rdb规则。

save与bgsave

手动触发Redis进行RDB持久化的命令有两种:

  1. save

    该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止,不建议使用。

  2. bgsave

    执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。

高级配置

stop-writes-on-bgsave-error

默认值是yes。当Redis无法写入磁盘的话,直接关闭Redis的写操作。

image-20210706105448816

rdbcompression

默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩。如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能,但是存储在磁盘上的快照会比较大。

image-20210706105622243

rdbchecksum

默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。

image-20210706105924714

恢复数据

只需要将rdb文件放在Redis的启动目录,Redis启动时会自动加载dump.rdb并恢复数据。

优势

  • 适合大规模的数据恢复
  • 对数据完整性和一致性要求不高更适合使用
  • 节省磁盘空间
  • 恢复速度快

劣势

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

实时学习反馈

1. Redis技术中下列____是RDB缺点。

A 恢复速度快

B 节省空间

C 会丢失最后一次快照后的所有修改

D 适合大规模的数据恢复

2. Redis技术中RDB持久化如何表示30秒内如果至少有100个 key的值变化,则保存。

save 100 3

3 save 100

save 30 100

save 100 30

答案

1=>C 2=>C

Redis数据安全_AOF持久化机制实战

image-20211215142955995

AOF是什么

以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来。

image-20211221155715791

AOF默认不开启

可以在redis.conf中配置文件名称,默认为appendonly.aof。

image-20210706112212845

注意:

AOF文件的保存路径,同RDB的路径一致,如果AOF和RDB同时启动,Redis默认读取AOF的数据。

AOF启动/修复/恢复

开启AOF

设置Yes:修改默认的appendonly no,改为yes

appendonly yes

注意:

修改完需要重启redis服务。

设置数据。

set k11 v11
set k12 v12
set k13 v13
set k14 v14
set k15 v15

AOF同步频率设置

参数:

  1. appendfsync always

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

  1. appendfsync everysec

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

  1. appendfsync no

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

优势

  • 备份机制更稳健,丢失数据概率更低。
  • 可读的日志文本,通过操作AOF稳健,可以处理误操作。

劣势

  • 比起RDB占用更多的磁盘空间。
  • 恢复备份速度要慢。
  • 每次读写都同步的话,有一定的性能压力。

实时学习反馈

1. Redis技术中AOF持久化主要解决___问题。

A 节省磁盘空间

B 恢复备份速度要慢

C 比起RDB占用更多的磁盘空间

D 数据丢失

2.Redis技术中AOF同步频率设置每秒记录日志一次。

appendfsync always

appendfsync everysec

appendfsync no

appendonly yes

答案

1=>D 2=>B

Redis数据安全_如何选用持久化方式

image-20211221170313073

不要仅仅使用RDB

RDB数据快照文件,都是每隔5分钟,或者更长时间生成一次,这个时候就得接受一旦redis进程宕机,那么会丢失最近5分钟的数据。

也不要仅仅使用AOF

  1. 你通过AOF做冷备,没有RDB做冷备,来的恢复速度更快。
  2. RDB每次简单粗暴生成数据快照,更加健壮,可以避免AOF这种复杂的备份和恢复机制的bug。

综合使用AOF和RDB两种持久化机制

用AOF来保证数据不丢失,作为数据恢复的第一选择,用RDB来做不同程度的冷备,在AOF文件都丢失或损坏不可用的时候,还可以使用RDB来进行快速的数据恢复。

image-20211221170529631

实时学习反馈

1. Redis技术中企业中该如何选择持久化机制。

A 仅仅使用RDB

B 仅仅使用AOF

C 综合使用AOF和RDB两种持久化机制

D 不适用持久化

答案

1=>C

Redis事务_事务的概念与ACID特性

image-20211221172812856

数据库层面事务

在数据库层面,事务是指一组操作,这些操作要么全都被成功执行,要么全都不执行。

数据库事务的四大特性

  • A:Atomic,原子性,将所有SQL作为原子工作单元执行,要么全部执行,要么全部不执行;
  • C:Consistent,一致性,事务完成后,所有数据的状态都是一致的,即A账户只要减去了100,B账户则必定加上了100;
  • I:Isolation,隔离性,如果有多个事务并发执行,每个事务作出的修改必须与其他事务隔离;
  • D:Duration,持久性,即事务完成后,对数据库数据的修改被持久化存储。

Redis事务

Redis事务是一组命令的集合,一个事务中的所有命令都将被序列化,按照一次性、顺序性、排他性的执行一系列的命令。

image-20211221173122123

Redis事务三大特性

  1. 单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断;
  2. 没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”。
  3. 不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚;

Redis事务执行的三个阶段

image-20211221172945253

  • 开启:以MULTI开始一个事务;
  • 入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面;
  • 执行:由EXEC命令触发事务;

实时学习反馈

1. Redis事务说法错误的是__

A 没有隔离级别的概念

B 单独的隔离操作

C 保证原子性

2. Redis实现事务,是基于____

A 链表

B 数组

C 队列

D 栈

答案

1=>C 2=>C

Redis事务_Redis事务基本操作

image-20240201100557160

Multi、Exec、discard

事务从输入Multi命令开始,输入的命令都会依次压入命令缓冲队列中,并不会执行,直到输入Exec后,Redis会将之前的命令缓冲队列中的命令依次执行。组队过程中,可以通过discard来放弃组队。

例子

正常执行

127.0.0.1:6379> set t1 1
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set id 12
QUEUED
127.0.0.1:6379(TX)> get id
QUEUED
127.0.0.1:6379(TX)> incr t1
QUEUED
127.0.0.1:6379(TX)> incr t1
QUEUED
127.0.0.1:6379(TX)> get t1
QUEUED
127.0.0.1:6379(TX)> EXEC
1) OK
2) "12"
3) (integer) 2
4) (integer) 3
5) "3"

放弃事务

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set name z3
QUEUED
127.0.0.1:6379(TX)> set age 29
QUEUED
127.0.0.1:6379(TX)> incr t1
QUEUED
127.0.0.1:6379(TX)> DISCARD
OK


全体连坐

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set name z3
QUEUED
127.0.0.1:6379(TX)> get name
QUEUED
127.0.0.1:6379(TX)> incr t1
QUEUED
127.0.0.1:6379(TX)> get t1
QUEUED
127.0.0.1:6379(TX)> set email
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.


注意:

命令集合中含有错误的指令(注意是语法错误),均连坐,全部失败。

冤有头,债有主

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set age 11
QUEUED
127.0.0.1:6379(TX)> incr t1
QUEUED
127.0.0.1:6379(TX)> set email abc@163.com
QUEUED
127.0.0.1:6379(TX)> incr email
QUEUED
127.0.0.1:6379(TX)> get age
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (integer) 5
3) OK
4) (error) ERR value is not an integer or out of range
5) "11"


注意:

运行时错误,即非语法错误,正确命令都会执行,错误命令返回错误。

实时学习反馈

1. Redis技术中如何开启一个事务。

multi

start

exec

save

答案

1=>A

Redis集群_主从复制

image-20211222103514565

概述

在现有企业中80%公司大部分使用的是Redis单机服务,在实际的场景当中单一节点的Redis容易面临风险。

面临问题:

  1. 机器故障。我们部署到一台 Redis 服务器,当发生机器故障时,需要迁移到另外一台服务器并且要保证数据是同步的。
  2. 容量瓶颈。当我们有需求需要扩容 Redis 内存时,从 16G 的内存升到 64G,单机肯定是满足不了。当然,你可以重新买个 128G 的新机器。

解决办法

要实现分布式数据库的更大的存储容量和承受高并发访问量,我们会将原来集中式数据库的数据分别存储到其他多个网络节点上。

注意:

Redis 为了解决这个单一节点的问题,也会把数据复制多个副本部署到其他节点上进行复制,实现 Redis的高可用,实现对数据的冗余备份从而保证数据和服务的高可用。

什么是主从复制

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。

image-20211222104124324

主从复制的作用

  1. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  2. 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
  3. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  4. 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

实时学习反馈

1.单机Redis会遇到____问题。

A 响应

B 容量

C 速度

D 兼容

2. 下列不是Redis主从复制作用的是____

A 高可用

B 负载均衡

C 故障恢复

D 兼容性

答案

1=>B 2=>D         

Redis集群_主从复制环境搭建

image-20211222105320779

编写配置文件

新建redis6379.conf

include /usr/local/redis-7.2.4/redis.config

pidfile /var/run/redis_6379.pid

port 6379

dbfilename dump6379.rdb

新建redis6380.conf

include /usr/local/redis-7.2.4/redis.config

pidfile /var/run/redis_6380.pid

port 6380

dbfilename dump6380.rdb

新建redis6381.conf

include /usr/local/redis-7.2.4/redis.config

pidfile /var/run/redis_6381.pid

port 6381

dbfilename dump6381.rdb

启动三台redis服务器

./redis-server ../redis6379.conf

./redis-server ../redis6380.conf

./redis-server ../redis6381.conf

查看系统进程

[root@localhost src]# ps -ef |grep redis

root 40737 1 0 22:05 ? 00:00:00 ./redis-server *:6379

root 40743 1 0 22:05 ? 00:00:00 ./redis-server *:6380

root 40750 1 0 22:05 ? 00:00:00 ./redis-server *:6381

root 40758 40631 0 22:05 pts/0 00:00:00 grep --color=auto redis

查看三台主机运行情况

#打印主从复制的相关信息

./redis-cli -p 6379

./redis-cli -p 6380

./redis-cli -p 6381

127.0.0.1:6379> info replication

127.0.0.1:6380> info replication

127.0.0.1:6381> info replication

配从库  不配主库

语法格式:

slaveof <ip> <port>

示例:

在6380和6381上执行。

127.0.0.1:6380> SLAVEOF 127.0.0.1 6379

OK

在主机上写,在从机上可以读取数据

set k1 v1

Redis集群_主从复制原理剖析

img

主从复制可以分为3个阶段

  • 连接建立阶段(即准备阶段)
  • 数据同步阶段
  • 命令传播阶段

复制过程大致分为6个过程

image-20211221223008806

  • 1、保存主节点(master)信息。

执行 slaveof 后 查看状态信息

info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up


  • 2、从节点(slave)内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接

image-20211222101341470

  • 3、从节点与主节点建立网络连接

从节点会建立一个 socket 套接字,从节点建立了一个端口为51234的套接字,专门用于接受主节点发送的复制命令。

image-20211222101731424

  • 4、发送ping命令

    连接建立成功后从节点发送 ping 请求进行首次通信。

image-20211223114048536

作用:

  • 检测主从之间网络套接字是否可用。
  • 检测主节点当前是否可以接受命令 。
  • 4、权限验证。

如果主节点设置了 requirepass 参数,则需要密码验证,从节点必须配置 masterauth 参数保证与主节点相同的密码才能通过验证;如果验证失败复制将终止,从节点重新发起复制流程。

  • 5、同步数据集。

主从复制连接正常通信后,对于首次建立复制的场景,主节点会把持有的数据全部发送给从节点,这部分操作是耗时最长的步骤。

image-20211223114716229

主从同步策略

主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。

例如

保存一个缓

set name baizhan

记录命令为

$3 \r \n
set \r \n
$4 \r \n
name \r \n
$5  \r \n
baizhan \r \n


偏移量100010011002100310041005100610071008
字节值$3\r\n$4nam
  • 6、命令持续复制。

当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下来主节点会持续地把写命令发送给从节点,保证主从数据一致性。

实时学习反馈

1. Redis主从复制技术中主从刚刚连接的时候,进行___同步。

A 增量

B 全量

C 差异化

D 不进行同步

答案

1=>B

Redis集群_ 哨兵监控概述

image-20211222095454911

Redis主从复制缺点

当主机 Master 宕机以后,我们需要人工解决切换。

image-20211222100056841

暴漏问题:

一旦主节点宕机,写服务无法使用,就需要手动去切换,重新选取主节点,手动设置主从关系。

主从切换技术

当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式

哨兵概述

哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。

image-20211222100702198

哨兵作用

  • 集群监控:负责监控redis master和slave进程是否正常工作
  • 消息通知:如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
  • 故障转移:如果master node挂掉了,会自动转移到slave node上
  • 配置中心:如果故障转移发生了,通知client客户端新的master地址

实时学习反馈

1.Redis技术中哨兵模式解决____问题。

A 响应

B 主从切换

C 高可用

D 兼容

答案

1=>B

Redis集群_哨兵监控环境搭建

image-20211222181523362

新建sentinel-26379.conf文件

 

#端口
port 26379
#守护进程运行
daemonize yes
#日志文件
logfile "26379.log"
sentinel monitor mymaster localhost 6379 2


参数:

sentinel monitor mymaster 192.168.92.128 6379 2 配置的含义是:该哨兵节点监控192.168.92.128:6379这个主节点,该主节点的名称是mymaster,最后的2的含义与主节点的故障判定有关:至少需要2个哨兵节点同意,才能判定主节点故障并进行故障转移。

新建sentinel-26380.conf文件

 

#端口
port 26380
#守护进程运行
daemonize yes
#日志文件
logfile "26380.log"
sentinel monitor mymaster localhost 6379 2


新建sentinel-26381.conf文件

 

#端口
port 26381
#守护进程运行
daemonize yes
#日志文件
logfile "26381.log"
sentinel monitor mymaster localhost 6379 2

哨兵节点的启动

redis-sentinel sentinel-26379.conf

查看哨兵节点状态

 

[root@localhost src]# ./redis-cli -p 26379
127.0.0.1:26379> 
127.0.0.1:26379> 
127.0.0.1:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.66.100:6379,slaves=2,sentinels=3


Redis集群_哨兵工作原理详解

image-20211222181818930

监控阶段

image-20211222145241996

注意:

  • sentinel(哨兵1)----->向master(主)和slave(从)发起info,拿到全信息。
  • sentinel(哨兵2)----->向master(主)发起info,就知道已经存在的sentinel(哨兵1)的信息,并且连接slave(从)。
  • sentinel(哨兵2)----->向sentinel(哨兵1)发起subscribe(订阅)。

通知阶段

sentinel不断的向master和slave发起通知,收集信息。

故障转移阶段

通知阶段sentinel发送的通知没得到master的回应,就会把master标记为SRI_S_DOWN,并且把master的状态发给各个sentinel,其他sentinel听到master挂了,说我不信,我也去看看,并把结果共享给各个sentinel,当有一半的sentinel都认为master挂了的时候,就会把master标记为SRI_0_DOWN。

image-20211222145917372

问题来了:

这时就要把master给换掉了,到底谁当Master呢。

投票方式

img

方式:

自己最先接到哪个sentinel的竞选通知就会把票投给它。

剔除一些情况:

  1. 不在线的
  2. 响应慢的
  3. 与原来master断开时间久的
  4. 优先级原则

image-20211222151351680

实时学习反馈

1.Redis哨兵模式是Redis的___架构的一种方式。

A 高可用

B 高性能

C 高可靠

D 高并发

答案

1=>A

Redis集群_Cluster模式概述

image-20211224101923526

Redis有三种集群模式

  • 主从模式
  • Sentinel模式
  • Cluster模式

哨兵模式的缺点

image-20211222100702198

缺点:

  • 当master挂掉的时候,sentinel 会选举出来一个 master,选举的时候是没有办法去访问Redis的,会存在访问瞬断的情况;
  • 哨兵模式,对外只有master节点可以写,slave节点只能用于读。尽管Redis单节点最多支持10W的QPS,但是在电商大促的时候,写数据的压力全部在master上。
  • Redis的单节点内存不能设置过大,若数据过大在主从同步将会很慢;在节点启动的时候,时间特别长;

Cluster模式概述

image-20211224101351974

Redis集群是一个由多个主从节点群组成的分布式服务集群,它具有复制、高可用和分片特性。

Redis集群的优点

  • Redis集群有多个master,可以减小访问瞬断问题的影响
  • Redis集群有多个master,可以提供更高的并发量 
  • Redis集群可以分片存储,这样就可以存储更多的数据

实时学习反馈

1.Redis集群是一个由__节点群组成的分布式服务集群。

A 单个主从

B 单个

C 主从

D 多个主从

2.Redis哨兵模式的缺点是____

A 有主从模式主库异常手动主从切换的问题

B 高可用

C 哨兵选举期间,不能对外提供服务

D 读写分离,从库分担读操作

答案

1=>D 2=>C

Redis集群_Cluster模式搭建

image-20211224144441122

Redis的集群搭建最少需要3个master节点,我们这里搭建3个master,每个下面挂一个slave节点,总共6个Redis节点;

image-20211224101351974

集群搭建

创建6个不同的redis节点,端口号分别为6379、6380、6381、6382、6383、6384。

注意:拷贝前必须删除dump.rdb和appendonly.aof文件

1、新建配置文件

创建redis6379.config,redis6380.config,redis6381.config,redis6382.config,redis6383.config,redis6384.config文件修改配置文件中端口号使其和文件端口号对应。

 

daemonize yes
dir /usr/local/redis-7.2.4/redis-cluster/6382/
bind 192.168.47.100
port 6382
dbfilename dump6382.rdb
cluster-enabled yes
cluster-config-file nodes-6382.conf
cluster-node-timeout 5000
appendonly yes
protected-mode no


参数:

  • cluster-config-file : 集群持久化配置文件,内容包含其它节点的状态,持久化变量等,会自动生成在上面配置的dir目录下。每个节点在运行过程中,会维护一份集群配置文件;每当集群信息发生变化时(如增减节点),集群内所有节点会将最新信息更新到该配置文件;当节点重启后,会重新读取该配置文件,获取集群信息,可以方便的重新加入到集群中。集群配置文件由Redis维护,不需要人工修改。
  • clouster-enabled: 开启集群

创建文件夹

 

mkdir -p /usr/local/redis-7.2.4/redis-cluster/6379/
mkdir -p /usr/local/redis-7.2.4/redis-cluster/6380/
mkdir -p /usr/local/redis-7.2.4/redis-cluster/6381/
mkdir -p /usr/local/redis-7.2.4/redis-cluster/6382/
mkdir -p /usr/local/redis-7.2.4/redis-cluster/6383/
mkdir -p /usr/local/redis-7.2.4/redis-cluster/6384/


启动六个节点

[root@bogon src]# ./redis-server ../redis6379.config
[root@bogon src]# ./redis-server ../redis6380.config
[root@bogon src]# ./redis-server ../redis6381.config
[root@bogon src]# ./redis-server ../redis6382.config
[root@bogon src]# ./redis-server ../redis6383.config
[root@bogon src]# ./redis-server ../redis6384.config

查看各个节点是否启动成功

[root@bogon src]# ps -ef | grep redis
root    3889    1 0 09:56 ?    00:00:03 ./redis-server 0.0.0.0:6379 [cluster]
root    3895    1 0 09:56 ?    00:00:03 ./redis-server 0.0.0.0:6380 [cluster]
root    3901    1 0 09:57 ?    00:00:03 ./redis-server 0.0.0.0:6381 [cluster]
root    3907    1 0 09:57 ?    00:00:02 ./redis-server *:6382 [cluster]
root    3913    1 0 09:57 ?    00:00:02 ./redis-server 0.0.0.0:6383 [cluster]
root    3919    1 0 09:57 ?    00:00:02 ./redis-server 0.0.0.0:6384 [cluster]
root    4247  2418 0 10:22 pts/0  00:00:00 grep --color=auto redis

配置集群

命令格式: --cluster-replicas 1 表示为每个master创建一个slave节点

注意:这里的IP为每个节点所在机器的真实IP

[root@localhost src]# ./redis-cli --cluster create 192.168.47.100:6379 192.168.47.100:6380 192.168.47.100:6381 192.168.47.100:6382 192.168.47.100:6383 192.168.47.100:6384 --cluster-replicas 1

image-20240204145221738

验证集群

连接任意一个客户端

./redis-cli -h 192.168.47.100 -p 6379 -c

参数:

  • ‐h : host地址
  • -p : 端口号
  • ‐c : 表示集群模式

数据写测试

 

[root@bogon src]# ./redis-cli -p 6379 -c
127.0.0.1:6379> set name zhangsan
-> Redirected to slot [5798] located at 192.168.47.100:6380
OK
192.168.47.100:6380> get name
"zhangsan"
192.168.47.100:6380>
[root@bogon src]# ./redis-cli -p 6383 -c
127.0.0.1:6383> get name
-> Redirected to slot [5798] located at 192.168.47.100:6380
"zhangsan"
192.168.47.100:6380>
[root@bogon src]# ./redis-cli -p 6383 -c
127.0.0.1:6383> readonly
OK
127.0.0.1:6383> get name
"zhangsan"
127.0.0.1:6383>


Redis集群_Cluster模式原理分析

img

Redis Cluster将所有数据划分为16384个slots(槽位),每个节点负责其中一部分槽位。槽位的信息存储于每个节点中。只有master节点会被分配槽位,slave节点不会分配槽位。

img

槽位定位算法: k1 = 127001

Cluster 默认会对 key 值使用 crc16 算法进行 hash 得到一个整数值,然后用这个整数值对 16384 进行取模来得到具体槽位。

HASH_SLOT = CRC16(key) % 16384

故障恢复

查看节点
192.168.66.103:8001> cluster nodes
杀死Master节点
lsof -i:8001
 
kill -9 pid
观察节点信息

image-20211224151850502

实时学习反馈

1. Redis Cluster模式默认会对key值使用__算法进行hash得到一个整数值,然后用这个整数值对16384 进行取模来得到具体槽位。

crc-8

crc-16

md5

des

答案

1=>B

Redis集群_Java操作Redis集群

image-20211224161614733

修改配置文件

spring.data.redis.cluster.nodes=192.168.47.100:6381,192.168.47.100:6383,192.168.47.100:6380

注意事项

  • 1、Redis集群需要至少3个节点才能保证高可用性。
  • 2、应该尽量避免在Redis集群的运行过程中添加或删除节点,因为这可能导致数据迁移,进而影响Redis集群的整体性能。
Java编写的代码
@SpringBootTest
public class CluseterTest {@Autowiredprivate RedisTemplate<String,Object> redisTemplate;@Testvoid string() {//  保存字符串redisTemplate.opsForValue().set("itbaizhan","itbaizhan123");System.out.println(redisTemplate.opsForValue().get("itbaizhan"));}
}

Redis企业级解决方案_Redis脑裂

image-20211224111123960

什么是Redis的集群脑裂

Redis的集群脑裂是指因为网络问题,导致Redis Master节点跟Redis slave节点和Sentinel集群处于不同的网络分区,此时因为sentinel集群无法感知到master的存在,所以将slave节点提升为master节点。

image-20211224172028672

注意:

此时存在两个不同的master节点,就像一个大脑分裂成了两个。集群脑裂问题中,如果客户端还在基于原来的master节点继续写入数据,那么新的Master节点将无法同步这些数据,当网络问题解决之后,sentinel集群将原先的Master节点降为slave节点,此时再从新的master中同步数据,将会造成大量的数据丢失。

解决方案

redis.conf配置参数:

min-replicas-to-write 1
min-replicas-max-lag 5


参数:

  • 第一个参数表示最少的slave节点为1个
  • 第二个参数表示数据复制和同步的延迟不能超过5秒

配置了这两个参数:如果发生脑裂原Master会在客户端写入操作的时候拒绝请求。这样可以避免大量数据丢失。

!!!

其实主要记住:脑裂就是在redis集群过程中出现了两个master,然后为客户端提供服务,他会出现数据丢失的现象。

实时学习反馈

1.Redis的集群脑裂是指_____问题。

A 连接

B 复制

C 同步

D 网络

2.Redis的集群脑裂通过配置____命令解决。

daemonize

tcp-keepalive

min-replicas-max-lag

slave-serve-stale-data

答案

1=>D 2=>C

Redis企业级解决方案_缓存预热

image-20211224111611519

缓存冷启动

缓存中没有数据,由于缓存冷启动一点数据都没有,如果直接就对外提供服务了,那么并发量上来Mysql就裸奔挂掉了。

image-20211228103342981

缓存冷启动场景

新启动的系统没有任何缓存数据,在缓存重建数据的过程中,系统性能和数据库负载都不太好,所以最好是在系统上线之前就把要缓存的热点数据加载到缓存中,这种缓存预加载手段就是缓存预热。

解决思路

  • 提前给redis中灌入部分数据,再提供服务
  • 如果数据量非常大,就不可能将所有数据都写入redis,因为数据量太大了,第一是因为耗费的时间太长了,第二根本redis容纳不下所有的数据
  • 需要根据当天的具体访问情况,实时统计出访问频率较高的热数据
  • 然后将访问频率较高的热数据写入redis中,肯定是热数据也比较多,我们也得多个服务并行读取数据去写,并行的分布式的缓存预热

image-20211228104437180

实时学习反馈

1.Redis技术中缓存预热主要解决____问题。

A 缓存冷启动

B 安全

C 响应速度

D 高可用

答案

1=>A

Redis企业级解决方案_缓存穿透

image-20211224113501765

概念

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

img

解释:

缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。

解决方案

  1. 对空值缓存:如果一个查询返回的数据为空(不管数据是否存在),我们仍然把这个空结果缓存,设置空结果的过期时间会很短,最长不超过5分钟。
  2. 布隆过滤器:如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定。

布隆过滤器

什么是布隆过滤器

布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。

image-20211227144957707

注意:

布隆说不存在一定不存在,布隆说存在你要小心了,它有可能不存在。

代码实现

引入hutool包
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.17</version>
</dependency>

java代码实现

// 初始化 注意 构造方法的参数大小10 决定了布隆过滤器BitMap的大小BitMapBloomFilter filter = new BitMapBloomFilter(10);filter.add("123");filter.add("abc");filter.add("ddd");boolean abc = filter.contains("abc");System.out.println(abc);

!!!

缓存穿透要用布隆过滤器解决,他是一个概率型的数据结构,特点是高效的插入和查询(阔以存几百亿上千亿的数据,能做到毫秒级反应,缺点是可能会误判也就是他说不存在一定不存在,他说存在那么是有可能不存在的

实时学习反馈

1.布隆过滤器是一种数据结构,底层是_____。

A 链表

B 数组

C bit数组

D 队列

2.Redis技术中缓存穿透主要指____问题。

A 缓存和数据库中都没有的数据

B 缓存冷启动

C 缓存中没有但数据库中有的数据

D 缓存中数据大批量到过期时间

答案

1=>C 2=>A

Redis企业级解决方案_缓存击穿

img

概念

某一个热点 key,在缓存过期的一瞬间,同时有大量的请求打进来,由于此时缓存过期了,所以请求最终都会走到数据库,造成瞬时数据库请求量大、压力骤增,甚至可能打垮数据库。

image-20240204162303742

解决方案

  1. 互斥锁:在并发的多个请求中,只有第一个请求线程能拿到锁并执行数据库查询操作,其他的线程拿不到锁就阻塞等着,等到第一个线程将数据写入缓存后,其他线程直接查询缓存。
  2. 热点数据不过期:直接将缓存设置为不过期,然后由定时任务去异步加载数据,更新缓存。

代码实现

 private final String LOCK_PREFIX = "lock:";public User getUser(Long id) throws InterruptedException {//设置个KeyUser user = (User) template.opsForValue().get("user:" + id);if (user != null) {return user;} else {Boolean b = tryLock(LOCK_PREFIX + id, "1", 100, TimeUnit.SECONDS);if (b) {log.info("开始执行业务逻辑");// 1、从数据库中查询User u = userMapper.selectById(id);// 2、加Redis缓存template.opsForValue().set("user:" + id, u);return u;} else {Thread.sleep(2000);return getUser(id);}}}// 抢占式 互斥加锁public Boolean tryLock(String key, String value, long timeout, TimeUnit unit) {// setIfAbsent已经是setNx + expire的合并命令return template.opsForValue().setIfAbsent(key, value, timeout, unit);}

实时学习反馈

1.Redis技术中缓存击穿如何解决。

A 对空值缓存

B 搭建Redis高可用

C 使用布隆过滤器

D 加互斥锁

答案

1=>D

Redis企业级解决方案_Redis开发规范

image-20211224114622192

key设计技巧

  • 1、把表名转换为key前缀,如tag:
  • 2、把第二段放置用于区分key的字段,对应msyql中主键的列名,如user_id
  • 3、第三段放置主键值,如2,3,4
  • 4、第四段写存储的列名
user_idnameage
1baizhan18
2itbaizhan20

示例

#  表名 主键 主键值 存储列名字  
set user:user_id:1  {json}
set user:user_id:1:age 20
#查询这个用户
keys user:user_id:9*

value设计

拒绝bigkey

防止网卡流量、慢查询,string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。

命令使用

1、禁用命令

禁止线上使用keys、flushall、flushdb等,通过redis的rename机制禁掉命令,或者使用scan的方式渐进式处理。

2、合理使用select

redis的多数据库较弱,使用数字进行区分,很多客户端支持较差,同时多业务用多数据库实际还是单线程处理,会有干扰。

3、使用批量操作提高效率
  • 原生命令:例如mget、mset。
  • 非原生命令:可以使用pipeline提高效率。

注意:

但要注意控制一次批量操作的元素个数(例如500以内,实际也和元素字节数有关)。

4、不建议过多使用Redis事务功能

Redis的事务功能较弱(不支持回滚),而且集群版本(自研和官方)要求一次事务操作的key必须在一个slot上。

客户端使用

  1. Jedis :https://github.com/xetorthio/jedis 重点推荐
  2. Spring Data redis :https://github.com/spring-projects/spring-data-redis 使用Spring框架时推荐
  3. Redisson :https://github.com/mrniko/redisson 分布式锁、阻塞队列的时重点推荐
1、避免多个应用使用一个Redis实例

不相干的业务拆分,公共数据做服务化。

2、使用连接池

可以有效控制连接,同时提高效率,标准使用方式:

执行命令如下:
Jedis jedis = null;
try {jedis = jedisPool.getResource();//具体的命令jedis.executeCommand()
} catch (Exception e) {logger.error("op key {} error: " + e.getMessage(), key, e);
} finally {//注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。if (jedis != null)jedis.close();
}

实时学习反馈

1.Redis技术中下列符合key设计__

user.id

user.id.1.age

user:id.1.age

user:id:1:age

答案

1=>C

Redis企业级解决方案_数据一致性

image-20211224115250315

缓存已经在项目中被广泛使用,在读取缓存方面,大家没啥疑问,都是按照下图的流程来进行业务操作。

image-20211227103422548

缓存说明:

从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。

三种更新策略

  1. 先更新数据库,再更新缓存
  2. 先删除缓存,再更新数据库
  3. 先更新数据库,再删除缓存

先更新数据库,再更新缓存

这套方案,大家是普遍反对的。为什么呢?

线程安全角度

同时有请求A和请求B进行更新操作,那么会出现

(1)线程A更新了数据库 (2)线程B更新了数据库 (3)线程B更新了缓存 (4)线程A更新了缓存

这就出现请求A更新缓存应该比请求B更新缓存早才对,但是因为网络等原因,B却比A更早更新了缓存。这就导致了脏数据,因此不考虑。

先删缓存,再更新数据库

该方案会导致不一致的原因是。同时有一个请求A进行更新操作,另一个请求B进行查询操作。那么会出现如下情形:

(1)请求A进行写操作,删除缓存 (2)请求B查询发现缓存不存在 (3)请求B去数据库查询得到旧值 (4)请求B将旧值写入缓存 (5)请求A将新值写入数据库

注意:

该数据永远都是脏数据。

先更新数据库,再延时删缓存

img

这种情况存在并发问题吗?

(1)缓存刚好失效 (2)请求A查询数据库,得一个旧值 (3)请求B将新值写入数据库 (4)请求B删除缓存 (5)请求A将查到的旧值写入缓存

发生这种情况的概率又有多少?

发生上述情况有一个先天性条件,就是步骤(3)的写数据库操作比步骤(2)的读数据库操作耗时更短,才有可能使得步骤(4)先于步骤(5)。可是,大家想想,数据库的读操作的速度远快于写操作的,因此步骤(3)耗时比步骤(2)更短,这一情形很难出现。

实时学习反馈

1.Redis技术中哪一种保证数据一致性最合理。

A 先更新数据库,再更新缓存

B 先删除缓存,再更新数据库

C 先更新数据库,再删除缓存,设置过期时间

D 设置过期时间

答案

1=>D


文章转载自:

http://c3MK0iLj.hnrLs.cn
http://xMxaFVZv.hnrLs.cn
http://E5AkWqUj.hnrLs.cn
http://6sukvoRH.hnrLs.cn
http://DpZWkqRG.hnrLs.cn
http://c160gcbZ.hnrLs.cn
http://b4UDbvuP.hnrLs.cn
http://YDxeVJGl.hnrLs.cn
http://BqN3r9Mr.hnrLs.cn
http://jng6Rs2R.hnrLs.cn
http://i5eDZZuY.hnrLs.cn
http://TrI7zJfT.hnrLs.cn
http://BUSxamlJ.hnrLs.cn
http://0gHW4rPZ.hnrLs.cn
http://bRmZ5E2q.hnrLs.cn
http://XgInnRLU.hnrLs.cn
http://49uDlInw.hnrLs.cn
http://LqHxnkcK.hnrLs.cn
http://I7wDj58O.hnrLs.cn
http://hQEq9CdU.hnrLs.cn
http://GQwowRhQ.hnrLs.cn
http://kzfbinAG.hnrLs.cn
http://Q8FvcDsP.hnrLs.cn
http://bFr7Fodd.hnrLs.cn
http://amAlKXYh.hnrLs.cn
http://sQh28eMm.hnrLs.cn
http://g6pfMIFE.hnrLs.cn
http://r3mfBYD6.hnrLs.cn
http://vjNsOdEk.hnrLs.cn
http://xJE9v9IH.hnrLs.cn
http://www.dtcms.com/a/375953.html

相关文章:

  • pybind11错误书
  • 在 PostgreSQL中查看有哪些用户和用户权限
  • ctfshow- web入门-XXE漏洞
  • 六级第二关———坐地铁(1)
  • 实用 html 小工具
  • C#(链表创建与原地反转)
  • 光伏MPPT——拓扑结构及发波方式
  • Flink通讯超时问题深度解析:Akka AskTimeoutException解决方案
  • 美团核销接口助力第三方供应商拓展市场份额的策略
  • 基于dijkstra算法的WSN网络MAC协议matlab仿真,分析网络延迟与网络开销
  • 《Linux运维工程师基础技能测试简答题》
  • CPUID
  • aiagent知识点
  • DPO原理 | 公式推导
  • 代码随想录算法训练营第三十九天|62.不同路径 63.不同路径ll
  • Redis(主从复制)
  • 嵌入式 - ARM3
  • 【QT随笔】结合应用案例一文完美概括QT中的队列(Queue)
  • lesson57:CSS媒体查询完全指南:从基础语法到移动端响应式设计最佳实践
  • 定制 ResourceBundle 的实现与 DuiLib 思想在 Chromium 架构下的应用解析
  • 常用排序算法核心知识点梳理
  • Dubbo3序列化安全机制导致的一次生产故障
  • 《2025年AI产业发展十大趋势报告》四十七
  • 传统项目管理中如何控制进度
  • C 语言第一课:hello word c
  • Cartographer 位姿推测器pose_extrapolator
  • Matlab机器人工具箱使用5 轨迹规划
  • 【git】Git 大文件推送失败问题及解决方案
  • ctfshow-web入门-php特性(二)
  • CSP认证练习题目推荐 (1)