Skip to content

Redis的使用

1、redis基础?

转载与:https://www.cnblogs.com/powertoolsteam/p/redis.html,若侵权请联系删除 在Web应用发展的初期,那时关系型数据库受到了较为广泛的关注和应用,原因是因为那时候Web站点基本上访问和并发不高、交互也较少。而在后来,随着访问量的提升,使用关系型数据库的Web站点多多少少都开始在性能上出现了一些瓶颈,而瓶颈的源头一般是在磁盘的I/O上。而随着互联网技术的进一步发展,各种类型的应用层出不穷,这导致在当今云计算、大数据盛行的时代,对性能有了更多的需求,主要体现在以下四个方面:

  • 低延迟的读写速度:应用快速地反应能极大地提升用户的满意度
  • 支撑海量的数据和流量:对于搜索这样大型应用而言,需要利用PB级别的数据和能应对百万级的流量
  • 大规模集群的管理:系统管理员希望分布式应用能更简单的部署和管理
  • 庞大运营成本的考量:IT部门希望在硬件成本、软件成本和人力成本能够有大幅度地降低

为了克服这一问题,NoSQL应运而生,它同时具备了高性能、可扩展性强、高可用等优点,受到广泛开发人员和仓库管理人员的青睐。

1.1、redis是什么

Redis是现在最受欢迎的NoSQL数据库之一,Redis是一个使用ANSI C编写的开源、包含多种数据结构、支持网络、基于内存、可选持久性的键值对存储数据库,

1.2、redis特性

  • 1、基于内存运行,性能高效
  • 2、支持分布式,理论上可以无限扩展
  • 3、key-value存储系统
  • 4、开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API

相比于其他数据库类型,Redis具备的特点是:

  • C/S通讯模型
  • 单进程单线程模型
  • 丰富的数据类型
  • 操作具有原子性
  • 持久化
  • 高并发读写
  • 支持lua脚本

1.3、Redis的应用场景有哪些?

Redis 的应用场景包括:

  • 缓存系统(“热点”数据:高频读、低频写)
  • 计数器、
  • 消息队列系统、
  • 排行榜、
  • 社交网络和实时系统

image.png

1.4、Redis的数据类型及主要特性

Redis提供的数据类型主要分为5种自有类型和一种自定义类型,这5种自有类型包括:String类型、哈希类型、列表类型、集合类型和顺序集合类型。 image.png

1.4.1、String类型

它是一个二进制安全的字符串,意味着它不仅能够存储字符串、还能存储图片、视频等多种类型, 最大长度支持512M。

对每种数据类型,Redis都提供了丰富的操作命令,如:

  • GET/MGET
  • SET/SETEX/MSET/MSETNX
  • INCR/DECR
  • GETSET
  • DEL

1.4.2、哈希类型(hash)

该类型是由field和关联的value组成的map。其中,field和value都是字符串类型的。

Hash的操作命令如下:

  • GET/HMGET/HGETALL
  • SET/HMSET/HSETNX
  • EXISTS/HLEN
  • KEYS/HDEL
  • HVALS

1.4.3、列表类型(list)

该类型是一个插入顺序排序的字符串元素集合, 基于双链表实现。

List的操作命令如下:

  • LPUSH/LPUSHX/LPOP/RPUSH/RPUSHX/RPOP/LINSERT/LSET
  • LINDEX/LRANGE
  • LLEN/LTRIM

1.4.4、集合类型(set)

Set类型是一种无顺序集合, 它和List类型最大的区别是:集合中的元素没有顺序, 且元素是唯一的。

Set类型的底层是通过哈希表实现的,其操作命令为:

  • SADD/SPOP/SMOVE/SCARD
  • SINTER/SDIFF/SDIFFSTORE/SUNION

Set类型主要应用于:在某些场景,如社交场景中,通过交集、并集和差集运算,通过Set类型可以非常方便地查找共同好友、共同关注和共同偏好等社交关系。

1.4.5、顺序集合类型(sorted set)

ZSet是一种有序集合类型,每个元素都会关联一个double类型的分数权值,通过这个权值来为集合中的成员进行从小到大的排序。与Set类型一样,其底层也是通过哈希表实现的。

ZSet命令:

  • ZADD/ZPOP/ZMOVE/ZCARD/ZCOUNT
  • ZINTER/ZDIFF/ZDIFFSTORE/ZUNION

1.5、Redis的数据结构

Redis的数据结构如下图所示: image.png

关于上表中的部分释义:

  • 1、压缩列表是列表键和哈希键的底层实现之一。当一个列表键只包含少量列表项,并且每个列表项要么就是小整数,要么就是长度比较短的字符串,Redis就会使用压缩列表来做列表键的底层实现
  • 2、整数集合是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现

1.6、Redis常见问题解析

1.6.1、击穿

概念:在Redis获取某一key时, 由于key不存在, 而必须向DB发起一次请求的行为, 称为“Redis击穿”。 image.png

引发击穿的原因:

  • 第一次访问
  • 恶意访问不存在的key
  • Key过期

合理的规避方案:

  • 服务器启动时, 提前写入
  • 规范key的命名, 通过中间件拦截
  • 对某些高频访问的Key,设置合理的TTL或永不过期

1.6.2、雪崩

概念:Redis缓存层由于某种原因宕机后,所有的请求会涌向存储层,短时间内的高并发请求可能会导致存储层挂机,称之为“Redis雪崩”。

合理的规避方案:

  • 使用Redis集群
  • 限流

2、redis进阶

2.1、Redis协议简介

Redis客户端通讯协议:RESP(Redis Serialization Protocol),其特点是:

  • 简单
  • 解析速度快
  • 可读性好

Redis集群内部通讯协议:RECP(Redis Cluster Protocol ) ,其特点是:

  • 每一个node两个tcp 连接
  • 一个负责client-server通讯(P: 6379)
  • 一个负责node之间通讯(P: 10000 + 6379)

3、其他NoSQL型数据库

市面上类似于Redis,同样是NoSQL型的数据库有很多,如下图所示,除了Redis,还有MemCache、Cassadra和Mongo。下面,我们就分别对这几个数据库做一下简要的介绍: image.png

  • Memcache
    • 这是一个和Redis非常相似的数据库,但是它的数据类型没有Redis丰富。Memcache由LiveJournal的Brad Fitzpatrick开发,作为一套分布式的高速缓存系统,被许多网站使用以提升网站的访问速度,对于一些大型的、需要频繁访问数据库的网站访问速度的提升效果十分显著。
  • Apache Cassandra
    • (社区内一般简称为C*)这是一套开源分布式NoSQL数据库系统。它最初由Facebook开发,用于储存收件箱等简单格式数据,集Google BigTable的数据模型与Amazon Dynamo的完全分布式架构于一身。Facebook于2008将 Cassandra 开源,由于其良好的可扩展性和性能,被 Apple、Comcast、Instagram、Spotify、eBay、Rackspace、Netflix等知名网站所采用,成为了一种流行的分布式结构化数据存储方案。
  • MongoDB
    • 是一个基于分布式文件存储、面向文档的NoSQL数据库,由C++编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系型数据库的,它支持的数据结构非常松散,是一种类似json的BSON格式。

4、终端中使用redis

4.1、连接redis服务

redis默认端口为 6379

无密码连接运行

sh
[root@VM-0-2-centos ~]# redis-cli
或者
[root@VM-0-2-centos ~]# redis-cli -h 127.0.0.1 -p 6379

有密码连接运行

sh
redis-cli -h 127.0.0.1 -p 6379 -a "password"

5、redis常用操做

5.1、写入数据

sh
set <key> <value>
例如:set age 12  # 写入age的为12

image.png

5.2、读取数据

sh
get <key>
例如:get age   # 读取age的值---> 输出12

image.png

5.3、查询所有的key

sh
keys *  # 输出所有的key

image.png

5.4、中文乱码问题

输入和显示中文时,默认会显示乱码,这时启动时在后面加 --raw ,输入时会乱码,但是显示时就能正常显示了

sh
redis-cli -h 127.0.0.1 -p 6379 -a "password" --raw

image.pngimage.png

5.5、获取所有的键的数量

sh
dbsize # 输出键的数量

image.png

5.6、检查键是否存在

sh
exists <key> # 输出1表示存在,输出0表示不存在

image.png

5.7、删除键(单个,多个)

sh
del <key1> <key2> ...

image.pngimage.png

5.8、查看键的类型(键的数据结构类型)

sh
type <key>

image.png

5.9、重命名键

仅当 newkey 不存在时,将 key 改名为 newkey

sh
rename <key> <新名>

image.png

5.10、过期时间

设置key的过期时间,超过时间后,将会自动删除该key. 参考:https://www.cnblogs.com/sunshine-long/p/12706868.html

(1)设置一个key在当前时间"seconds"(秒)之后过期 expire

EXPIRE <key> <秒> # 返回1表示设置成功,返回0代表key不存在或者无法设置过期时间。

image.png

(2)设置一个key在当前时间"milliseconds"(毫秒)之后过期 pexpire

PEXPIRE <key> <毫秒> # 返回1表示设置成功,返回0代表key不存在或者无法设置过期时间。 1秒===1000毫秒 image.png

(3)设置一个key在"timestamp"(时间戳(秒))之后过期 expireat

EXPIREAT <key> <时间戳(秒)> #返回1代表设置成功,返回0代表key不存在或者无法设置过期时间。 image.png

(4)设置一个key在"milliseconds-timestamp"(时间戳(毫秒))之后过期 pexpireat

PEXPIREAT <key> <时间戳(毫秒)> # 返回1代表设置成功,返回0代表key不存在或者无法设置过期时间 image.png

(5)获取key的过期时间(秒) TTL <key>

TTL <key>

  • 如果key存在过期时间,返回剩余生存时间(秒);
  • 如果key是永久的,返回-1;
  • 如果key不存在或者已过期,返回-2。

(6)获取key的过期时间(毫秒)

PTTL <key>

  • 如果key存在过期时间,返回剩余生存时间(毫秒);
  • 如果key是永久的,返回-1;
  • 如果key不存在或者已过期,返回-2。

(7)移除key的过期时间,将其转换为永久状态

PERSIST <key>

  • 如果返回1,代表转换成功。
  • 如果返回0,代表key不存在或者之前就已经是永久状态。

(8)写入数据,同时设置过期时间(几秒)

SETEX <key> 秒 <value>

  • SETEX在逻辑上等价于SET和EXPIRE合并的操作,区别之处在于SETEX是一条命令,而命令的执行是原子性的,所以不会出现并发问题。

(9)redis如何清理过期key?

redis出于性能上的考虑,无法做到对每一个过期的key进行即时的过期监听和删除。但是redis提供了其它的方法来清理过期的key。

9.1)被动清理

  • 当用户主动访问一个过期的key时,redis会将其直接从内存中删除

9.2)主动清理

  • 在redis的持久化中,我们知道redis为了保持系统的稳定性,健壮性,会周期性的执行一个函数。在这个过程中,会进行之前已经提到过的自动的持久化操作,同时也会进行内存的主动清理。
  • 在内存主动清理的过程中,redis采用了一个随机算法来进行这个过程:简单来说,redis会随机的抽取N(默认100)个被设置了过期时间的key,检查这其中已经过期的key,将其清除。同时,如果这其中已经过期的key超过了一定的百分比M(默认是25),则将继续执行一次主动清理,直至过期key的百分比在概率上降低到M以下。

9.3)内存不足时触发主动清理

  • 在redis的内存不足时,也会触发主动清理

6、redis中一些热门问题

6.1、redis内存不足时的策略

redis是一个基于内存的数据库,如果存储的数据量很大,达到了内存限制的最大值,将会出现内存不足的问题。redis允许用户通过配置maxmemory-policy参数,指定redis在内存不足时的解决策略

centos默认的redis配置文件在/etc/redis.conf 可以通过whereis redis 命令查看配置文件在哪

image.png

1、volatile-lru 使用LRU算法删除一个键(只针对设置了过期时间的key 2、allkeys-lru 使用LRU算法删除一个键 3、volatile-lfu 使用LFU算法删除一个键(只针对设置了过期时间的键) 4、allkeys-lfu 使用LFU算法删除一个键 5、volatile-random 随机删除一个键(只针对设置了过期时间的键) 6、allkeys-random 随机删除一个键 7、volatile-ttl 删除最早过期的一个键 8、noeviction 不删除键,返回错误信息(redis默认选项)

对于只针对设置了过期时间的键进行删除的策略,在所有的可被删除的键(非永久的键)都被删除时内存依然不足,将会抛出错误。 其中,LRU算法--->最近最少使用算法,较为注重于时间;LFU算法--->最近最不常用算法,较为注重于被访问频率。

redis的内存置换算法和操作系统中的内存置换算法类似。

6.2、redis中数据持久化怎么实现?

为什么要持久化?

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

redis提供两种方式进行持久化,

  • RDB持久化(原理是将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化)
  • AO持久化(原理是将Reids的操作日志以追加的方式写入文件)

6.2.1 RDB持久化

RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

image.png

特点:

  • 基于快照的持久化,速度更快,一般用作备份,主从复制也是依赖于rdb持久化功能

实现ROB持久化

1)修改配置文件redis.conf(修改前先备份)

centos默认位置/etc/redis.conf

sh
daemonize yes
port 6379
logfile /data/6379/redis.log
dir /data/6379               #定义持久化文件存储位置
dbfilename  dbmp.rdb         #rdb持久化文件
bind 10.0.0.10  127.0.0.1     #redis绑定地址
requirepass 123             #redis登录密码
save 900 1                     #rdb机制 每900秒 有1个修改记录
save 300 10                    #每300秒        10个修改记录
save 60  10000                 #每60秒内        10000修改记录

2)通过save触发持久化,将数据写入RDB文件

image.png

之后,可以在持久化目录下查看.rdb持久化文件 image.png

6.2.2 AOF持久化(append only file)

AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。

image.png 特点:

  • 以追加的方式记录redis操作日志的文件。可以最大程度的保证redis数据安全,类似于mysql的binlog
  • 优点:最大程度保证数据不丢
  • 缺点:日志记录非常大

实现AOF持久化

1)修改配置文件 redis.conf(先备份)

centos默认位置/etc/redis.conf

## AOF持久化配置,两条参数
appendonly yes
appendfsync     always    # 总是修改类的操作
                everysec  # 每秒做一次持久化
                no    # 依赖于系统自带的缓存大小机制

2)写入数据,检查redis数据目录/data/6379/是否产生了aof文件,关闭redis,检查数据是否持久化

6.3、redis 主从同步(Master&Slave)?

6.3.1 是什么?

  • 主从复制:主机数据更新后根据配置和策略,自动同步到备机的master/slave机制,Master以写为主,Slave以读为主 image.png

6.3.2 能干啥?

  • 读写分离 容灾备份

6.3.3 主从复制原理?

  • 1、从服务器向主服务器发送 SYNC 命令。
  • 2、接到 SYNC 命令的主服务器会调用BGSAVE 命令,创建一个 RDB 文件,并使用缓冲区记录接下来执行的所有写命令。
  • 3、当主服务器执行完 BGSAVE 命令时,它会向从服务器发送 RDB 文件,而从服务器则会接收并载入这个文件。
  • 4、主服务器将缓冲区储存的所有写命令发送给从服务器执行。

6.3.4 怎么实现?

https://www.cnblogs.com/peng104/p/10274857.html#autoid-1-3-0

6.4、怎么在Node项目中怎么使用redis?

6.4.1 egg-redis

在egg项目中,egg提供了egg-redis插件,也有很好的文档,地址如下 github地址:https://github.com/eggjs/egg-redis

默认使用的get和set方法操作的字符串类型 如果想操作其他类型:取巧可以直接用字符串表示,JSON.stringify,JSON.parse

6.4.2 使用ioredis

适用于Node.js 的强大、注重性能且功能齐全的Redis客户端。 支持 Redis >= 2.6.12 和 (Node.js >= 6)。完全兼容Redis 6.x。

github地址:https://github.com/luin/ioredis

egg-redis 就是在 ioredis 上包了一层,所以 ioredis 的 api 和 egg-redis 是通用的 ioredis 是官方推荐的 nodejs 客户端,建议不要直接用 redis 模块

使用egg-redis时不知道非字符串类型的操作方法时,直接查看ioredis的api即可

6.4.3 redis+mysql的使用

首先redis在key value值查询的速度高于mysql,所以缓存有利于提高性能;

redis是内存型数据库,不可能存储过大的数据;

mysql是存储在硬盘上的,可以支持更大规模的数据,成本更低

一般mysql结合redis缓存,

读取数据时先从redis中查询,查询不到再从mysql中查询; 写入数据首先更新mysql,然后拿到更新结果之后,刷新缓存数据或者删除缓存数据 具体可以看下一节(6.5、如何保持mysql和redis中数据的一致性?)

6.5、如何保持mysql和redis中数据的一致性?

现在很多架构都采用了Redis+MySQL来进行存储,但是由于多方面的原因,总会导致Redis和MySQL之间出现数据的不一致性。

例如:

  • 如果一个事务执行失败回滚了,但是如果采取了先写Redis的方式,就会造成Redis和MySQL数据库的不一致,

再比如说,

  • 一个事务写入了MySQL,但是此时还未写入Redis,如果这时候有用户访问Redis,则此时就会出现数据不一致。

为了解决这些问题,以下提供集中解决方案:

1)分别处理

针对某些对数据一致性要求不是特别高的情况下,可以将这些数据放入Redis,请求来了直接查询Redis,例如近期回复、历史排名这种实时性不强的业务。而针对那些强实时性的业务,例如虚拟货币、物品购买件数等等,则直接穿透Redis至MySQL上,等到MySQL上写入成功,再同步更新到Redis上去。这样既可以起到Redis的分流大量查询请求的作用,又保证了关键数据的一致性。

2)高并发情况下

此时如果写入请求较多,则直接写入Redis中去,然后间隔一段时间,批量将所有的写入请求,刷新到MySQL中去;如果此时写入请求不多,则可以在每次写入Redis,都立刻将该命令同步至MySQL中去。这两种方法有利有弊,需要根据不同的场景来权衡。

3)基于订阅binlog的同步机制

阿里巴巴的一款开源框架canal,提供了一种发布/ 订阅模式的同步机制,通过该框架我们可以对MySQL的binlog进行订阅,这样一旦MySQL中产生了新的写入、更新、删除等操作,就可以把binlog相关的消息推送至Redis,Redis再根据binlog中的记录,对Redis进行更新。值得注意的是,binlog需要手动打开,并且不会记录关于MySQL查询的命令和操作。

其实这种机制,很类似MySQL的主从备份机制,因为MySQL的主备也是通过binlog来实现的数据一致性。而canal正是模仿了slave数据库的备份请求,使得Redis的数据更新达到了相同的效果。如下图就可以看到Slave数据库中启动了2个线程,一个是MySQL SQL线程,这个线程跟Matser数据库中起的线程是一样的,负责MySQL的业务率执行,而另外一个线程就是MySQL的I/O线程,这个线程的主要作用就是同步Master 数据库中的binlog,达到数据备份的效果。而binlog就可以理解为一堆SQL语言组成的日志。