Redis原理详解

数据类型

Redis最为常用的数据类型主要有以下五种:

String

Hash

List

Set

Sorted set

在具体描述这几种数据类型之前,我们先通过一张图了解下Redis内部内存管理中是如何描述这些不同数据类型的:

首先Redis内部使用一个redisObject对象来表示所有的key和value,redisObject最主要的信息如上图所示:type 代表一个value对象具体是何种数据类型,encoding是不同数据类型在redis内部的存储方式,比如:type=string代表value存 储的是一个普通字符串,那么对应的encoding可以是raw或者是int,如果是int则代表实际redis内部是按数值型类存储和表示这个字符串 的,当然前提是这个字符串本身可以用数值表示,比如:"123" "456"这样的字符串。

这里需要特殊说明一下vm字段,只有打开了Redis的虚拟内存功能,此字段才会真正的分配内存,该功能默认是关闭状态的,该功能会在后面具体描 述。通过上图我们可以发现Redis使用redisObject来表示所有的key/value数据是比较浪费内存的,当然这些内存管理成本的付出主要也 是为了给Redis不同数据类型提供一个统一的管理接口,实际作者也提供了多种方法帮助我们尽量节省内存使用,我们随后会具体讨论。

下面我们先来逐一的分析下这五种数据类型的使用和内部实现方式:

String

常用命令:  

set,get,decr,incr,mget 等。

应用场景:

String是最常用的一种数据类型,普通的key/value存储都可以归为此类,这里就不所做解释了。

实现方式:

String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr,decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。

Hash

常用命令:

hget,hset,hgetall 等。

应用场景:

我们简单举个实例来描述下Hash的应用场景,比如我们要存储一个用户信息对象数据,包含以下信息:

用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储,主要有以下2种存储方式:

第一种方式将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。

第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的。

那么Redis提供的Hash很好的解决了这个问题,Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口。

也就是说,Key仍然是用户ID, value是一个Map,这个Map的key是成员的属性名,value是属性值,这样对数据的修改和存取都可以直接通过其内部Map的 Key(Redis里称内部Map的key为field), 也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题。很好的解决了问题。

这里同时需要注意,Redis提供了接口(hgetall)可以直接取到全部的属性数据,但是如果内部Map的成员很多,那么涉及到遍历整个内部Map的操作,由于Redis单线程模型的缘故,这个遍历操作可能会比较耗时,而其它客户端的请求完全不响应,这点需要格外注意。

上面已经说到Redis Hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现,这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。

List

常用命令:

lpush,rpush,lpop,rpop,lrange等。

应用场景:  

Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现,比较好理解,这里不再重复。

实现方式:  

Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。

Set

常用命令:  

sadd,spop,smembers,sunion 等。

应用场景:

Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。

实现方式:

set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。

Sorted set

常用命令:

zadd,zrange,zrem,zcard等

使用场景:

Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么 可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。

实现方式:

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

常用内存优化手段与参数

vm-enabled=no

关闭Redis的虚拟内存功能,并不成熟。

maxmemory

早前版本此参数是告诉Redis当使用了多少物理内存后就开始拒绝后续的写入请求,新一点的版本应该有这个参数maxmemory-policy来执行置换策略,具体如下:

  • ·volatile-lru在使用了过期设置的集合中,尝试删除一个最近没在用的键。
  • ·volatile-tt在使用了过期设置的集合中,尝试删除一个有较短expire时间的键。
  • ·volatile-random在使用了过期设置的集合中随机删除一个键。
  • ·allkeys-lru跟volatile-lru类似,但它会将每一种类型键都移除,不管是有效还是过期的只要设置了过期时间。
  • ·allkeys-random跟volatile-random类似,但它会将每一种类型键都移除,不管是有效还是过期的只要设置了过期时间。
hash-max-zipmap-entries 64 
hash-max-zipmap-value 512 
hash-max-zipmap-entries

当value这个Map内部不超过多少个成员时会采用线性紧凑格式存储,默认是64,即value内部有64个以下的成员就是使用线性紧凑存储,超过该值自动转成真正的HashMap。hash-max-zipmap-value 含义是当 value这个Map内部的每个成员值长度不超过多少字节就会采用线性紧凑存储来节省空间。以上2个条件任意一个条件超过设置值都会转换成真正的HashMap,也就不会再节省内存了。

list-max-ziplist-entries 512

list数据类型多少节点以下会采用去指针的紧凑存储格式。

list-max-ziplist-value 64

list数据类型节点值大小小于多少字节会采用紧凑存储格式。

set-max-intset-entries 512

set数据类型内部数据如果全部是数值型,且包含多少节点以下会采用紧凑格式存储。

最后想说的是Redis内部实现没有对内存分配方面做过多的优化,在一定程度上会存在内存碎片,不过大多数情况下这个不会成为Redis的性能瓶颈,不过如果在Redis内部存储的大部分数据是数值型的话,Redis内部采用了一个shared integer的方式来省去分配内存的开销,即在系统启动时先分配一个从1~n 那么多个数值对象放在一个池子中,如果存储的数据恰好是这个数值范围内的数据,则直接从池子里取出该对象,并且通过引用计数的方式来共享,这样在系统存储了大量数值下,也能一定程度上节省内存并且提高性能,这个参数值n的设置需要修改源代码中的一行宏定义REDIS_SHARED_INTEGERS,该值 默认是10000,可以根据自己的需要进行修改,修改后重新编译就可以了。

参考资料:

http://blog.csdn.net/freebird_lb/article/details/7778999

http://www.tuicool.com/articles/naeEJbv

http://www.cnblogs.com/lulu/archive/2013/06/10/3130878.html

Redis原理详解的更多相关文章

  1. sso单点登录原理详解

    sso单点登录原理详解     01 单系统登录机制    1.http无状态协议 web应用采用browser/server架构,http作为通信协议.http是无状态协议,浏览器的每一次请求,服务 ...

  2. I2C 基础原理详解

    今天来学习下I2C通信~ I2C(Inter-Intergrated Circuit)指的是 IC(Intergrated Circuit)之间的(Inter) 通信方式.如上图所以有很多的周边设备都 ...

  3. redis配置详解

    ##redis配置详解 # Redis configuration file example. # # Note that in order to read the configuration fil ...

  4. Zigbee组网原理详解

    Zigbee组网原理详解 来源:互联网 作者:佚名2015年08月13日 15:57   [导读] 组建一个完整的zigbee网状网络包括两个步骤:网络初始化.节点加入网络.其中节点加入网络又包括两个 ...

  5. 块级格式化上下文(block formatting context)、浮动和绝对定位的工作原理详解

    CSS的可视化格式模型中具有一个非常重要地位的概念——定位方案.定位方案用以控制元素的布局,在CSS2.1中,有三种定位方案——普通流.浮动和绝对定位: 普通流:元素按照先后位置自上而下布局,inli ...

  6. SSL/TLS 原理详解

    本文大部分整理自网络,相关文章请见文后参考. SSL/TLS作为一种互联网安全加密技术,原理较为复杂,枯燥而无味,我也是试图理解之后重新整理,尽量做到层次清晰.正文开始. 1. SSL/TLS概览 1 ...

  7. 锁之“轻量级锁”原理详解(Lightweight Locking)

    大家知道,Java的多线程安全是基于Lock机制实现的,而Lock的性能往往不如人意. 原因是,monitorenter与monitorexit这两个控制多线程同步的bytecode原语,是JVM依赖 ...

  8. [转]js中几种实用的跨域方法原理详解

    转自:js中几种实用的跨域方法原理详解 - 无双 - 博客园 // // 这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同 ...

  9. 节点地址的函数list_entry()原理详解

    本节中,我们继续讲解,在linux2.4内核下,如果通过一些列函数从路径名找到目标节点. 3.3.1)接下来查看chached_lookup()的代码(namei.c) [path_walk()> ...

随机推荐

  1. Dart语言学习(十五) Dart函数方法

    Dart函数方法可分为两类: 一.内置方法/函数: print(); 二.自定义方法: 自定义方法的基本格式: 返回类型 方法名称(参数1,参数2,...){ 方法体 return 返回值; } vo ...

  2. 15条MySQL改善经验让系统更稳定

    MySQL 作为目前的主流开源数据库, 既能挑战 “双11”,又能扛起 “618”,比起网红版数据库,MySQL 绝对是实力担当. 1. 为查询缓存优化查询 像 NOW() 和 RAND() 或是其它 ...

  3. P1029最大公约数和最小公倍数

    P1029最大公约数和最小公倍数 #include <iostream> #include <cmath> #include <algorithm> #define ...

  4. Python基础-4 运算符

    运算符 运算符:以1 + 2为例,1和2被称为操作数,"+" 称为运算符. Python语言支持以下类型的运算符: 算术运算符 比较(关系)运算符 赋值运算符 逻辑运算符 位运算符 ...

  5. Python学习第十九课——类的装饰器

    类的装饰器 # def deco(func): # print('==========') # return func # # # @deco #test=deco(test) # # def tes ...

  6. 【PAT甲级】1023 Have Fun with Numbers (20 分)

    题意: 输入一个不超过20位的正整数,问乘2以后是否和之前的数组排列相同(数字种类和出现的个数不变),输出Yes或No,并输出乘2后的数字. AAAAAccepted code: #define HA ...

  7. springboot 不停服动态更新定时任务时间(转)

    转 https://blog.csdn.net/u012129558/article/details/80834303 Spring框架自3.0版本起,自带了任务调度功能,好比是一个轻量级的Quart ...

  8. C++11并发编程1------并发介绍

    也许有人会觉得多线程和并发难用,复杂,还会让代码出现各种各样的问题,不过,其实它是一个强有力的工具,能让程序充分利用硬件资源,让程序运行得更快. 何谓并发: 两个或更多独立得活动同时发生.计算机中就是 ...

  9. C#Winfrom实现Skyline画直线功能

    C#Winfrom实现Skyline画直线功能 前言: 这里记录了我在学习Skyline二次开发中所遇到的问题,适合刚接触Skyline二次开发的同学查看使用,从逻辑到代码逐一详解,但是还是重在理解, ...

  10. SQL注入之SQLmap

    注意:sqlmap只是用来检测和利用sql注入点的,并不能扫描出网站有哪些漏洞,使用前请先使用扫描工具扫出sql注入点. 1.