redis(2)---redis基本数据类型及常见命令
Redis的魅力
缓存大致可以分为两类,一种是应用内缓存,比如Map(简单的数据结构),以及EH Cache(Java第三方库),另一种就是缓存组件,比如Memached,Redis;Redis(remote dictionary server)是一个基于KEY-VALUE的高性能的
存储系统,通过提供多种键值数据类型来适应不同场景下的缓存与存储需求
存储结构
大家一定对字典类型的数据结构非常熟悉,比如map ,通过key value的方式存储的结构。 redis的全称是remote dictionary server(远程字典服务器),它以字典结构存储数据,并允许其他应用通过TCP协议读写字典中的内容。数
据结构如下
Redis常用命令网址:
http://redisdoc.com
数据类型
字符串类型
字符串类型是redis中最基本的数据类型,它能存储任何形式的字符串,包括二进制数据。你可以用它存储用户的邮箱、json化的对象甚至是图片。一个字符类型键允许存储的最大容量是512M
内部数据结构
在Redis内部,String类型通过 int、SDS(simple dynamic string)作为结构存储,int用来存放整型数据,sds存放字节/字符串和浮点型数据。在C的标准字符串结构下进行了封装,用来提升基本操作的性能,同时也充分利用已有的
C的标准库,简化实现逻辑。我们可以在redis的源码中【sds.h】中看到sds的结构如下;
typedef char *sds;
redis3.2分支引入了五种sdshdr类型,目的是为了满足不同长度字符串可以使用不同大小的Header,从而节省内存,每次在创建一个sds时根据sds的实际长度判断应该选择什么类型的sdshdr,不同类型的sdshdr占用的内存空
间不同。这样细分一下可以省去很多不必要的内存开销,下面是3.2的sdshdr定义
`struct __attribute__ ((__packed__)) sdshdr8
{ 8表示字符串最大长度是2^8-1 (长度为255)`
`uint8_t len;//表示当前sds的长度(单位是字节)``
uint8_t alloc; //表示已为sds分配的内存大小(单位是字节)``
unsigned char flags; //用一个字节表示当前sdshdr的类型,因为有sdshdr有五种类型,所
以至少需要3位来表示
000:sdshdr5,001:sdshdr8,010:sdshdr16,011:sdshdr32,100:sdshdr64。
高5位用不到所以都为0。``
char buf[];//sds实际存放的位置``};`
sdshdr8的内存布局
列表类型
列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素或者获得列表的某一个片段。列表类型内部使用双向链表实现,所以向列表两端添加元素的时间复杂度为O(1), 获取越接近两端的元素速度就越快。
这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是很快的
内部数据结构
redis3.2之前,List类型的value对象内部以linkedlist或者ziplist来实现, 当list的元素个数和单个元素的长度比较小的时候,Redis会采用ziplist(压缩列表)来实现来减少内存占用。否则就会采用linkedlist(双向链表)结构。
redis3.2之后,采用的一种叫quicklist的数据结构来存储list,列表的底层都由quicklist实现。这两种存储方式都有优缺点,双向链表在链表两端进行push和pop操作,在插入节点上复杂度比较低,但是内存开
销比较大; ziplist存储在一段连续的内存上,所以存储效率很高,但是插入和删除都需要频繁申请和释放内存;quicklist仍然是一个双向链表,只是列表的每个节点都是一个ziplist,其实就是linkedlist和ziplist的结合,quicklist
中每个节点ziplist都能够存储多个数据元素,在源码中的文件为【quicklist.c】,在源码第一行中有解释为:Adoubly linked list of ziplists意思为一个由ziplist组成的双向链表;
hash类型
数据结构
map提供两种结构来存储,一种是hashtable、另一种是前面讲的ziplist,数据量小的时候用ziplist. 在redis中,哈希表分为三层,分别是,源码地址【dict.h】
dictEntry
管理一个key-value,同时保留同一个桶中相邻元素的指针,用来维护哈希桶的内部链;
typedef struct dictEntry {
void *key;
union { //因为value有多种类型,所以value用了union来存储
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;//下一个节点的地址,用来处理碰撞,所有分配到同一索引的元素通
过next指针链接起来形成链表key和v都可以保存多种类型的数据
} dictEntry;
dictht
实现一个hash表会使用一个buckets存放dictEntry的地址,一般情况下通过hash(key)%len得到的值就是buckets的
索引,这个值决定了我们要将此dictEntry节点放入buckets的哪个索引里,这个buckets实际上就是我们说的hash表。dict.h的dictht结构中table存放的就是buckets的地址
typedef struct dict {
dictType *type;//dictType里存放的是一堆工具函数的函数指针,
void *privdata;//保存type中的某些函数需要作为参数的数据
dictht ht[2];//两个dictht,ht[0]平时用,ht[1] rehash时用
long rehashidx; //当前rehash到buckets的哪个索引,-1时表示非rehash状态
int iterators; //安全迭代器的计数。
} dict;
比如我们要讲一个数据存储到hash表中,那么会先通过murmur计算key对应的hashcode,然后根据hashcode取模得到bucket的位置,再插入到链表中 img
集合类型
集合类型中,每个元素都是不同的,也就是不能有重复数据,同时集合类型中的数据是无序的。一个集合类型键可以存储至多232-1个 。集合类型和列表类型的最大的区别是有序性和唯一性
集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在。由于集合类型在redis内部是使用的值为空的散列表(hash table),所以这些操作的时间复杂度都是O(1).
数据结构
Set在的底层数据结构以intset或者hashtable来存储。当set中只包含整数型的元素时,采用intset来存储,否则,采用hashtable存储,但是对于set来说,该hashtable的value值用于为NULL。通过key来存储元素
有序集合
有序集合类型,顾名思义,和前面讲的集合类型的区别就是多了有序的功能在集合类型的基础上,有序集合类型为集合中的每个元素都关联了一个分数,这使得我们不仅可以完成插入、删除
和判断元素是否存在等集合类型支持的操作,还能获得分数最高(或最低)的前N个元素、获得指定分数范围内的元素等与分数有关的操作。虽然集合中每个元素都是不同的,但是他们的分数却可以相同
数据结构
zset类型的数据结构就比较复杂一点,内部是以ziplist或者skiplist+hashtable来实现,这里面最核心的一个结构就是skiplist,也就是跳跃表
归纳总结补充Redis底层数据结构:
字符串(比较简单)
列表(list)
对应两种实现方法:一种压缩列表(ziplist),一种双向循环链表
当列表中存储的数据量比较小的时候,采用压缩列表,满足以下两个条件:
(1)列表中保存的单个数据(可能是字符串类型的)小于64字节
(2)列表中的数据个数少于512个
压缩列表: 不是基础数据结构,而是redis自己设计的一种数据储存结构。类似数组,通过一片连续的内存空间来存储数据。不过,他与数组不同的是,它允许存储的数据大小不同。
这种数据结构,一方面比较节省内存,另一方面又可以支持不同数据类型的存储。而且,因为数据存储在一片连续的内存空间(对缓存非常友好),通过键来获取值为列表类型的数据,读取的效率也非常高。
当存储数据不满组上面两个条件,则用双向链表实现(上述已描述)。
字典(hash)
字典类型数据用来存储一组数据对。每个数据对包含键值两部分。字典实现方式。一、压缩列表 二、散列表
当字典中存储的数据量比较小的时候,采用压缩列表,满足以下两个条件:
(1)字典中保存的键和值的大小都要小于64字节
(2)字典中的键值对个数少于512个
不满足则使用散列表,redis使用MurmurHash2()这种运行速度快,随机性好的哈希算法作为哈希函数。对于哈希冲突,redis使用链表法来解决。除此之外,redis还支持散列表的动态扩容、缩容。
当数据动态增加后,散列表的装载因子会不停的变大。为了避免散列表的性能的下降,当装载因子大于1时,redis会触发扩容,将散列表扩大为原来的大小的2倍左右。
当数据动态减少后,散列表的装载因子会不停的变小。为了避免散列表的性能的下降,当装载因子小于0.1时,redis会触发索容,将散列表缩小为原来的大小的2倍左右。
而扩容、缩容需要大量的数据搬迁和哈希值的重新计算,比较耗时。针对这个问题,redis使用了渐进式扩容缩容策略,将数据的搬移分批进行,避免了大量数据一次性搬移导致的服务停顿。
渐进式扩容缩容策略:为了解决一次性扩容耗时过多的情况,我们可以将扩容操作穿插在插入操作的过程中,分配完成。当装载因子达到阈值后,我们只申请新空间,但不将老的数据搬移到新散列表中。
当有新数据要插入时,直接插入到新散列表中,并且从老的散列表中拿出一个数据放入到新散列表中。每次插入一个数据时,重复上面过程,把数据一点一点进行搬移。用这样均摊的方法,任何情况下,插入一个数据的时间复杂度为O(1)
期间的查询操作,谦容性考虑,先从新的散列表中查询,没找到再去老的散列表中查找。
哈希表解决哈希冲突的方法:一、开放寻址法(基于数组,内存友好序列化简单,但删除麻烦先标记,容易冲突) 当数据量比较小、装载因子小的时候适用,比如:ThreadLocalMap适用 开放寻址法解决散列冲突问题。
二、链表法 适用于存储大对象、大数据量的散列表,比起开放寻址法更加灵活,支持优化策略,比如用红黑树、跳表代替链表(当链表过大时)。
散列表的设计:
要求:
支持快速的查询、插入、删除操作
内存占用合理,不能浪费过多的内存
性能稳定
设计:
定义一个合适的散列函数即哈希函数
定义装载因子阈值,设计动态扩容缩容策略
选择合适的散列冲突解决办法
集合和有序集合 都是基于hash的
redis(2)---redis基本数据类型及常见命令的更多相关文章
- Redis学习之二 数据类型和相关命令
原文:https://www.cnblogs.com/lonelyxmas/p/9073928.html 如果还不懂安装的,请看 Windows环境下安装Redis Redis一共支持五种数据类型 1 ...
- Redis 基础及各数据类型对应的命令
Redis 命令文档 基本概念 安装及使用 可以在官网下载源码编译安装.对于 CentOS,还可以通过 yum install redis 安装. Redis 安装完成后,通过 redis-serve ...
- 缓存系列之三:redis安装及基本数据类型命令使用
一:Redis是一个开源的key-value存储系统.与Memcached类似,Redis将大部分数据存储在内存中,支持的数据类型包括:字符串.哈希表.链表.集合.有序集合以及基于这些数据类型的相关操 ...
- redis五种数据类型和常用命令及适用场景
一.redis的5种数据类型: 1.基础理解: string 字符串(可以为整形.浮点型和字符串,统称为元素) list 列表(实现队列,元素不唯一,先入先出原则) set 集合(各不相同的元素) h ...
- Redis数据类型和常用命令
Redis相较于其它的数据库虽然简单,但是要熟记所有命令的用法也并非易事.一个简单的技巧是通过要操作的数据类型来将这些命令进行结构化. 数据类型和对应命令 所有存储于redis中的数据都对应于一个键值 ...
- Redis的数据类型及其常用命令
快速入门Redis 首先安装redis: windows下安装redis Linux下安装redis 1. 什么是redis Redis属于nosql(非关系型数据库) 关系型数据库是基于关系表的数据 ...
- Redis(三)数据类型
之前的文章中说了Redis的常见应用场景和特性,在特性章节中也大致说了数据结构契合场景.因为我想在更深入.更全面的学习Redis之前,了解场景和特性,才能在学习时更加全面且理解更透彻: redis的什 ...
- Redis服务之常用数据类型
上一篇博客我们聊了下redis的主从复制.aof持久化.集群.慢日志相关配置指令的说明,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/13416534.html ...
- redis基础:redis下载安装与配置,redis数据类型使用,redis常用指令,jedis使用,RDB和AOF持久化
知识点梳理 课堂讲义 课程计划 1. REDIS 入 门 (了解) (操作) 2. 数据类型 (重点) (操作) (理解) 3. 常用指令 (操作) 4. Jedis (重点) (操作) ...
随机推荐
- WCF类型共享技巧【转载】
调用过WCF服务的同学可能都会遇到这样的问题,同一个实体类型,不同的服务Visual Studio生成了不同的版本,例如Service1.User和Service2.User,对于C#来说,这是两个不 ...
- PHP localhost和127.0.0.1 的区别
- Ubuntu 14.10 下安装伪分布式hbase 0.99.0
HBase 安装分为:单击模式,伪分布式,完全分布式,在单机模式中,HBase使用本地文件系统而不是HDFS ,所有的服务和zooKeeper都运作在一个JVM中.本文是安装的伪分布式. 安装步骤如下 ...
- Hadoop概念学习系列之谈hadoop/spark里分别是如何实现容错性?(四十二)
Hadoop使用数据复制来实现容错性(I/O高) Spark使用RDD数据存储模型来实现容错性. RDD是只读的.分区记录的集合.如果一个RDD的一个分区丢失,RDD含有如何重建这个分区的相关信息. ...
- PAT 乙级 1010 一元多项式求导 (25) C++版
1010. 一元多项式求导 (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 设计函数求一元多项式的导数.(注:xn(n为整数)的一 ...
- 窗口事件onresize
在做自适应布局的时候,我们常常需要根据窗口不同的分辨率给出不同布局和样式,今天说的onresize便能帮我们实现这一效果. onresize事件在窗口或者框架的大小发生改变的时候会被调用,下面我们用一 ...
- NIO文件锁FileLock
目录 <linux文件锁flock> <NIO文件锁FileLock> <java程序怎么在一个电脑上只启动一次,只开一个进程> 文件锁可以是shared(共享锁) ...
- WordPress设置地址的问题
刚刚安装了一个Wordpress,第一次使用,所以对设置不是很熟悉. 在常规设置那里,有两个地址设置,一个是WordPress 地址(URL),另一个是站点地址(URL),刚开始分不清这两个的区别,所 ...
- DeviceIOControl与驱动层 - 缓冲区模式
IO交互模式中的DeviceIOControl与驱动层交互有三种:缓冲区模式.直接访问模式.其他模式,这里本人学习的是缓冲区访问模式,原理如图: 驱动中最好不要直接访问用户模式下的内存地址,使用缓冲区 ...
- [UE4]Get All Widgets Of Class、Get All Widgets with Interface,根据类名或者接口UI实例对象
Get All Widgets Of Class.Get All Widgets with Interface,是系统蓝图函数库提供的方法,可以在任何蓝图中使用. 可以方便地获得UI实例对象,进而使用 ...