Redis缓存数据库(二)

文章详细介绍了Redis的几个关键技术特性,包括Pipelining优化网络通信,发布/订阅系统用于实时消息传递,事务处理的简单高效模型,以及RedisBloom模块在缓存穿透问题中的应用。此外,还讨论了Redis的键过期策略和两种持久化方法(RDB和AOF)及其优缺点。
摘要由CSDN通过智能技术生成

目录

一、概述

1、管道(Pipelining)

2、发布/订阅(Redis Pub/Sub)

3、事物

4、RedisBloom

5、redis作为数据库缓存的区别

6、过期(Expires) 

7、管道/父子进程

8、Redis持久化


一、概述

1、管道(Pipelining)

管道(Pipelining):怎么一次性发送多个命令,节省往返时间。

[root@localhost ~]# yum install nc     #安装nc
Loaded plugins: fastestmirror, product-id, search-disabled-repos, subscription-
              : manager
........
Installed:
  nmap-ncat.x86_64 2:6.40-19.el7

Complete!
[root@localhost ~]# nc localhost 6379
auth 123456
+OK
keys *
*4
$2
k2
$2
k3
$2
k4
$2
k1
set k8 h1
+OK
^C
[root@localhost ~]# echo -e "auth 123456\nset k9 99 \nincr k9\n get k9" | nc localhost 6379
+OK
+OK
:100
$3
100
[root@localhost ~]#

2、发布/订阅(Redis Pub/Sub)

发布/订阅(Redis Pub/Sub):Redis是一个快速稳定的发布/订阅消息系统!

发布/订阅:实时性

127.0.0.1:6379> help @pubsub

  PSUBSCRIBE pattern [pattern ...]
  summary: Listen for messages published to channels matching the given patterns
  since: 2.0.0

  PUBLISH channel message
  summary: Post a message to a channel
  since: 2.0.0

  PUBSUB
  summary: A container for Pub/Sub commands
  since: 2.8.0

  PUBSUB CHANNELS [pattern]
  summary: List active channels
  since: 2.8.0

  PUBSUB HELP
  summary: Show helpful text about the different subcommands
  since: 6.2.0

  PUBSUB NUMPAT
  summary: Get the count of unique patterns pattern subscriptions
  since: 2.8.0

  PUBSUB NUMSUB [channel [channel ...]]
  summary: Get the count of subscribers for channels
  since: 2.8.0

  PUBSUB SHARDCHANNELS [pattern]
  summary: List active shard channels
  since: 7.0.0

  PUBSUB SHARDNUMSUB [shardchannel [shardchannel ...]]
  summary: Get the count of subscribers for shard channels
  since: 7.0.0

  PUNSUBSCRIBE [pattern [pattern ...]]
  summary: Stop listening for messages posted to channels matching the given patterns
  since: 2.0.0

  SPUBLISH shardchannel message
  summary: Post a message to a shard channel
  since: 7.0.0

  SSUBSCRIBE shardchannel [shardchannel ...]
  summary: Listen for messages published to the given shard channels
  since: 7.0.0

  SUBSCRIBE channel [channel ...]  #监听通道后,才能接收到通道的消息
  summary: Listen for messages published to the given channels
  since: 2.0.0

  SUNSUBSCRIBE [shardchannel [shardchannel ...]]
  summary: Stop listening for messages posted to the given shard channels
  since: 7.0.0

  UNSUBSCRIBE [channel [channel ...]]
  summary: Stop listening for messages posted to the given channels
  since: 2.0.0

127.0.0.1:6379> PUBLISH channel1 hello  #此时没有订阅此通道,不会接收到此消息
(integer) 0
127.0.0.1:6379> PUBLISH channel1 he    # 执行此命令前再开启一个客户端,订阅此通道
(integer) 1
127.0.0.1:6379>

#另一个客户端
127.0.0.1:6379> SUBSCRIBE channel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1
1) "message"
2) "channel1"
3) "he"                 #订阅到he

3、事物

Redis使用 MULTI 命令标记事务开始,它总是返回OK。MULTI执行之后,客户端可以发送多条命令,Redis会把这些命令保存在队列当中,而不是立刻执行这些命令。所有的命令会在调用EXEC 命令之后执行。 

如果不调用EXEC,调用 DISCARD 会清空事务队列并退出事务。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k31 aaa
QUEUED
127.0.0.1:6379(TX)> set k32 bbb
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
127.0.0.1:6379>

为什么Redis不支持回滚?
如果你了解关系数据库,那么Redis事务处理机制看起来有点奇怪。

Redis事务中的命令允许失败,但是Redis会继续执行其它的命令而不是回滚所有命令。

这么做的原因有两点:

Redis 命令只在两种情况失败:
语法错误的时候才失败(在命令输入的时候不检查语法)。
要执行的key数据类型不匹配:这种错误实际上是编程错误,这应该在开发阶段被测试出来,而不是生产上。
因为不需要回滚,所以Redis内部实现简单并高效。
当出现bug的时候Redis的这种做法并不友好,可是需要注意的是回滚并不能解决程序bug。

例如,对于需要增加1的逻辑增加了2,或者操作的key类型不对,这些情况回滚并没有什么帮助。

考虑到没有人能避免程序员错误,并且这种错误也基本不能进入生产环境,我们选择了更简单且更高效的方法,不支持错误回滚。

4、RedisBloom

Redis官网 ---模块(modules)---RedisBloom

RedisBloom类似一个插件,引入到redis中来使用,需要安装

解决缓存穿透问题。

缓存穿透:某网站商品,搜索此网站没有的商品,大量请求,请求就会透过缓存到达数据库访问。

RedisBloom使用:bitmap把已有的商品存储下来,映射函数进行映射,概率解决问题,不能100%解决,概率<1%阻挡。1.你有什么2.向bitmap中标记3.请求可能被吴标记4.但是一定概率减少放行,穿透5.成本低

客户端:实现bloom算法,自己承载bitmap------------------redis
客户端:实现bloom算法------------------redis:bitmap
客户端------------------redis:bloom.so,bitmap

数据库增加新元素-->完成对bloom的添加

补充:?
bloom
counting bloom
cukcoo
布谷鸟过滤器

bloom
1.访问redis.io
2.modules
3.访问RedisBloom的github
4.linux wget *.zip
5.yum install unzip
6.unzip *.zip
7.make
8.cp bloom.so /.../redis/
9.redis-server --loadmodule /..../redis/redisbloom.so
10.redis-cli
11.bf.add k1 abc
bf.exits abc
bf.exits sdddd

12.cf.add #布谷鸟过滤器

5、redis作为数据库缓存的区别

redis作为数据库缓存的区别?

1、缓存数据不重要,不是全量数据,缓存应该随着访问变化热数据

2、redis作为缓存,redis里的数据怎么能随着业务变化,只保留热数据,因为内存大小是有限的,也就是瓶颈

        ①、业务逻辑:key的有效期

                1、会随着访问延长?不对

                2、发生写,会剔除过期时间

                3、倒计时,且redis不能延长

                4、定时

        ②、业务运转:内存是有限的,随着访问的变化,应该淘汰掉冷数据

                内存多大呢?maxmemory <bytes>

                mamemory-policy noeviction

                内存淘汰策略

                        LFU:碰了多少次

                        LRU:多久没碰它

内存中的内容:掉电易失

6、过期(Expires) 

过期(Expires) Redis允许对每个key设置存活的时间,以便于当到期时间过了自动删除这些Key。

127.0.0.1:6379> set k1 hello
OK
127.0.0.1:6379> get k1
"hello"
127.0.0.1:6379> EXPIRE k1 20  #过期时间 秒
(integer) 1
127.0.0.1:6379> ttl k1
(integer) 17
127.0.0.1:6379> ttl k1
(integer) 13
127.0.0.1:6379> set k1 20  #发生写,剔除过期时间
OK
127.0.0.1:6379> ttl k1
(integer) -1
127.0.0.1:6379> time
1) "1684078721"
2) "487550"
127.0.0.1:6379> EXPIREAT k1 1684078821   
(integer) 1
127.0.0.1:6379> ttl k1
(integer) 15
127.0.0.1:6379> ttl k1
(integer) 10
127.0.0.1:6379> ttl k1
(integer) 6
127.0.0.1:6379> ttl k1
(integer) 1
127.0.0.1:6379> ttl k1
(integer) -2
127.0.0.1:6379> ttl k1
(integer) -2
127.0.0.1:6379> get k1
(nil)
127.0.0.1:6379>

Redis 如何淘汰过期 Key
Redis 的 key 有两种过期淘汰的方式:被动方式、主动方式。

被动过期:用户访问某个 key 的时候,key 被发现过期。

当然,被动方式过期对于那些永远也不会再次被访问的 key 并没有效果。不管怎么,这些 key 都应被过期淘汰,所以 Redis 周期性主动随机检查一部分被设置生存时间的 key,那些已经过期的 key 会被从 key 空间中删除。

Redis每秒执行10次下面的操作:

        1、从带有生存时间的 key 的集合中随机选 20 进行检查。
        2、删除所有过期的key。
        3、如20里面有超过25%的key过期,立刻继续执行步骤1。
这是一个狭义概率算法,我们假设我们选出来的样本 key 代表整个 key 空间,我们继续过期检查直到过期 key 的比例降到 25% 以下。

这意味着在任意时刻已经过期但还占用内存的 key 的数量,最多等于每秒最多写操作的四分之一。

7、管道/父子进程

[root@localhost ~]# num=0
[root@localhost ~]# echo $num
0
[root@localhost ~]# ((num++))
[root@localhost ~]# echo $num
1
[root@localhost ~]# ((num++)) | echo ok
ok
[root@localhost ~]# echo $num
1
[root@localhost ~]# echo $$    #$$获取当前进程
1123
[root@localhost ~]# echo $$ | more   #原因:因为$$的优先级是高于|
1123
[root@localhost ~]# echo $BASHPID   #$BASHPID获取当前进程
1123
[root@localhost ~]# echo $BASHPID | more
1566
[root@localhost ~]#

管道:

1、衔接前一个命令的输出作为后一个命令的输入

2、管道会触发创建[子进程]

使用linux的时候:有父子进程

父进程的数据,子进程可不可以看得到?

常规思想,进程是数据隔离的!进阶思想,父进程其实可以让子进程看到数据!

linux中export的环境变量,子进程的修改不会破坏父进程

父进程的修改也不会破坏子进程

[root@localhost ~]# echo $$
1123
[root@localhost ~]# echo $num
1
[root@localhost ~]# /bin/bash   #进入子进程
[root@localhost ~]# echo $$     #子进程
1753
[root@localhost ~]# echo $num   #获取不到num

[root@localhost ~]# exit        #退出子进程
exit
[root@localhost ~]# echo $num    
1
[root@localhost ~]# echo $$
1123
[root@localhost ~]# export num     #export 
[root@localhost ~]# echo $num
1
[root@localhost ~]# /bin/bash
[root@localhost ~]# echo $num
1
[root@localhost ~]# echo $$
1793
[root@localhost ~]# vi test.sh
##======》插入:
###################=======[root@localhost ~]# vi test.sh   #test.sh内容如下
#!/bin/bash

echo $$
echo $num
num=999
echo num:$num

sleep 20

echo $num

###################=======
[root@localhost ~]# chmod +x test.sh
#######子进程修改不影响父进程
[root@localhost ~]# ./test.sh &    #&:在后台运行
[1] 1810
[root@localhost ~]# 1810
1
num:999

[root@localhost ~]# echo $num    #上面脚本text.sh开启子进程,修改num  不会影响到父进程
1
[root@localhost ~]# 999

[1]+  Done                    ./test.sh

#######父进程修改不影响子进程
[root@localhost ~]# echo $num
1
[root@localhost ~]# ./test.sh &
[1] 1825
[root@localhost ~]# 1825
1
num:999

[root@localhost ~]# echo $num
1
[root@localhost ~]# num=888
[root@localhost ~]# echo $num
888
[root@localhost ~]# 999

[1]+  Done                    ./test.sh
[root@localhost ~]#

创建子进程的速度应该是什么程序?如果父进程是redis,内存数据比如10G    1、速度2、内存空间够不够--->fork()--->  1、速度:快  2、空间:小

Under Linux, fork() is implemented using copy-on-write  pages

copy on write:内核机制,写时复制

创建子进程并不发生复制,使用指针。

[root@localhost ~]# man fork
FORK(2)                    Linux Programmer's Manual                   FORK(2)

NAME
       fork - create a child process

SYNOPSIS
       #include <unistd.h>

       pid_t fork(void);

DESCRIPTION
       fork()  creates  a new process by duplicating the calling process.  The
       new process, referred to as the child, is an  exact  duplicate  of  the
       calling  process,  referred  to as the parent, except for the following
       points:

       *  The child has its own unique process ID, and this PID does not match
          the ID of any existing process group (setpgid(2)).

       *  The  child's  parent  process ID is the same as the parent's process
          ID.

       *  The child does not inherit  its  parent's  memory  locks  (mlock(2),
          mlockall(2)).

       *  Process  resource  utilizations (getrusage(2)) and CPU time counters
          (times(2)) are reset to zero in the child.

       *  The child's set of pending  signals  is  initially  empty  (sigpend‐
          ing(2)).

       *  The  child  does  not  inherit semaphore adjustments from its parent
          (semop(2)).

       *  The child does not inherit record locks from its parent (fcntl(2)).

       *  The child does not inherit timers  from  its  parent  (setitimer(2),
          alarm(2), timer_create(2)).

       *  The  child  does not inherit outstanding asynchronous I/O operations
          from its parent (aio_read(3), aio_write(3)), nor does it inherit any
          asynchronous I/O contexts from its parent (see io_setup(2)).

       The  process  attributes  in  the  preceding  list are all specified in
       POSIX.1-2001.  The parent and child also differ  with  respect  to  the
       following Linux-specific process attributes:

       *  The  child does not inherit directory change notifications (dnotify)
          from its parent (see the description of F_NOTIFY in fcntl(2)).
....................
 EAGAIN fork() cannot allocate sufficient memory to  copy  the  parent's
              page tables and allocate a task structure for the child.

       EAGAIN It was not possible to create a new process because the caller's
              RLIMIT_NPROC resource limit was  encountered.   To  exceed  this
              limit,  the  process  must  have either the CAP_SYS_ADMIN or the
              CAP_SYS_RESOURCE capability.

       ENOMEM fork()  failed  to  allocate  the  necessary  kernel  structures
              because memory is tight.

       ENOSYS fork()  is not supported on this platform (for example, hardware
              without a Memory-Management Unit).

CONFORMING TO
       SVr4, 4.3BSD, POSIX.1-2001.

NOTES
       Under Linux, fork() is implemented using copy-on-write  pages,  so  the
       only  penalty  that it incurs is the time and memory required to dupli‐
       cate the parent's page tables, and to create a  unique  task  structure
       for the child.

       Since  version  2.3.3,  rather than invoking the kernel's fork() system
       call, the glibc fork() wrapper that is provided as  part  of  the  NPTL
       threading  implementation  invokes clone(2) with flags that provide the
       same effect as the traditional system  call.   (A  call  to  fork()  is
       equivalent  to  a  call  to clone(2) specifying flags as just SIGCHLD.)
       The glibc wrapper invokes any fork handlers that have been  established
       using pthread_atfork(3).

EXAMPLE
       See pipe(2) and wait(2).

SEE ALSO
       clone(2),   execve(2),  exit(2),  setrlimit(2),  unshare(2),  vfork(2),
       wait(2), daemon(3), capabilities(7), credentials(7)

COLOPHON
       This page is part of release 3.53 of the Linux  man-pages  project.   A
       description  of  the project, and information about reporting bugs, can
       be found at http://www.kernel.org/doc/man-pages/.

Linux                             2013-03-12                           FORK(2)

8、Redis持久化

什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?

持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。

Redis 提供了两种持久化方式:RDB(默认) 和AOF

RDB:

rdb是Redis DataBase缩写

功能核心函数rdbSave(生成RDB文件)和rdbLoad(从文件加载内存)两个函数

弊端:丢失数据相对多一些、时点与时点之间窗口数据容易丢失

优点:类似java中的序列化,恢复速度相对快。

AOF:

Aof是Append-only file缩写

每当执行服务器(定时)任务或者函数时flushAppendOnlyFile 函数都会被调用, 这个函数执行以下两个工作

aof写入保存:

WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件

SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。

存储结构:

内容是redis通讯协议(RESP )格式的命令文本存储。

优点:AOF丢失数据少

弊端:体量无限变大--->恢复慢

设计一个方案让日志,AOF足够小

        1、hdfs,fsimage+edits.log 让日志只记录增量合并的过程

        2、redis 4.0以前:重写-->删除抵消的命令,合并重复的命令-->最终也是一个纯指令的日志文

        3、redis 4.0以后:重写-->将老的数据RDB到AOF文件中将增量的以指令的方式Append到AOF--->AOF是一个混合体,利用了RDB的快,利用了日志的全量

redis是内存数据库:写操作会触发IO

redis配置文件

appendonly yes    #开启
appendfilename "appendonly.aof"

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

appendfsync always
appendfsync everysec
appendfsync no

比较

1、aof文件比rdb更新频率高,优先使用aof还原数据。

2、aof比rdb更安全也更大

3、rdb性能比aof好

4、如果两个都配了优先加载AOF

刚刚上面你有提到redis通讯协议(RESP ),能解释下什么是RESP?有什么特点?

RESP 是redis客户端和服务端之前使用的一种通讯协议;

RESP 的特点:实现简单、快速解析、可读性好

Redis缓存数据库(一)

Redis缓存数据库(三)

干我们这行,啥时候懈怠,就意味着长进的停止,长进的停止就意味着被淘汰,只能往前冲,直到凤凰涅槃的一天!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杀神lwz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值