当前位置: 代码迷 >> 综合 >> Redis基础知识十问十答
  详细解决方案

Redis基础知识十问十答

热度:6   发布时间:2024-02-05 03:47:01.0

前言:

Redis 是当前比较流行的非关系型数据库(NoSQL)。非关系型数据库是数据结构化存储的集合,它具有格式灵活、速度快、高扩展性的优点,而且Redis是基于内存的非关系型数据库,性能出色,每秒可以处理超过10万次读写操作。所以它非常适合互联网项目,为互联网相关系统提升性能。下面,我们就来学习一下Redis的基础知识:

正文:

Q&A1:什么是Redis?

1) 概念:

Redis(Remote Dictionary Server【远程数据服务】):是一个基于内存的高性能key-value数据库。

2) 原理:

Redis把整个数据库加载到内存当中,通过定期的异步操作把数据库数据flush到硬盘上进行保存。

3) 优点:

i. 性能出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB(因为加载内存中);

ii. 支持保存多种数据结构,此外单个value的最大限制是1GB;

iii. 支持事务;支持Key设置expire(到期)时间;

4) 缺点:

Redis数据库容量受到物理内存的限制,不能用作海量数据的高性能读写。

5) 使用场景:

少量高频数据的存储,高速读写访问(系统中数据字典信息)。

6) 特点:

i. 内存,快;K,V

ii. 单线程,指的是worker线程(即符合串行化处理);但 6.X以后多线程,但只是IO是多线程,worker还是单线程;

iii. 连接很多:epoll(多路复用);

iv. Value有类型(5种),且每种类型有自己本地方法(get、index);

v. 本地方法:计算向数据移动,其实是IO优化;

vi. 整体模型是一种串行化,尽量保持原子性。

Q&A2:Redis支持的数据类型?

1) Key:

给存储在redis内存中的数据起的变量名字,命名规则:除了空格、\n换行外其它的大部分字符都可以使用,但是名字尽量有意义。

2) Value:

  • String  (Binary-safe strings):

二进制安全的String,最大容量512M。

应用场景:统计网站访问数量,当前在线人数,数据库查询结果用JSON字符串化存储。

  • List  (Lists Binary-safe strings):

String类型的List,List允许用户从序列的两端推入或者弹出元素,List是由多个字符串组成的有序可重复的序列,是链表结构。

应用场景:最新消息排行榜;消息队列(完成多程序之间的消息交换),可以用push操作将任务存在list中(生产者),然后线程在用pop操作将任务取出进行执行。(消费者)

  • Sets (Sets Binary-safe strings):

String类型的Set,无序不可重复的,它的优势:进行交集并集差集操作。

应用场景:利用交集求共同好友;利用唯一性,统计访问网站的所有独立IP;好友推荐。

  • Sorted sets (Sorted sets Binary-safe strings)

String类型的SortSet,有序不可重复的。

应用场景:一个大型在线游戏的积分排行榜,每当玩家的分数发生变化时,可以执行zadd更新玩家分数(score),此后在通过zrange获取几分top ten的用户信息。

  • Hash:

Hash可以看成具有String key和String value的map容器

应用场景:存储、读取、修改用户属性(name,age,pwd等)

Q&A3:Redis的持久化机制是什么?各自的优缺点?

Redis两种持久化机制:RDB持久化(默认), AOF持久化。

1) RDB持久化(Redis DataBase):

使用快照的方式(半持久化模式)记录Redis数据库的所有键值对,在某个时间点将数据写入一个临时文件,用临时文件替换上次持久化的文件,恢复数据。 

 优点:方便持久化(只有一个文件dump.rdb)、容灾性好(一个文件可以保存到安全的磁盘)、性能最大化、数据集大时,比AOF的启动效率更高。 

 缺点:数据安全性低 (RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候)

2)  AOF持久化(append only file):

把用户执行的每个“写”指令(添加/修改/删除)都备份到文件中,还原数据的时候就是执行具体写指令而已。

优点:数据安全、数据一致性 

缺点:AOF文件比RDB文件大,且恢复速度慢、数据集大的时,比RDB效率低。

Q&A4:Redis有哪几种数据淘汰策略?

1) 原因:

Redis作为缓存时, 如果内存空间用满, 就会自动驱逐老的数据。Redis使用maxmemory指令或配置文件,设置最大使用内存。

2)  具体策略(6种):

Noeviction(不删除策略):达到最大内存限制时,如果需要更多内存,直接返回错误信息(大部分的写入指令,但DEL和几个例外)

allkeys-lru: 所有key通用; 优先删除最近最少使用(less recently used ,LRU)的 key。

allkeys-random:所有key通用; 随机删除一部分 key。

volatile-lru: 只限于设置了expire的key,优先删除最近最少使用(LRU) 的 key。

volatile-random: 只限于设置了 expire 的key,随机删除一部分 key。

volatile-ttl: 只限于设置了expire的key,优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。

Tips:

如果没有设置 expire 的key,不满足先决条件,volatile-*相关策略与 noeviction策略基本上一致。

Q&A5:Redis过期键的删除策略?

1)  过期相关命令:

redis设置过期时间四种命令:
EXPIRE <KEY> <TTL> : 将键的生存时间设为 ttl 秒   
PEXPIRE <KEY> <TTL> : 将键的生存时间设为 ttl 毫秒   
EXPIREAT <KEY> <timestamp> : 将键的过期时间设为 timestamp 所指定的秒数时间戳  
PEXPIREAT <KEY> <timestamp> : 将键的过期时间设为 timestamp 所指定的毫秒数时间戳 
移除过期时间命令:
PERSIST KEY_NAME:persist命令就是expire命令的反命令,这个函数在过期字典中查找给定的键,并从过期字典中移除   
计算并返回剩余生存时间:
TTL KEY_NAME : ttl命令(以秒返回)  
PTTL KEY_NAME : pttl命令(以毫秒返回)   

2)  具体策略:

  • 定时删除:

概念:在设置键的过期时间,同时创建一个定时器(timer).,让定时器在键的过期时间来临时,立即执行对键的删除操作。

  • 惰性删除:

概念:平时放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。

实现:由db.c/expirelfNeeded 函数实现,所有读写数据库的Redis命令在执行之前都会调用该函数对输入键进行检查:如果输入键已经过期,那么该函数将输入键从数据库中删除;反之该函数不做动作。

  • 定期删除:

概念:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。

实现:由redis.c/activeExpireCycle 函数实现,Redis执行周期性该函数,它在规定的时间内,分多次遍历服务器中的各个数据库,从数据库的expires 字典中随机检查一部分键的过期时间,并删除其中的过期键。

  • 实际使用:

Redis实际使用的是惰性删除和定期删除两种策略: 通过配合使用这两种删除策略,服务器可以很好地在合理使用CPU时间和避免浪费内存空间之间取得平衡。

Q&A5:Redis有哪些适合的场景?

  • 会话缓存(Session Cache)

最常用的场景。它比其他存储(如Memcached)的优势:Redis提供持久化。例如:缓存用户的购物车信息(维护一个不是严格要求一致性的缓存)

  • 全页缓存(FPC)

全页缓存的概念:缓存曾经浏览过的页面。

Redis提供简便的FPC平台。并且Redis提供持久化,即使重启了Redis实例,用户也不会看到页面加载速度的下降。

  • 队列

Reids在内存存储引擎提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。

  • 排行榜/计数器

Redis在内存中对数字的递增或递减的操作非常好。Redis又提供了集合(Set)和有序集合(Sorted Set)两种数据结构。

  • 发布/订阅

Redis的发布/订阅功能。

Q&A6:Redis是单进程单线程?

Redis是单进程单线程的,Redis利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销。

Q&A7:Redis为什么把数据放到内存中?

为了达到最快的读写速度,减少磁盘I/O对速度的影响。

Q&A8:Redis如何做内存优化?

尽可能使用散列表(hashes),散列表使用的内存非常小,所以应该尽可能的将你的数据模型抽象到一个散列表里面。例如,用户对象,不要把每一个属性当做key值存储,要把整个用户信息散列单一张表里。

Q&A9:Redis如何保证数据一致性?

读操作:先从Redis中读取数据,如果redis不存在,再到DB中去读数据。

插入操作:插入数据时,只对数据库进行操作,不使用Redis进行缓存。这样就避免了Redis的脏数据,读操作时,再把这条数据缓存到Redis中。

更新操作:先对DB进行更新操作,然后将Redis缓存的数据设置为无效(expire命令),读操作时,再把这条数据缓存到Redis中。

 

Q&A10:Redis击穿、穿透、雪崩及解决方案:

1) 击穿:

概念:单个key在缓存中查不到,去数据库查询。如果数据量不大或并发不大没有问题,反之,可能造成数据库压力过大而崩溃(这里是单个key发生高并发)。

解决:使用同步锁,每次只让一个key去查询,堵塞其他; 使用互斥锁。

2) 穿透:

概念:查询一个不存在的数据,每次都去数据库查询,如果流量大时,可能导致数据库崩溃(恶意频繁查询,攻击应用时出现)。

解决:使用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

3) 雪崩:

概念:多个key查询并且出现高并发,缓存中失效或者查不到,然后都去数据库查询,导致数据库崩溃。

原因:  key同时失效、redis本身崩溃

解决:在缓存数据的过期时间上再加一个随机值,减少缓存在同一时间过期;在缓存失效后,通过加锁或者队列来控制查询数据库线程的数量。