Redis原理及应用

Redis 是一个开源的(BSD)基于内存的高性能数据存储器,可作为数据库、缓存、消息代理。支持的数据类型有字符串(strings), 散列表(hashes), 数组(lists), 集合(sets), 有序集合(sorted sets with range queries), 位图(bitmaps), 大数统计(hyperloglogs), geospatial indexes with radius queries and streams.

前言

Redis是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说Redis是实现高并发服务不可或缺的一部分。通常我们使用Redis时,会接触到5种数据类型(字符串、哈希、列表、集合、有序集合),丰富的类型可以协助我们实现各种业务场景需求。知其然知其所以然,进一步了解Redis的内存模型及相关持久化,事物,锁的实现方式和原理可以进一步让我们深入理解Redis,在使用时选择最佳的实现方式,在遇到问题时也能有分析解决的思路;

数据类型

先介绍一下Redis对外提供支持的数据类型, String, List, Hash, Set, Sorted-Set;

Binary-safe strings

字符串是最常用且简单的类型,Redis中key是字符串类型的,如果value也用字符串,这样我们可以做到一个字符串到另一个字符串的映射,这个在缓存html页面,或者将一个大对象序列化为字符串,都是非常有用的;

  • 字符串最大值为512M
  • 字符串内容为整数时,可以进行原子性的加减操作 ‘incr, INCRBY, DECR and DECRBY’
  • 获取字符串长度 ‘STRLEN’
  • 截取字符串 'GETRANGE ’
  • 批量设置多个keys的字符串值 'mset ’

Lists

List即一系列有序的对象,通常有Arrays和Linked-List两种实现方式,且两者相关属性有所不同;Redis中的list是基于linked-list实现的,这样就保证了即使在有百万条数据的list中,在头部或者尾部插入一条新记录也会在很短的时间内完成,相对应的缺点就是当按照数组索引去访问元素时,就会随着lists的增大而效率变低;但是为什么这样实现的原因还是在于为数据库提供较高的插入效率是至关重要的;如果需要快速访问一个大list里面中间的元素时,可以考虑使用下文会提及的有序集合;

  • 可以从左右两端插入数据进List(lpush, rpush)
  • 从左端获取指定索引区间的对象 (lrange key start end), start 为开始索引,end为结束索引, 和python一样支持负数, 即向左方向获取
  • 弹出元素(rpop)
  • 削整List, 保留一部分,移出不需要的元素(LTRIM key start end)
  • 获取元素时当list为空,可以做一个延迟阻塞等待(brpop, blpop)

应用场景

  1. 保存一个用户最后一次发布的微博内容
  2. 应用消费者-生产者模式,实现对一类消息进行生产消费的消息队列缓冲器;

Hashes

Hashes是一个键值(key=>value)对集合,很多场景下都可能会用到它。

  • 在内存足够的情况下,大小无限制;
  • 支持单次多个键值对一起插入和获取
  • 依旧支持对hashes中的某个值进行原子加减

应用场景

  1. 缓存用户信息,假如username全局唯一,就可以用hashes存储所有用户信息,username作为hashes的key,这样在获取用户信息或刷新缓用户信息时只操作某一个用户,而不影响其他的用户,减少网络请求数据量;

Sets

无序的字符串集合(不重复),底层是通过hash表实现,对value计算hash可以实现快速判重;

  • 微博用户的好友即为一个集合对象,如果要求两个人的共同好友,或者进行好友的好友推荐,就可以由对集合进行求交,求并等操作来实现;
  • 商品筛选时,根据各种条件筛选,也可以用到集合的交并运算;

Sorted sets

有序的字符串集合(不重复),内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。

  • 百度最热搜索前20,可以用此实现;

内存模型

上面我们罗列了Redis对外提供可直接使用的几种数据类型,接下来我们了解一下Redis的内存模型,即在Redis内部是如何实现丰富的数据类型及相关操作;

内存状态

在客户端通过redis-cli连接服务器后,通过info命令查看内存使用情况: info memory

  • used_memory: Redis分配器分配的内存总量(单位是字节),包括使用的虚拟内存(即swap);Redis分配器后面会介绍。used_memory_human只是显示更友好。
  • used_memory_rss:Redis进程占据操作系统的内存(单位是字节),与top及ps命令看到的值是一致的;除了分配器分配的内存之外,used_memory_rss还包括进程运行本身需要的内存、内存碎片等,但是不包括虚拟内存。
  • mem_fragmentation_ratio:内存碎片比率,该值是used_memory_rss / used_memory的比值。
  • mem_allocator:Redis使用的内存分配器,在编译时指定;可以是 libc 、jemalloc或者tcmalloc,默认是jemalloc;截图中使用的便是默认的jemalloc。

内存划分

Redis作为内存数据库,在内存中存储的内容主要是数据(键值对);通过前面的叙述可以知道,除了数据以外,Redis的其他部分也会占用内存。

1、数据

作为数据库,数据是最主要的部分;这部分占用的内存会统计在used_memory中。
Redis使用键值对存储数据,其中的值(对象)包括5种类型,即字符串、哈希、列表、集合、有序集合。这5种类型是Redis对外提供的,实际上,在Redis内部,每种类型可能有2种或更多的内部编码实现;此外,Redis在存储对象时,并不是直接将数据扔进内存,而是会对对象进行各种包装:如redisObject、SDS等;这篇文章后面将重点介绍Redis中数据存储的细节。

2、进程本身运行需要的内存

Redis主进程本身运行肯定需要占用内存,如代码、常量池等等;这部分内存大约几兆,在大多数生产环境中与Redis数据占用的内存相比可以忽略。这部分内存不是由jemalloc分配,因此不会统计在used_memory中。
补充说明:除了主进程外,Redis创建的子进程运行也会占用内存,如Redis执行AOF、RDB重写时创建的子进程。当然,这部分内存不属于Redis进程,也不会统计在used_memory和used_memory_rss中。

3、缓冲内存

缓冲内存包括客户端缓冲区、复制积压缓冲区、AOF缓冲区等;其中,客户端缓冲存储客户端连接的输入输出缓冲;复制积压缓冲用于部分复制功能;AOF缓冲区用于在进行AOF重写时,保存最近的写入命令。在了解相应功能之前,不需要知道这些缓冲的细节;这部分内存由jemalloc分配,因此会统计在used_memory中。

4、内存碎片

内存碎片是Redis在分配、回收物理内存过程中产生的。例如,如果对数据的更改频繁,而且数据之间的大小相差很大,可能导致redis释放的空间在物理内存中并没有释放,但redis又无法有效利用,这就形成了内存碎片。内存碎片不会统计在used_memory中。
内存碎片的产生与对数据进行的操作、数据的特点等都有关;此外,与使用的内存分配器也有关系:如果内存分配器设计合理,可以尽可能的减少内存碎片的产生。后面将要说到的jemalloc便在控制内存碎片方面做的很好。
如果Redis服务器中的内存碎片已经很大,可以通过安全重启的方式减小内存碎片:因为重启之后,Redis重新从备份文件中读取数据,在内存中进行重排,为每个数据重新选择合适的内存单元,减小内存碎片。

持久化

应用场景

1. 显示最新的发布记录

在一个论坛的首页,展示最新发布的20片帖子,我们在每一篇帖子发布后,将帖子的简要信息及id存储在Redis-List里面,用lpush的命令,这样保证了从左至右就是按照帖子的发布时间倒序排列的;这样无论取最新20,50,100都可以用命令lrange来读取,或者用命令ltrim对list进行削整,保存最新的帖子,淘汰较早的帖子

lpush post "生活大爆炸大结局"
lpush post "华为备胎芯片转正"
lpush post "滴滴回收违规单车"
lpush post "合照杀手王祖贤"
lpush post "Evil Genius 战队勇夺TI5冠军"
lrange post 0 2
-------
1) "Evil Genius 战队勇夺TI5冠军"
2) "合照杀手王祖贤"
3) "滴滴回收违规单车"
-------
llen post
(integer) 5
ltrim post 0 2
llen post
(integer) 3
2. 天梯分数高手排行榜(维护一个有序队列)
zadd ranking 9987 "miracle"
zadd ranking 9762 "天命"
zadd ranking 9521 "高手坟墓"
zadd ranking 9785 "RAMZES666"
zadd ranking 9589 "中华小当家"
zadd ranking 9129 "fly"
zadd ranking 9899 "Arteezy"
// 降序输出天梯排名前五(降序)
zrevrange ranking 0 
// 升序输出
zrange ranking 0 4

注意: 这里放进set中的值key不是分数而是内容, 所以当我们放入已经存在的值时, 会触发重新排序;

其他

  1. 阿里云Redis开发规范
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值