redis -- 基本数据类型对象
2017-04-10 19:56
253 查看
redis:remote dictionary server
字面意思:远程“字典“服务器(哈哈哈哈)由Salvatore Sanfilippo写的key-value存储系统,属于nosql的一种,Redis是一个开源的使用ANSI c语言编写,遵守BSD协议,支持网络, 基于内存但是可以实现持久的一款数据库。
redis相对于数据库家族的优势:
redis有着更为复杂的数据类型,支持set、list、hash、string等,它的一个键最大可为512M,value最大可达1G。redis有着更高的运行速度和持久化的选择, redis是一款运行在内存的单进程单线程的异步数据库,单进程单线程并代表就low ,能读的速度是110000次/s,写的速度是81000次/s 。对于持持久化来说,它提供快照持久化和AOF持久化的方式选择设置,以及master-slave方式的数据备份,极大的方便了程序媛用户。
redis极高的支持原子化操作,redis的操作基本上都是原子化的,此外,多个操作合并后的原子性执行它也是支持的。
redis的安装:
网上教程多,它支持原码安装,看一下它的src目录下的关键文件redis中的对象类型:
命名规则:除了\n和空格不能作为名字的组成外,其他都可以出现,且长度不作要求。
redis基本数据对象类型模型图:
string对象类型:
string类型的基本操作:string对象类型是redis的基本数据类型, 但是它跟c语言里面的字符串常量或则字符数组不是同一个概念;在redis里面实现string对象有三种基本的方式:
如果string对象保存的是整数值, 并且这个值可以用long来表示, 那么redisobject里面的ptr指针从void*转变为long型, 字符串编码设置为int。这个编码方式并不是一成不变的,
当操作改变了存储对象,编码也许会变为raw。
如果string对象保存的是字符串值, 且这个字符串长度大于32字节, string对象将采用一个SDS来保存这个字符串,编码方式为raw, 调用两次内存分配,
一次是redisobject对象结构一次是sdshdr对象结构。
如果string对象保存的是字符串值, 且这个字符串长度小于32字节, string对象将采用一个embstr编码方式来保存,分配的内容跟raw编码一样,只不过它调用一次内存分配一块连续的空间,
这块空间包含两个结构体。严格意义上, embstr编码方式的字符串只是可读的,若操作改变, 则可能变为raw编码方式。
int
embstr
sdshdr – SDS
看一下SDS(简单动态字符串)的结构组成:SDS相对应c语言字符串的优势:
1. 将获取字符串长度的工作从0(n)降到0(1)的复杂度。
2. SDS API能够降SDS的空间自动扩展,解决溢出的问题。如c的strcat不检查溢出,以及SDS的sdscat的检查并扩展容量。这也是一种节约空间的策略;
3. 空间优化策略: 起作用的前提是SDS对象的数组长度内容频繁的改变 ;利用的是SDS对象的free属性
·······a、空间预分配:优化在字符数组增长;扩展数组时,额外分配空间(小于1M翻倍分配,大于1M多加1M);利用SDS的free属性来记录这个位置。
·······b、惰性空间释放:优化字符数组缩短;缩短数组时,并不通过内存重新分配回缩,而是调整free的值,记录,以待将来对这段空间的使用。
4. 字节数组,类型多;在c的数组里面他以
\0的方式标志结束一个字符串,内容中间不可包含空格;但是SDS中它是可以保存的,它采用二进制处理buf里面的内容,不会对其有任何的限制,存取不会造成差别,所以各种的文件类型都可以被它完好的引用。
list对象类型:
lis对象t类型的基本操作:编码方式:
1. ziplist 列表保存的所有字符串元素长度都小于64个字节;保存的元素个数小于512个。
2. 其余情况使用linklist。
linklist
linklist是一个对双向不循环链表进行过封装的对象,两边双向一共四个方向操作,可以练两结合,因此list既可以作为栈出现也可以作为队列出现。链表结点结构体: typedef struct listNode{ //前驱节点 struct listNode* prev; //后驱节点 struct listNode* next; //节点值 void *value; }listNode;
链表结构: typedef struct list{ //表头结点 listNode* head; //表尾结点 listNode* tail; //链表长度 unsigned long len; //结点值复制函数 void *(*dup)(void * ptr); //结点值释放函数 void (*free)(void * ptr); //结点值对比函数 void (*match)(void *ptr, void* key); }list;
实现模型:
ziplist
ziplist 压缩列表, 为了节约内存而出现, 特点:是由一系列特殊比阿妈的连续内存块组成的序列, 可以包含任意多个结点。zlbytes 记录整个压缩列表占用的内存字节数
zltail 记录压缩列表 表尾结点 距离列表起始位置的字节数
zllen 记录压缩列表包含的字节数
entry 列表结点
zlend 标记压缩列表的结束
压缩列表的结点:
结点包含三个属性:
其中previous_entry_length属性记录的是上一个结点的长度,如果上一个结点长度小于254个字节, 该属性用一个字节来保存它的长度, 否则用5个字节来保存。(利用这个属性, 可以实现向前的结点获取)
enccoding 属性标记结点保存的数据类型以及长度
content 保存结点的值
连锁更新:
当有结点的添加或着扩大修改或着删除中间小结点的时候, 可能后续结点的prvious_entry_length属性的大小由1变为5, 因此,该结点的大小如果大于254个字节。后继结点的prvious_entry_length属性应该变为5个字节,因此又引发一次空间重新分配。以此类推。
set类型:
可以实现数学里面的交,并,差集的运算,集合中的元素不重复。set类型的基本操作:
编码方式:
1. intset 集合对象保存的所有元素都是整数值,元素数量不超过512个。
2.其他情况使用hashtable
hashtable
intset
intset 整数集合 :各个数据项在数组中按值的大小从小到大有序的排列,并且数组中不包含任何重复项。整数集合的结构体: typedef struct inset{ uint32_t encoding; //编码方式 uint32_t length; //集合包含的元素数量 int8_t contents[]; //保存元素的数组 }inset;
整数集合的升级: 新添加的元素类型比整数集合现有的元素的类型要长,就要进行升级的活动。
升级三部曲:
1. 扩展底层数组的大小
2. 将底层数组现有的元素都换成与新元素相同的类型,并将转换后的元素放置到正确的位置上,这个过程保持元素的有序性。
3. 将新元素添加到底层数组里面。
注意:不存在回缩的降级。
sorted set有序对象类型:
每个元素都是一个值-权的组合,通过权值可以有序的获取集合中的元素。sorted set的基本操作:
有序对象zset的编码方式:
1. ziplist 保存元素小于128个, 所有雅俗怒的长度都小于64个字节。
2. 其余情况采用skiplist编码。
ziplist
ziplist 方式:每个集合元素使用两个紧挨着的仪器的压缩列表结点来保存,第一个结点保存元素的成员,第二个元素爆粗元素的分值。元素在压缩列表内按分值的从小到达进行排序。skiplist编码方式采用zset结构体的方式实现,zset结构体包含一个跳越表skiplist和一个字典。
typedef struct set{ zskiplist *zsl; dict *dict; }set;
skiplist – zset
使用skiplist编码的zset对象模型:跳越表:
跳越表: typedef struct zskiplist{ struct skiplistNode *header, *tail; //表头结点和表尾结点 unsigned long length; //表中结点数量 int level; // 表中层数最大的结点的层数 }zskiplist; 跳越表结点: typedef struct zskiplistNode{ struct zskiplistLevel{ //层 struct zskiplistNode *forward; //前进指针 unsigned int span; //跨度 }level[]; struct zskiplistNode *backward; //后退指针 double score; //分值 rob *obj; //对象 }zskiplistNode;
跳越表的结构模型:
hash对象类型:
键-值对集合,每个hash可以存储40多亿个键-值对。hansh对象的编码方式:
ziplist 哈希对象保存的所有键值对的键和值的字符串长度都小于64字节,哈希对象保存的键值对数量小于512个,满足以上条件使用ziplist。
不满足以上条件使用hashtable编码。
ziplist
采用ziplist的hash对象,首先将保存了键的压缩列表结点压入压缩列表表尾, 然后再将保存了值的压缩列表结点压入队尾, 这样键值对的位置是相邻的。hashtable
hashtable 编码采用字典作为底层的实现。字典:又称符号表、关联数组或映射。
字典: typedef struct dict{ dictType *type; //类型特定函数 void *privdata; //私有数据 ditch ht[2]; //哈希表, 包含两个数组项, 通常只用第一个, 第二个在进行rehash的时候使用。 int trehashind; //rehash索引 }dict; 哈希表: typedef struct dictch{ dictEntry **table; //哈希表数组 unsigned long size; //哈希表大小 unsigned long sizemask; //哈希表大小掩码, 用于计算索引值, 数值为size-1 unsigned long used; //哈希表已有的结点数量 }dictcht; 哈希表结点: typedef struct dictEntry{ void *key; union{ void *val; uint64_tu64; int64_ts64; } struct dictEntry *next; //后面连接的是哈希表结点, 将多个哈希值相同的键值对连接在一起, 以此来解决键冲突的问题。 }dictEntry;
对象模型:
解决键冲突:当两个或以上的键被分配到了哈希数组的同一个索引上的时候, 就称发生了冲突。
链地址法:将这些发生冲突的结点, 使用next指针构成一个单向的链表。以此来解决键冲突的问题。(通常将新的结点放在第一个位置, 因此添加的复杂度为 0(1))
rehash重整散列:这个动作不是一下完成的,具有渐进式的特点。
重新计算键的哈希值和索引值,然后将键值对放置到ht[1]哈希表的制定位置上。当所有的动作完成, ht[0]变成一个空表,将ht[1]设置为ht[0],并再创建一个空的哈希表, 为下一次的rehash做准备。
rehash的步骤;
1. 为ht[1]分配空间, 让字典同时持有ht[1] ht[0]两个哈希表。
2. 在字典维护一个索引计数器变量rehashidx,并将它设为0, 表示rehash正式开始。
3. 在reshah期间, 每次对字典的操作, 程序会顺带将ht[0]哈希表在rehashidx索引上的所有键值对rehash到ht[1],当工作完成rehashidx属性的值增加一。
4. 当rehash操作完全的完成, 程序将rehashidx属性的值设置为-1;
负载因子=哈希表已保存结点数量/哈希表大小
哈希表自动进行扩展的条件:
1. 服务器目前没有进行BGSAVE 或BGREWRITEAOF,并且哈希表的负载因子大于1。
2. 服务器目前进行BGSAVE 或BGREWRITEAOF,并且哈希表的负载因子大于5。
持久化:
为了数据安全, redis会把本身的数据以文件的形式保存到硬盘中,在服务器重启之后自动吧硬盘数据恢复到内存里面。将数据保存到硬盘的过程就叫做’持久化’。快照持久化RDB:
一次性把redis中的全部数据文件(键值对)保存一份存储在硬盘中。(适合数据量不大的情况,如10G以下)设置快照频率的配置:
手动发起快照持久化的方式有两种
save和
bgsave:
save是直接的阻塞服务器,直到工作完成,
bgsave会启动子进程。
RDB的载入是在服务器启动的时候如果检测到它的存在就会自动载入。(如果开启了AOF持久化,则载入AOF文件不载入RDB文件)
AOF(APPEND ONLY FILE)持久化:
把用户执行的指令(添加、修改、删除)都保存到备份到文件中,还原数据的时候及时执行具体写指令。注意:当在配置文件中开启AOF持久化的时候会清空redis数据库的全部内容,所以慎重!!
相关配置:
优化操作:
持久化的相关操作:
主从服务器:
目的:降低一个redis服务器的负载,将多个redis服务器设置为一个主服务器的slave服务器,master主服务器根slave服务器之间会进行自动的数据同步。将一个服务器设置为其他服务器的slave服务器的方法:
all
相关文章推荐
- 6.3 基本数据类型的对象包装类
- JAVA高级01_04 基本数据类型的对象包装类 2011-4-20
- redis基本数据类型分析
- JAVA 基本数据类型对象包装类新特性
- 黑马程序员-JAVA基础-基本数据类型对象包装类
- JAVA API系列----基本数据类型的对象包装类
- redis数据类型与基本操作
- 黑马程序员_学习记录12:String、StringBuffer、基本数据类型对象包装类
- 黑马程序员__String_StringBuffer_基本数据类型对象包装类
- 39.黑马程序员-基本数据类型对象包装类
- Java 基本数据类型和对象类型
- 16-1:基本数据类型对象包装类
- My_Java之笔记(2)-基本数据类型对象包装类
- 基本数据类型对象 及包装类学习
- 2006-07-28 Java的常用包,"=="和"equals"的用法,基本数据类型与引用类型,对象的克隆
- 基本数据类型和对象
- 黑马程序员:API及基本数据类型的对象包装类
- C++中提供了多种基本的数据类型。实际上,这些远不能满足我们的需求,如复数(第10章的例子大多是处理虚数的),再如分数。本任务将设计一个简单的分数类,完成对分数的几个运算。一则巩固基于对象编程的方法,
- 黑马程序员——java编程那些事儿____基本数据类型对象包装类及其jdk1.5新特性
- 常用对象API(基本数据类型对象包装类的概述和字符串转换成基本数值)