抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

计算机就是数据结构和算法.

redis的学习网站:

  1. redis.cn

  2. redis.io

  3. db-engines.com

API代码的学习:

1
2
3
1.  redis.io 的client 中有JAVA语言的客户端:jedis、lettuce等可以分别访问他们的github学习
2. 另外是基于spring的使用,主动通过spring.io官网学习spring.data.redis
3. spring.io中: https://spring.io/projects/spring-data-redis

基础概念

为什么要使用Redis

文件里 数据 data.txt,当查询某个字段的时候grep awk,java但是随着文件变大,速度变慢为什么?
因为:硬盘i/o成为瓶颈。查询有一个过程,取出比较。主要取决于取出的速度。

💖常识:

磁盘:

  1. 寻址:ms

  2. 带宽:G/M

内存:

  1. 寻址:ns

  2. 带宽:很大

    秒>毫秒>微秒>纳秒 磁盘比内存在寻址上慢了10W倍

I/O buffer:成本问题

磁盘与磁道,扇区,一扇区 512Byte带来一个成本变大:索引

windows操作系统,一个扇区4K,4K 操作系统,无论你读多少,都是最少4k从磁盘拿,4K对其。

😘关系型数据库特性

磁盘中:数据+索引,内存中B+T树。
image-20210313160025961

关系型数据库建表:必须给出schema

类型:字节宽度。

存:倾向于行级存储。

(✿◡‿◡)面试题:

数据库:表很大,性能下降?

如果表有索引

维护索引会使得,增删改变慢。

查询速度呢?

  1. 1个或少量查询依然很快。

  2. 并发大的时候会受硬盘带宽影响速度。

💕技术选型

数据库对比网站:https://db-engines.com/en/

架构师:

  1. 技术选型

数据在磁盘和内存体积不一样,SAPHANA内存级别的关系型数据库2T,这种造价太高。

取折中的方案,使用缓存

  1. memcached

  2. redis

速度主要是由个基础设施影响:

  1. 冯诺依曼体系的硬件

  2. 以太网,tcp/ip的网络

Redis简介

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。

它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。

Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

😎Redis和memcache的区别
image-20210313162711212

总结一句话:计算像数据移动。

字符集的的使用

字符集 ascii,其他一般叫做扩展字符集。

扩展: 其他字符集不在对ascii重编码。

0xxxxxxx,以0开头的肯定是ascii编码,其他字符集一遇到就默认使用ascii了。

你自己写一个程序,字节流读取,每字节判断。

Redis安装

😊命令执行过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1,yum install wget
2,cd ~
3,mkdir soft
4,cd soft
5,wget http://download.redis.io/releases/redis-5.0.5.tar.gz
6,tar xf redis...tar.gz
7,cd redis-src
8,看README.md
9, make
....yum install gcc
....make distclean
10,make
11,cd src ....生成了可执行程序
12, cd ..
13,make install PREFIX=/usr/local/redis
14,vi /etc/profile
... export REDIS_HOME=/opt/mashibing/redis5
... export PATH=$PATH:$REDIS_HOME/bin
..source /etc/profile
15,cd utils
16,./install_server.sh (可以执行一次或多次)
a) 一个物理机中可以有多个redis实例(进程),通过port区分
b) 可执行程序就一份在目录,但是内存中未来的多个实例需要各自的配置文件,持久化目录等资源
c) service redis_6379 start/stop/stauts > linux /etc/init.d/****
d)脚本还会帮你启动!
f) /etc/redis/ 目录中会生成redis启动的配置文件,以后也可以通过,redis-server 搭配此配置文件进行服务的启动。
17,ps -fe | grep redis

卸载Redis

find / -name redis

查找redis的文件目录,删除即可。

常用命令

查看Redis

查看Redis的bin被安装位置

1
2
whereis redis-cli
whereis redis-server

=======
测试结果,只会找配置在环境变量中的目录下的。

查看Redis的进程:

1
ps -ef |grep redis

查看Redis版本:

1
./redis-server -v

登录Redis

1
./redis-cli -h IP地址 -p 端口 -a 密码  -c  --raw
参数描述
-c开启reidis cluster模式,连接redis cluster节点时候使用。
-p端口号,不写此参数就是用默认端口
-hip地址,不写,默认本机。
--raw使用客户端的指定的字符集进行解码显示。

启动Redis

1
2
3
4
5
6
7
8
#如果在服务目录/etc/init.d/****中注册了脚本.
service redis_6379 start

#使用客户端命令的启动方式,搭配配置文件即可。
./redis-server /etc/redis/6379.confg

#如果配置了环境变量,手工启动测试,会将当前启动的目录作为,持久化目录,所以必须先创建目录.
redis-server --port 6379
参数描述
--loadmodule加载模块启动服务

停止Redis

强制停止法:

1
2
3
ps -ef | grep redis  #查看端口占用的进程

kill -9 进程的pid #强制停止进程

登录客户端停止法:

1
2
3
4
5
6
7
# 登录命令
./redis-cli ......
#停止
shoutdown

#或者联合起来使用
./redis-cli -p 6379 shutdown

服务停止法:

1
service redis_6379 stop

查看Redis配置:

1
2
redis-cli ...  #登录redis
CONFIG GET * #获取配置。

conf配置文件

引入公共的配置:
image-20210318092941190

加载模块配置
image-20210318093014144

绑定能够访问的IP地址
image-20210318093349733

默认是本机。如果需要在虚拟机外部的程序访问需要修改为:

1
bind 0.0.0.0

是否允许外部主机访问。
image-20210318093445106

是否为后台服务模式
image-20210318093643043

启动后,进程的pid文件。放在这个目录下。通过pid文件判定进程是否在运行。
image-20210318093752634

日志级别
image-20210318093937957

日志存放在那个目录下:
image-20210318094051461

库的数量:
image-20210318094218580

需要密码登录:
image-20210318094408735

将一些敏感的命令重命名,屏蔽。
image-20210318094542304

允许最大连接数:
image-20210318094657329

最大内存,根据经验一般是1G~10G。
image-20210318094916390

内核演变过程

演变

image-20210313164359128

🤢阻塞时期:
image-20210313164448116

cpu只有1颗

JVM: 一个线程的成本 1MB

  1. 线程多了调度成本CPU浪费。

  2. 内存成本。

🤔非阻塞时期
image-20210313164726540

同步非阻塞 NIO

如果有1000fd,代表用户进程轮询调用1000次kernel,成本问题。

😃select,多路复用NIO时期。

image-20210313165341855

fd相关数据考来考去。数据拷贝和传参调用成本比较高。

😎epoll,多路复用NIO时期。
image-20210313165629053

共享空间mmap,降低了传参调用。

😍🤷‍♀️mmap,sendfile时期。

image-20210313170729325

也就是现在的卡夫卡(kafka)技术的出现。减少数据拷贝和传参调用。

Redis内核

image-20210313172750644

redis的每个服务端都是,单进程,单线程,单实例。

单线程单进程的好处:“顺序”性,每连接内的命令顺序。

Value结构和操作命令:

客户端help命令可以查看所有命令的用法。
image-20210313173926024

字符串

image-20210313173204325

redis所有存储的都是字节流。如果时字符串,取决于客户端的编码格式,所以一定要统一编码。

每一个字节,都存在正反向索引:
image-20210313191623242

set

设置值。
image-20210313174158906

get

获取key的值。
image-20210313174245296

append

字符串追加。
image-20210313174527895
image-20210313174624584

setrange

从某个位置开始覆盖替换字符串。
image-20210313174954008

getrange

获取一段字符串,从字节位置开始。所以用来取中文就不太方便了。
image-20210313175510267

strlen

获取字节数量意义上的长度。
image-20210313175901455

数值

image-20210313180413871

incr

从原来的数上+1.

image-20210313180326263

用途场景:
抢购,秒杀,详情页,点赞,评论
规避并发下,
对数据库的事务操作
完全由redis内存操作代替

bitmap

🤷‍♀️直接操作二进制位,牛逼。

image-20210313180832154

setbit

针对某个二进制位进行设置(0/1):
image-20210313181505822
image-20210313190454924

1
2
3
setbit k1 1 1
setbit k1 7 1
setbit k1 9 1

image-20210313191121942

bitcount

根据字节角标,计算二进制位1存在的数量。
image-20210313181935487

bitpos

根据字节角标,命令返回字符串里面第一个被设置为1或者0的bit位。出现的起始位置坐标。
image-20210313184144821

🐱‍👤注意:需要指定查询出现的0或者1。

BITPOS k1 0 0

第一个参数0指定查询0

第二个参数是0指定从第0个字节角标开始。

bitop

将多个bitmap进行逻辑计算操作,并将结果存入新的bitmap中。
image-20210313185329239

使用场景

  1. 有用户系统,统计用户登录天数,且窗口随机.

    😏实现思路:用户id作为key,将二进制位1存储表示已登陆的实际第几天。

    1
    2
    3
    4
    5
    setbit sean 1 1
    setbit sean 7 1
    setbit sean 364 1
    STRLEN sean
    BITCOUNT sean -2 -1
  2. 京东就是你们的,618做活动:送礼物,大库备货多少礼物?

    假设京东有2E用户

    僵尸用户

    冷热用户/忠诚用户

    活跃用户统计!随即窗口

    比如说 1号~3号 连续登录要 去重

    😏实现思路:天数作为key,二进制位代表实际用户,已登录,用1表示。

    1
    2
    3
    4
    5
    setbit 20190101  1 1
    setbit 20190102 1 1
    setbit 20190102 7 1
    bitop or destkey 20190101 20190102
    BITCOUNT destkey 0 -1

List

链表结构,存在争对元素的正负索引。

image-20210313192511217

😁口诀:同向命令模拟栈操作,逆向命令模拟队列操作。

lpush

从左向右存入元素。

lpop

按从左向右存入的顺序,弹出最后存入的数据。

lpush+lpop 同向命令模拟栈的使用。
image-20210313194522973

rpush

从右向左存入元素

rpop

按从右向左存入的顺序,弹出最后存入的数据。

lpush+rpop逆向命令模拟队列的数据结构。

LRANGE

按照给定的范围,取出list中的值。lpush的list是栈取出。rpush的list是队列取出。
image-20210313195221779

LINDEX

按照索引取出值。操作索引就相当于数组结构了。

image-20210407194644342

LSET

按照索引设置对于的某个值.

LREM

从list中,按照给定的数量,移除某个值.数量为正数,正向移除,为负数,逆向移除.
image-20210313203240738

LINSERT

在list中的某个元素前后插入元素。
image-20210313203610647

llen

统计有多少个元素。

blpop brpop

阻塞弹出元素。实现消息的单播队列。谁先阻塞,谁先拿到元素。FIFO,先进先出。

image-20210313203928005

开启窗口1 2 3 。

窗口1先进入阻塞,等待k1弹出。
image-20210313204127871

窗口2后进入阻塞,等待k2弹出。

窗口3,压入元素。
image-20210313204301276

窗口1,先收到弹出的元素。
image-20210313204338574

窗口3,再压入元素。
image-20210313204426379

窗口2,收到弹出的元素。
image-20210313204545509

LTRIM

对两端外的数据进行移除和删除。不包括端的元素。
image-20210313204616252

Hash

image-20210313204918924

hset

存hash,键值对类型的数据。
image-20210313213236238

hmset

在一个key的value中,存放多个,键值对类型数据。

hget

取出

hmget

根据key的values中根据多个键取值。

hkeys

取出所有的键。

hvals

取出所有的值。

hgetall

取出所有的键和值。

hincrbyfloat

将某个map中的值进行浮点数计算。
image-20210313213801505

Set

最有优势的就是去重。

image-20210314071703405

sadd

集合中添加元素.
image-20210314065726468

smembers

显示集合中的元素.
image-20210314070835592

sinter

做多个集合的交集,不存放结果.,通过io将结果输出.
image-20210314071107943

sinterstore

做多个集合的交集,存放结果.没有io的交互.​👓​
image-20210314071355655

sunion

做并集

sunionstore

做并集并保存.

sdiff

差集.以第一个元素为基础取差集。
image-20210314071526720

srandmember

随机数
SRANDMEMBER key count
正数:取出一个去重的结果集(不能超过已有集),超过已有集数量返回整个set集合。
负数:取出一个带重复的结果集,一定满足你要的数量。
如果:0,不返回
image-20210314072014223
🎑应用场景:

  1. 抽奖:
    10个奖品
    用户:
    <10 使用负数抽取。
    >10 使用正数抽取。
    中奖:是否重复

  2. 解决家庭争斗!
    取名字的时候。5个名字,取10次(可重复)

spop

将集合元素随机弹出。每次只弹出一个。
image-20210314072729670

sorted_set

理解排序的集合中,内部存储方式。对每个元素内置了一个分值,按照分值来排序的。

image-20210314075256639

image-20210314075228310

🎆**面试问题:**排序是怎么实现的,增删改查的速度?

​ 答:使用了跳跃表的概念。吸收存储空间,换取未来的查询速度。
image-20210314110258844

zadd

集合中添加元素。
image-20210314075706277

zrange

取出集合中的元素,按照元素角标顺序。
image-20210314080759460

zrangebyscore

通过分值范围取出元素。
image-20210314080941980
image-20210314080959723

zrevrange

逆向取出元素。从大到小取出。
image-20210314083926241
image-20210314083639171

zscore

通过元素取出分值。image-20210314084020929
image-20210314084238321

zrank

通过元素,取出排名,就是索引角标。
image-20210314084455313

zincrby

增加元素的分值,并且根据分值实时维护,元素的顺序。
image-20210314084753185

🎗场景:

歌曲排行榜。倒叙显示。每播放一次+1.

zunionstore

并集操作。

image-20210314100615228
参数numkeys表示参数运算的key。
weight权重,与对应集合中的分值相乘计算后,参数与最后的集合与几何间的运算。

sum|min|max,集合元素的运算,默认为加法。
image-20210314101133351

权重运算:
image-20210314101847231

求最大值运算:
image-20210314102354089

管道pipeline的使用

接住管道可以一次发送多个命令,节省往返时间。

建立socket链接
nc localhost 6379

将多个命令,通过管道传入redis执行。

1
echo -e "set k2 99\nincr k2\n get k2 | nc localhost 6379"

一般用于冷加载,启动的时候,将一些预留的数据放到redis中。

发布订阅

pubsub

image-20210315202134107

publish

向管道中添加元素。
image-20210315202347140

SUBSCRIBE

接受元素,监听之后别人发的消息才能收到.
image-20210315202449934

✌🤞**使用场景:**微信聊天窗口。
image-20210315204035666

image-20210315210755333

事务的使用

永远记住一句话,速度快时特征,才会去选用Redis。Redis设计的事务也是往速度方面设计考虑,不是特别复杂。
image-20210315212104221

为什么 Redis 不支持回滚(roll back)
如果你有使用关系式数据库的经验, 那么 “Redis 在事务失败时不进行回滚,而是继续执行余下的命令”这种做法可能会让你觉得有点奇怪。以下是这种做法的优点:

​ Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。
​ 有种观点认为 Redis 处理事务的做法会产生 bug , 然而需要注意的是, 在通常情况下, 回滚并不能解决编程错误带来的问题。 举个例子, 如果你本来想通过 INCR 命令将键的值加上 1 , 却不小心加上了 2 , 又或者对错误类型的键执行了 INCR , 回滚是没有办法处理这些情况的。

MULTI
开启事务

EXEC
关闭事务,谁的exe先到达,谁的事务就先执行。

WATCH
乐观锁。

image-20210315212201608

例:

窗口1:watch k1 开启事务
image-20210315213811132
窗口2:开启事务,修改k1,关闭执行事务。
image-20210315214119091

窗口3:执行事务,由于k1被修改了,事务执行失败。
image-20210315214010903

Module布隆过滤器的使用

增加Redis的功能。

image-20210316204956533

安装bloom-filter

  1. 访问redis.io

  2. 点开modules

  3. 访问RedisBloom的githubhttps://github.com/RedisBloom/RedisBloom

  4. linux中wget *.zip

  5. yum install unzip

  6. unzip *.zip

  7. make

  8. cp bloom.so /opt/mashibing/redis5/

  9. redis-server --loadmodule /usr/local/redis/redisbloom.so 。
    🎨**注意:**此处启动,关联布隆过滤器的函数库redisbloom.so文件一定要用绝对路径。不能指定配置文件。才能启动成功。是属于临时启动。

命令

BF.ADD

布隆过滤器中添加元素。
image-20210327200702582
image-20210327201004521

BF.EXISTS

验证是否存在。
image-20210327200945858

返回1存在,返回0不存在。
image-20210327201024168

CF.ADD

布谷鸟过滤器相关的命令。

解决穿透问题

image-20210327201127446

概率解决问题,不可能百分百阻挡,>1%

1,你有啥

2,有的向bitmap中标记

3,请求的可能被误标记

4,但是,一定概率会大量减少放行:穿透

5,而且,成本低

几种分布式实现方式

image-20210327201246738
第三种最优。最符合分布式架构的思想。

过滤器种类介绍

过滤器:

bloom

布隆过滤器

counting bloom

cukcoo

布谷鸟过滤器

穿透了,但是数据库不存在的处理

  1. client,增加redis中的key,value标记.

  2. 数据库增加了元素.

  3. 完成元素对bloom的添加.

过滤器相当于是对数据库存量的一个小的映射。

Redis作为缓存

image-20210320103550760

设置key的存活时长

1
set k1 ex 50 #设置k1存活时长为50秒.

ttl key
查看key剩余存活时长.

结果描述
-2表示key已经过期,或者不存在。
-1表示key,一直存在。没有过期时间。
正数表示key的剩余过期时间,单位秒。

expire

设置key的过期时间。单位秒。几秒后过期。
image-20210327094216499

🎭注意:发生写会剔除过期时间。只针存在的key或者未过期的key有效。对已经过期,或者不存在的key设置无效。

🤗思考为什么会剔除过期时间?

设置新值的时候,新值存放在一个新的物理地址中,key指向新的value的物理地址,过期时间针对的是老的value。

expireat

设置key在将来的某个时间节点过期。
image-20210327094745511

time

取时间戳.

回收策略

当maxmemory限制达到的时候Redis会使用的行为由 Redis的maxmemory-policy配置指令来进行配置。

以下的策略是可用的:

  • noeviction:返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但DEL和几个例外)
  • allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
  • volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。
  • allkeys-random: 回收随机的键使得新添加的数据有空间存放。
  • volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
  • volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。

过期淘汰原理:

Redis keys过期有两种方式:被动和主动方式。

当一些客户端尝试访问它时,key会被发现并主动的过期。

当然,这样是不够的,因为有些过期的keys,永远不会访问他们。 无论如何,这些keys应该过期,所以定时随机测试设置keys的过期时间。所有这些过期的keys将会从密钥空间删除。

具体就是Redis每秒10次做的事情:

  1. 测试随机的20个keys进行相关过期检测。
  2. 删除所有已经过期的keys。
  3. 如果有多于25%的keys过期,重复步奏1.

这是一个平凡的概率算法,基本上的假设是,我们的样本是这个密钥控件,并且我们不断重复过期检测,直到过期的keys的百分百低于25%,这意味着,在任何给定的时刻,最多会清除1/4的过期keys。

持久化

一个进程既要满足修改,又要满足持久化,成本很高。

Redis的存储层:

  1. 快照/副本
  2. 日志

RDB

RDB持久化可实现的几种方式:

阻塞,数据拷贝:

image-20210408111750225

save命令实现

弊端:不能对外提供服务.明确关机维护的时候可以使用此命令。

非阻塞,数据拷贝:

image-20210408111917373

弊端:时点混乱.不使用此方式。

非阻塞,fork

image-20210408112309792

1.bgsave命令实现。

2.配置文件中给出bgsave的规则。

fork原理图:

image-20210408173910856

image-20210408112513691

优缺点:

😜优点:
  1. 时点性

  2. 类似java中的序列化 恢复的速度相对快

🤔缺点:
  1. 不支持拉链 只有一个dump.rdb

  2. 丢失数据相对多一些 时点与时点之间窗口数据容易丢失。

    8得到一个rdb,9纲要洛一个rdb,挂机了

fork介绍

进程的概念

image-20210408220231613

父子进程之间的关系:

  1. 进程之间是数据隔离的。

  2. 父进程其实可以让子进程看到数据,当刚创建还未修改时,本质是子进程中的指针与父进程中的一致。

  3. 子进程修改变量,不会影响父进程。父进程中修改变量也不会影响子进程。

创建子进程需要考虑的维度:

  1. 速度。

  2. 内存空间够不够。

fork()方法特点:

  1. 速度快。
  2. 占用内存小。

fork过程图解:

copy on write机制。
image-20210408221102660

💖重点一句话:玩的都是指针。

RDB配置:

1
2
3
4
5
save 900 1			#900秒内超过1条数据。
save 300 10 #300秒内超过10秒数据。
save 60 10000 #60秒内超过10000条数据。
dbfilename dump.rdb #存储的文件名。
dir /var/lib/redis/6379 #存储的文件目录。

AOF

image-20210409090933347

AOF概念:

redis的写操作记录到文件中.

优点:

丢失的数据量少。

AOF发展史

4.0以前

弊端:体量无限变大,恢复慢。

RDB和AOF可以同时开启,如果开启了AOF 只会用AOF恢复。

日志方案:hdfs,fsimage+edits.log 让日志只记录增量 合并的过程。

重写策略–删除抵消的命令 合并重复的命令。最终也是一个 纯指令的日志文件。

4.0以后

将老的数据RDB到aof文件中 将增量的以指令的方式 Append到AOF。

AOF是一个混合体 利用了RDB的快 利用了日志的全量。

重写命令执行之后,才会变成混合体。

增量日志+全量时点数据。恢复速度快,数据更全面。

AOF配置:

AOF记录时,写操作会触发IO。根据数据重要性,选择合适的写操作。

1
2
3
4
5
6
7
appendonly yes						#开启aof
appendfilename "appendonly.aof" #文件名
auto-aof-rewrite-percentage 100 #判断文件达到多大百分比时重写。
auto-aof-rewrite-min-size 64mb #自动重写,初始的文件容量。
appendfsync always #刷新buffer内存。
appendfsync everysec #每秒刷新buffer内存
appendfsync no #只有当buffer满时刷新内存。

往磁盘写操作内部理解:

只要是写操作,都会调用系统内核,内核中间有个buffer,缓冲区。就是用来减少交互次数。提升效率。
image-20210409092646328

实操

🙄配置文件相关:

设置redis,后台跑,还是前台阻塞跑。

1
daemonize  yes/no

设置输出日志文件路径:

1
logfile /var/log/redis_6379.log

如果关闭了后台运行,日志也可以关闭,使其在控制台前台打印。

开启Aof追加模式

1
appendonly yes

aof-rdb混合开关

1
aof-use-rdb-preamble no/yes

😏命令相关:

1
2
3
save  #阻塞方式rdb保存数据
bgsave #后台非阻塞方式保存rdb数据 fork(),保存为rdb文件。
bgrewirteaof #后台重写aof文件,命令整合。4,0之后为rdb+aof混合体。保存为aof文件。

AFK原理

单机存在的问题

image-20210409164905534

  1. 单点故障。
  2. 容量有限。
  3. 压力。

AFK策略图解image-20210409165209783

AKF

X轴:每一台都是全量,有一台为主,其他为镜像。

Y轴:通过业务和功能区分。分段。

Z轴:每种业务,优先级,逻辑再拆分。将数据落到不同的片上。分片。

AFK一变多,数据一致性解决方案,针对x轴。

通过AKF一变多

需要解决的问题:数据一致性!!!!

反问自己:为什么一变多,解决可用性。

同步阻塞法
image-20210409170034047

强一致性。但是,如果阻塞时间过长,破坏可用性。

异步法
image-20210409170312690

弱一致性,如果异步处理出现失败异常,会丢失数据。

kafka法
image-20210409170554416

redis与kafka之间为同步阻塞。redis和kafka之间为异步。由于kafka的可靠性,一定会同步到其他redis中。

期间可能会取到不一致的数据。但是最终数据会一直。强调:强一致性。

主备、主从、主主概念区分:

参考博客链接:https://www.cnblogs.com/tankblog/p/11190598.html

主备 主从 主主模式
单点故障的情况不可避免,而且单副本的存储方案早已无法满足业务的可靠性要求,单机可靠性就就两个9,也就是一年大概有3.65天不可用。因此一般情况下我们至少也会上个双机存储架构。凡事最好有个plan B。

主备

主:主机,备:备机。
主机的意思当然是以它为主了,读写都是主机上,而备机呢就是备用,默默的在背后吸收主机的数据,时刻待命着等待主机挂了之后取而代之(没这么坏哈哈)。因此在主机还活着的情况下,备机的唯一使命就是同步主机的数据,不对外提供服务。
img

image
❀优点:简单,主备之间只有数据同步,不需要考虑别的情况。就很简单的配置一下,再搞一台服务器就能组成主备架构了。

❀缺点:备机等于就拿来备份,浪费了备机这台服务器的资源。上面说的不考虑别的情况指的是主机和备机它们两之间就只要复制数据.

但是有些情况我们人还是得考虑的:主机挂了如何让备机上。有三种选择:

  1. 人工切换。人工切换时效性不高,出了事情首先你得开机,登录远程一阵啪啪得好几分钟或者万一你在LOL,黑铁晋级青铜最后一把努力了几个月即将晋升倔强青铜的一刻!是吧。还要万一在深夜或者说…是吧。

  2. 引入中间件。例如ZooKeeper、keepalived。就跟好多房东把房子委托给中介一样,这中间件就是个中介。全权由中介来打理主机和备机,它会根据机子状态来判别这时候是不是该备机上了。(建议)

  3. 主机备机之间状态传输(咱不找中介了,自己来打理),啥意思呢?就是除数据同步,主备之间还要有个状态传输过程,来让备机只要现在主机过得好不好,可以是主机主动推送它的状态给备机,或者是备机去索要状态。当状态拿不到或者不对的时候就开始主备切换。但是可能传输出现了波动啥的,导致备机误判了,然后备机升级为主机,这样就两主机了(下面会说主主的问题)。

主从

主:主机,从:从机

从机和备机的区别在于它得除了同步数据之外还得干活,对外提供读的操作,你可以理解为它是仆从。但是仆从和备机一样也有翻身做主人的一天,所以它也在默默的等待着主机挂了,取而代之。image

❀优点:
充分利用了资源,嘿嘿不想备机这么爽了,还得出来干活,对外提供读操作。而且在主机挂了的时候,如果没任命新机主之前,读操作还是能用的。

❀缺点:

  1. 客户端需要多个判断,也就是不同操作需要发放给不同服务器,我上图主机提供读写,有时候读写分离了,主机就提供写。

  2. 主从延迟,读操作分配给从库,就会存在数据同步的延迟问题,比如某个人注册了账号之后,登录走的是从机,这时候数据还未从主机同步过来,那可不让人很难受了。有关主从延迟问题的一些解决办法

  3. 和主备一样的切换问题。(参考主备)

主主

主主就是两台都是主机。同时对外提供读写操作。客户端任意访问提供的一台。
image
img

❀优点:

​ 主主的好处就是可以把写操作也分担一下,但是问题恰恰就出在写操作上,导致主主的架构有很大的局限性。

❀缺点:

​ 例如主机A有个注册的插入操作,生成的id是50,同一时刻主机B也有个插入操作生成的id也是50。然后它们之间的数据同步了,你说是谁覆盖谁呢?谁覆盖谁都不对!

​ 因此主主只适用于可以双向复制,覆盖的数据(例如用户登录生成的token)。但是我们平日里绝大部分的数据都不允许。

结语
这种双机存储架构一般而言应用于一些业务量不大的场景。主要还是为了存储的可用性。

CAP

CAP概念

参考链接:http://www.ruanyifeng.com/blog/2018/07/cap.html

分布式系统(distributed system)正变得越来越重要,大型网站几乎都是分布式的。

分布式系统的最大难点,就是各个节点的状态如何同步。CAP 定理是这方面的基本定理,也是理解分布式系统的起点。

本文介绍该定理。它其实很好懂,而且是显而易见的。下面的内容主要参考了 Michael Whittaker 的文章

一、分布式系统的三个指标

img

1998年,加州大学的计算机科学家 Eric Brewer 提出,分布式系统有三个指标。

  • Consistency
  • Availability
  • Partition tolerance

它们的第一个字母分别是 C、A、P。

Eric Brewer 说,这三个指标不可能同时做到。这个结论就叫做 CAP 定理。

二、Partition tolerance

先看 Partition tolerance,中文叫做"分区容错"。

大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。分区容错的意思是,区间通信可能失败。比如,一台服务器放在中国,另一台服务器放在美国,这就是两个区,它们之间可能无法通信。

img

上图中,G1 和 G2 是两台跨区的服务器。G1 向 G2 发送一条消息,G2 可能无法收到。系统设计的时候,必须考虑到这种情况。

一般来说,分区容错无法避免,因此可以认为 CAP 的 P 总是成立。CAP 定理告诉我们,剩下的 C 和 A 无法同时做到。

三、Consistency

Consistency 中文叫做"一致性"。意思是,写操作之后的读操作,必须返回该值。举例来说,某条记录是 v0,用户向 G1 发起一个写操作,将其改为 v1。

img

接下来,用户的读操作就会得到 v1。这就叫一致性。

img

问题是,用户有可能向 G2 发起读操作,由于 G2 的值没有发生变化,因此返回的是 v0。G1 和 G2 读操作的结果不一致,这就不满足一致性了。

img

为了让 G2 也能变为 v1,就要在 G1 写操作的时候,让 G1 向 G2 发送一条消息,要求 G2 也改成 v1。

img

这样的话,用户向 G2 发起读操作,也能得到 v1。

img

四、Availability

Availability 中文叫做"可用性",意思是只要收到用户的请求,服务器就必须给出回应。

用户可以选择向 G1 或 G2 发起读操作。不管是哪台服务器,只要收到请求,就必须告诉用户,到底是 v0 还是 v1,否则就不满足可用性。

五、Consistency 和 Availability 的矛盾

一致性和可用性,为什么不可能同时成立?答案很简单,因为可能通信失败(即出现分区容错)。

如果保证 G2 的一致性,那么 G1 必须在写操作时,锁定 G2 的读操作和写操作。只有数据同步后,才能重新开放读写。锁定期间,G2 不能读写,没有可用性不。

如果保证 G2 的可用性,那么势必不能锁定 G2,所以一致性不成立。

综上所述,G2 无法同时做到一致性和可用性。系统设计时只能选择一个目标。如果追求一致性,那么无法保证所有节点的可用性;如果追求所有节点的可用性,那就没法做到一致性。

在什么场合,可用性高于一致性?

举例来说,发布一张网页到 CDN,多个服务器有这张网页的副本。后来发现一个错误,需要更新网页,这时只能每个服务器都更新一遍。

一般来说,网页的更新不是特别强调一致性。短时期内,一些用户拿到老版本,另一些用户拿到新版本,问题不会特别大。当然,所有人最终都会看到新版本。所以,这个场合就是可用性高于一致性。

实时证明,大多数都是牺牲了一致性。像12306还有淘宝网,就好比是你买火车票,本来你看到的是还有一张票,其实在这个时刻已经被买走了,你填好了信息准备买的时候发现系统提示你没票了。这就是牺牲了一致性。

但是不是说牺牲一致性一定是最好的。就好比mysql中的事务机制,张三给李四转了100块钱,这时候必须保证张三的账户上少了100,李四的账户多了100。因此需要数据的一致性,而且什么时候转钱都可以,也需要可用性。但是可以转钱失败是可以允许的,这时候舍弃可用性。

Redis中主从CAP监控。

Redis中的集群关系,采用主从

image-20210418143351732

监控法,实行主备切换,由多个监控程序集群监控主。

image-20210418143510871

监控集群,势力范围n/2+1,一般使用奇数台。

奇数和偶数允许风险的个数一样,但是发生风险的个数偶数大于奇数,所以选择奇数台。

image-20210418143933529

Redis主从复制

使用经验:

  1. 从机如果是RDB模式,无论从机挂后重启或者从机运行时,每次同步数据,可从RDB文件中记录的ID给出偏移量,给到主,最后同步少量的增量。如果时AOF模式,每次同步数据都是全量同步。

image-20210418204223363

  1. 同步数据时从节点会先删除老数据,再同步新数据。

开启主从复制:

使用命令的方式:

❀redis运行时

SLAVEOF

REPLICAOF host port redis5版本后可以使用。

image-20210418203435087

❀redis启动时:

不仅设置了主,还设置了AOF持久化的方式。

redis-server ./6380.conf --replicaof 127.0.0.1 6379 --appendonly yes

个人理解--参数相当于覆盖原有的配置文件中的参数。

通过配置文件方式:

设置链接主redis的ip和端口:

1
replicaof <masterip> <masterport>

设置链接主redis的参数:

1
masterauth <master-password>

设置下载主最新rdb时,老数据是否可读。

1
replica-serve-stale-data yes

设置从只读

1
replica-read-only yes

设置同步方式,间接通过磁盘还是,直接网络IO。如果磁盘速度很慢,网络带宽很快就直接通过网络IO。

1
repl-diskless-sync no

image-20210418205356737

设置在同步增量数据时的存放队列的最大容量。如果超出最大容量,则直接使用全量RDB同步。

1
repl-backlog-size 1mb

image-20210418205707233

#增量复制,至少写成功三条。主从异步同步数据的弱一致性,往强一致性上靠。

1
2
min-replicas-to-write 3
min-replicas-max-lag 10

手工切换主备

1
2
`REPLICAOF NO ONE`  #设置替换的从机不再追随。
`REPLICAOF host port` #在其他从机设置,重新追随指定的替换从机。

监控哨兵Sentinel

Redis 的 Sentinel 系统用于管理多个 Redis 服务器(instance), 该系统执行以下三个任务:

  • 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。

  • 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。

  • 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

    Redis Sentinel 是一个分布式系统, 你可以在一个架构中运行多个 Sentinel 进程(progress), 这些进程使用流言协议(gossip protocols)来接收关于主服务器是否下线的信息, 并使用投票协议(agreement protocols)来决定是否执行自动故障迁移, 以及选择哪个从服务器作为新的主服务器。

    虽然 Redis Sentinel 释出为一个单独的可执行文件 redis-sentinel , 但实际上它只是一个运行在特殊模式下的 Redis 服务器, 你可以在启动一个普通 Redis 服务器时通过给定 –sentinel 选项来启动 Redis Sentinel 。

启动哨兵

哨兵可以独立成一个进程,一个程序。也可以使redis服务进程包含哨兵这段代码。

独立进场方式:

1
redis-sentienl  <配置文件>

redis服务方式:

1
redis-server <配置文件>  --sentinel

配置文件参考目录:下载的源码目录下。/usr/local/redis/redis-5.0.12

1
2
port 26379	#配置哨兵端口号
sentinel monitor mymaster 127.0.0.1 6379 2 #配置监听主机的ip和端口,投票的权重数。

Spring整合Redis

官网学习:https://spring.io/projects/spring-data-redis

Redis的Sharing分片逻辑

akf主要解决单机问题:

  1. 单点故障.
  2. 容量问题.
  3. 访问压力.

x,y,z轴思想.

主从复制,HA :从X轴,生成数据镜像,解决单点故障.

单节点:容量的问题,采取Sharing分片.

Y轴拆分方式

y轴按逻辑: 业务拆分
image-20210419161255353

Z轴Sharding分片逻辑:

  1. 按算法:hash+取模modula
    image-20210419161445349

缺点:取模的数必须固定 %3 %4 %10,影响分布式下的扩展性,每次添加新节点必须全局洗牌.

  1. 逻辑:random 随机

image-20210419162253337

random算法,用于存放list数据.例如实现消息队列场景.

  1. 逻辑:kemata 一致性哈希,没有取模data,node

    image-20210419162745964

    将node参数一致性hash运算,挂在一个环形的hash环上(实际是挂在树结构的节点上).每次有新的key进来,参数一致性hash运算,按照大小找到最近的node,存入机器.

    将node物理节点的实际ip计算,换成ip+数字,造出多个代表实际主机的虚拟节点,挂在哈希环上,解决数据倾斜问题.

    ❀优点:

    你加节点,的确可以分担其他节点的压力,不会造成全局洗牌

    ❀缺点:

    新增节点造成一小部分数据不能命中

    1,问题,击穿,压到mysql

    2,方案:没去取离我最近的2个物理节点

    更倾向于 作为缓存,而不是数据库用!!!!

Redis代理原理

代理可以解耦后面的Redis复杂度.

客户端直连Redis场景:

redis 的连接成本很高 对server端造成的,客户端直连,在很多客户端的情况下,会造成连接数过多,增加了链接成本.三次握手消耗资源.

image-20210419174749585

使用Proxy代理链接Redis场景:

达到负载均衡,减少链接的压力.
image-20210419182857854

单台代理主机,可能访问量大的时候,proxy主机压力过大.

使用虚拟服务LVS,和keepalived监控,负载均衡代理Proxy

image-20210419183519670

LVS,虚拟出一个VIP地址.

keepalived,监控LVS,使LVS高可用.监控proxy代理.

代理层的实现

Redis分片逻辑在代理层实现:
image-20210419184200061

代理技术:

  1. twemproxy

    github地址:https://github.com/twitter/twemproxy

  2. predixy

  3. cluster(Redis自身)

  4. codis

Redis中的cluster分片技术

预分区技术

由于三种分片算法,modula,random,ketama,一旦集群增加主机后,都会出现找不到原有数据情况.所以只能用作缓存.不能用作数据库.

实际分区两个,取模运算时,使用预分区个数10计算,算出的结果,在映射mapping下,转到实际主机.

image-20210419193006375w

当集群中增加redis时,将mapping映射关系和对应数据实际分区同步转移到,新增的主机中.

redis自身集群中.每台都会带有一个hash算法,和映射mapping,客户端随机访问主机,都会跳转到正确的redis机器上.
image-20210419193547162

数据分治聚合操作很难实现事务

Redis集群搭建

twemproxy

github地址:https://github.com/twitter/twemproxy

简单安装搭建步骤:

  1. 下载编译

    1
    2
    3
    4
    5
    6
    git clone git@github.com:twitter/twemproxy.git
    cd twemproxy
    autoreconf -fvi
    ./configure --enable-debug=full
    make
    src/nutcracker -h
  2. 安装为服务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    cp nutcracker.init /etc/init.d/twemproxy    #将源码路径script文件夹下的nutcracker.init,放入服务脚本文件夹中.nutcracker.init内容可研究参考.

    cd /etc

    mkdir nutcracker #创建脚本指定的启动配置文件夹.

    cp /usr/local/twemproxy/conf/* /etc/nutcracker/ #复制源码目录中的启动配置文件,进脚本指定的启动配置文件夹中.

    cp nutcracker /usr/bin/ #拷贝源码src目录下的nutcracker脚本到环境变量的目录中.

    service twemproxy start #启用代理服务.

缺点:

  1. 不支持keys *.

  2. 不支持watch ,multi事务.

predixy代理

github地址:https://github.com/joyieldInc/predixy

predixy评测:https://blog.csdn.net/rebaic/article/details/76384028

特点:

  1. 可以监控一套主从复制,也可以监控多套主从复制.
  2. 可以支持事务,但是只支持单group.

简易搭建步骤:

下载

地址:https://github.com/joyieldInc/predixy/releases

​ 解压后,修改conf目录下的predixy.conf配置文件:

​ 解开端口号:
image-20210420154626000

哨兵sentinel代理配置:

通过哨兵发现并代理Redis.

1.引入哨兵配置:

只能引入一种:
image-20210420185046763

2.配置主从哨兵:

sentinel.conf
Sentinels中配置哨兵的集群.

❀两套主从:
image-20210420171730557

Group为哨兵监控的主从组.名称为,哨兵配置文件中的主从关系名称.
image-20210420172020529

缺点是不支持事务.
image-20210420190507982

❀:一套主从
只需要设置一个group就可以了.
image-20210420190427258

优点,支持事务
image-20210420190456542

3.启动predixy:

进入bin目录:

1
./predixy ../conf/predixy.conf  #执行脚本,指定对应的配置文件.

Cluster集群

教程:http://redis.cn/topics/cluster-tutorial.html
image-20210419200426813

Redis 集群的数据分片

Redis 集群没有使用一致性hash, 而是引入了 哈希槽的概念.

Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么:

  • 节点 A 包含 0 到 5500号哈希槽.
  • 节点 B 包含5501 到 11000 号哈希槽.
  • 节点 C 包含11001 到 16384号哈希槽.

这种结构很容易添加或者删除节点. 比如如果我想新添加个节点D, 我需要从节点 A, B, C中得部分槽到D上. 如果我想移除节点A,需要将A中的槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可. 由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态.

单机创建集群演示

create-cluster:

脚本目录:/usr/local/redis/redis-5.0.12/utils/create-cluster

  1. 修改create-cluster脚本中的配置

    1
    2
    3
    4
    5
    # Settings
    PORT=30000
    TIMEOUT=2000
    NODES=6 #总结点数。
    REPLICAS=1 #每个分片的从节点数。 分片数=(6/1+1)
  2. 启动所有节点实例

    1
    ./create-cluster start

    image-20210420193747786

  3. 创建分片和主从关系。

    1
    ./create-cluster create

    image-20210420193904213

  4. 链接集群:

    1
    redis-cli -c -p 30001
  5. 停止集群:

    1
    ./create-cluster stop
  6. 清除日志,文件数据等缓存

    1
    ./create-cluster clean

    image-20210420195653783

分布式创建集群演示

使用客户端命令redis-cli创建.

查询帮助:

1
redis-cli --cluster help
  1. 启动所有redis实例

    集群的redis配置文件添加:

    1
    2
    3
    4
    5
    port 7000
    cluster-enabled yes
    cluster-config-file nodes.conf
    cluster-node-timeout 5000
    appendonly yes
  2. 客户端命令,创建集群分片和主从关系:

    1
    redis-cli --cluster create  127.0.0.1:30001 127.0.0.1:30002 127.0.0.1:30003 127.0.0.1:30004 127.0.0.1:30005 127.0.0.1:30006 --cluster-replicas 1
  3. 迁移数据
    解决新增、减少节点的问题,解决数据倾斜的问题。

    1
    redis-cli --cluster reshard 127.0.0.1:30001         #迁移节点上的数据。127.0.0.1:30001表示连接的集群地址,不代表要迁移的节点。

image-20210420203647629

  1. 查看集群节点信息

    1
    redis-cli --cluster info 127.0.0.1:30001

image-20210420204029891

1
redis-cli --cluster check 127.0.0.1:30001

image-20210420203910525

事务的使用技巧

分布式同一个事务中的不同key操作,如果计算出key的槽在不同节点,那么事务就无法提交。image-20210420195341221

解决方法:将事务关联的key,都设置{oo}类似一样的前缀,那么计算出的槽位,会在相同的节点下,这时候事务就可以提交执行。
image-20210420195104679

击穿

image-20210421080118885

击穿发生场景:某个的key已经过期,在高并发情况下访问。造成直接到达访问数据库。通俗理解为.某个key有几千的请求。

❀击穿解决方案:

当客户端访问key为null的时候,使用setnx锁。只有获取到锁的客户端才能访问数据库。其他客户端没有得到锁的情况下不能访问数据库,只能停留等待,设置睡眠时间重复获取。

如果设置了锁,可能会导致死锁。

​ 死锁解决方案:为锁设定过期时间。

如果设置了过期时间,可能会导致锁超时。

​ 锁超时解决方案:锁超时意味着,访问数据库时间超过了锁的过期时间。这时候可以加一个监控线程,监控数据是否被取回来了,如果没有,更新锁的过期时间。

个人理解:做锁在一定程度上,降低了执行速度,但是提高了数据准确性。同时也可以间接降低服务访问压力。

穿透

image-20210421083645029

穿透出现的场景:客户端访问系统中根本不存在的数据。

❀解决方案:

使用布隆过滤器:

  1. client包含布隆算法和数据。
  2. client包含了算法,数据在redis的bitmap中。redis无状态。
  3. redis集成了布隆算法和数据(布隆过滤器)。

布隆过滤器问题:不能删除数据。解决方法:1.使用布谷鸟。2.设置空key。

雪崩

大量的key同时失效,通俗理解为几百个key同时失效,每个可以有几十个几百个请求。

image-20210421094541365

❀解决方案:

1.对于与时点性无关的数据,设置key的随机过期时间。

2.对与时点性有关的数据,比如零点必须统一过期的数据,采取强依赖的击穿方案。并且可以在业务层加入判断,零点延时,减缓访问压力。

分布式锁

setnx

已经做锁了,对于效率要求就不大了。对于准确度和一致性要求高。

image-20210421100222819

redisson

锁的一种技术。

zookeeper

zookeeper没有redis快。数据可靠性、准确。zookeeper做分布式锁比较好。

API

官方文档:

https://docs.spring.io/spring-data/redis/docs/2.5.0/reference/html/#reference

高阶API

RedisTemplate

存放key的时候,会对key进行java字符串序列化.
image-20210421193109862

StringRedisTemplate

存放key字符串就不会进行java字符串序列化操作了。
image-20210421193130329

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.msb.spring.redis.demo;

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

/**
* @Auther: tang
* @Date: 2021/4/21 - 04 - 21 - 17:10
* @Description: com.msb.spring.redis.demo
* @version: 1.0
*/
@Component
public class TestRedis {

@Autowired
RedisTemplate redisTemplate;

@Autowired
StringRedisTemplate stringRedisTemplate;

public void testRedis() {

stringRedisTemplate.opsForValue().set("k2", "321");

System.out.println(stringRedisTemplate.opsForValue().get("k2"));

}
}

springboot配置文件:

1
2
spring.redis.host=192.168.57.129
spring.redis.port=6379

springboot启动测试类:

1
2
3
4
5
6
7
8
9
@SpringBootApplication
public class DemoredisApplication {

public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(DemoredisApplication.class, args);
RedisTest bean = ctx.getBean(RedisTest.class);
bean.hashTest2();
}
}

低阶API

RedisConnection

示例代码:

1
2
3
4
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.set("hello".getBytes(), "msbredis".getBytes());
byte[] bytes = connection.get("hello".getBytes());
System.out.println(new String(bytes));

Hash实现

设置单个属性:

1
2
3
4
5
6
public void hashTest() {
HashOperations<String, Object, Object> hash = stringRedisTemplate.opsForHash();
hash.put("tang", "name", "汤晔飞");
hash.put("tang", "age", "18");
hash.entries("tang");
}

存java对象。

引入pom工具:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>

实体类对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Person {

private String name;

private Integer age;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Autowired
ObjectMapper objectMapper; //对象属性转换工具。

public void hashTest2() {
Person person = new Person();
person.setName("小万");
person.setAge(20);
Jackson2HashMapper jm = new Jackson2HashMapper(objectMapper, false);//将对象转为map的工具,应该是先将对象转为json,再将json转为map。
stringRedisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));//由于stringRedisTemplate,支持的是string类型的序列化器。所以需要自定义hash的value序列化器使其支持int。
stringRedisTemplate.opsForHash().putAll("wan", jm.toHash(person));
Map<Object, Object> wan = stringRedisTemplate.opsForHash().entries("wan");
Person lordwan = objectMapper.convertValue(wan, Person.class);
System.out.println(lordwan);
}

自定义Template

自定义的Bean生成。

1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class Mytemplate {
//干预未来容器里自己想要的模板。
@Bean
public StringRedisTemplate ooxx(RedisConnectionFactory fc){
//自己new时候需要传入,RedisConnectionFactory。
StringRedisTemplate st = new StringRedisTemplate(fc);
st.setHashValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
return st;
}
}

注入干预:

1
2
3
@Autowired
@Qualifier("ooxx")
StringRedisTemplate stringRedisTemplate;

发布订阅实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void pubandsubTest() {/*往一个通道发送消息。*/
stringRedisTemplate.convertAndSend("ooxx", "hello");
RedisConnection conn = stringRedisTemplate.getConnectionFactory().getConnection();
conn.subscribe(new MessageListener() {
@Override
public void onMessage(Message message, byte[] bytes) {
System.out.println(new String(message.getBody()));
}
}, "ooxx".getBytes());
while (true) {
stringRedisTemplate.convertAndSend("ooxx", "汤小万");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

评论