Redis教学讲解课件_第1页
Redis教学讲解课件_第2页
Redis教学讲解课件_第3页
Redis教学讲解课件_第4页
Redis教学讲解课件_第5页
已阅读5页,还剩89页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

redisredis目录背景Redis简介Redis是什么功能与特点内部实现(单机)整体数据框架基本数据结构优化机制索引优化内存优化持久化主从备份集群Key-value数据库设计与其他数据库对比Redis不足应用场景目录背景主从备份国际上最大的redis用户是谁?在众多的NOSQL数据库与传统的关系数据库中为什么会出现redis?传统的key-value数据库(文档,string类型)有什么不足?----数据结构需求传统的关系型数据库IO操作性能问题?----性能需求传统的内存数据库有什么不足?---可靠性需求背景国际上最大的redis用户是谁?在众多的NOSQL数据库与传背景传统MySQL+Memcached架构遇到的问题1.MySQL需要不断进行拆库拆表,Memcached也需不断跟着扩容,扩容和维护工作占据大量开发时间。2.Memcached与MySQL数据库数据一致性问题。3.Memcached数据命中率低或down机,大量访问直接穿透到DB,MySQL无法支撑。4.跨机房cache同步问题。背景传统MySQL+Memcached架构遇到的问题Redis是一个开源的使用c语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value,多种数据结构的数据库,并提供多种语言(c,c#,java,javascript,perl,php,python,ruby,scala,erlang等)的API,仅有1万行代码。稳定版本发布于2011年3月4日。从2010年3月15日起,Redis的开发工作由VMware主持。应用场景:1.memorycache+mysql不能完全解决web2.0

需求

(mc穿透,跨机房问题,数据一致性

2.大量数据高速读写,数据结构需求。Redis简介Redis是一个开源的使用c语言编写、支持网络、可基于内存亦功能特点数据库数据存储持久化高速读写集群部署动态扩展数据一致性事务性操作主从备份Sche-mefree数据结构高并发vmredis支持支持目前仅单实例部署,但通过客户端做pre-sharding方案可搭建伪集群支持支持但不支持回滚支持,但是主机迭机后,不能自动选举丛机位为主机Key-value模型,支持支持(单线程的IO复用模型)支持一定量的并发数支持功能特点数据库数据存储持久化高速读写集群部署数据一致性事务性性能测试:CPU为Xeon2.80GHz*4内存为4G硬盘为一块400GSATA盘操作系统为64位CentOS5.3版本写测试:写入500万条记录,共耗时524秒,平均每秒写入数据9542笔。磁盘上的数据文件大小134M。读测试:成功读出500万条记录,共耗时184秒,平均每秒读出数据27174笔。性能测试:CPU为Xeon2.80GHz*4写测试:写入整体数据结构redisServerredisDB*db...01...nredisDBintiddict*dict...redisDBintiddict*dict...redisDBintiddict*dict...结构体redisServer对应服务器,字段db指向一个指针数组,数组元素值即各个数据库的入口地址。结构体redisDB对应数据库,用以保存数据库id,字典等信息。整体数据结构redisServerredisDB*db..redisDBredisDBintiddict*dict...dictdictType*typedictht*ht[0]dictht*ht[1]...dicthtdictEntry**tableusignedlongsizeusignedlongused...dictTypeint(*hashFunc)(void*key)...dicthtdictEntry**tableusignedlongsizeusignedlongused...dict即字典,type是字典类型,ditcht是哈希表。函数指针hashFunc记录用以计算hash值的hash函数。redisDBredisDBintiddict*dictDictdictdictType*typedictht*ht[0]dictht*ht[1]...dicthtdictEntry**tableusignedlongsizeusignedlongused...01...size-1dictEntryvoid*keyvoid*val*nextNULLNULL双重指针table指向一个数组,该数组记录各个dictEntry(条目?)的入口地址。size为table的大小,used是dictEntry的个数。每个dictEntry对应一个存储对象,通过key和val指针可找到对应对象的key和value。used就是存储的对象总数。DictdictdictType*typedictht*key和value的值存在哪?dictEntryvoid*keyvoid*val*nextredisObjectunsignedtype:4void*ptr...redisObjectunsignedtype:4void*ptr...keyvalueredisObject可以看成是对要存储的各种对象(如String、List、Set等)的抽象,type(长4bit的整数)用于标识该对象所属的数据结构的种类,ptr指向对象在内存中的地址。key和value的值存在哪?dictEntryvoid*一次查询流程由key找到碰撞链入口的时间性能是O(1)。在碰撞链中遍历查询的时间性能是O(n)。一次查询流程由key找到碰撞链入口的时间性能是O(1)。Redis并不是简单的key-value存储,它实际上是一个数据结构服务器。不仅可以用数据库预定义的数据结构存储的value,还支持这些数据结构的基本操作。

预定义的数据结构有:StringListofStringSetofStringSortedSetofStringHashTableofString基本数据结构Redis并不是简单的key-value存储,它实际上是一个StringString是最简单的类型,值可以是任何种类的字符串(包括二进制数据)。String的长度是int类型,最大长度为1GB每个对象的key都以String存储若数据库中所有value都为String,则Redis就像一个可持久化的memcachedstructsdshdrintlenintfreecharbuf[]redisObjecttype=stringvoid*ptr...StringstructsdshdrintlenintListofStringListlistNode*headlistNode*tailunsignedintlen...listNodeprev=nulllistNode*nextvoid*valuelistNodelistNode*prevlistNode*nextvoid*valueList基于双向链表实现。在头部或尾部添(删)一个结点,时间复杂度为常数级别用List支持的一些操作,如lpush(头添加)、lpop(头删除),rpush、lpop可以很容易地实现栈和队列redis向外部提供的list中listNode保存的value是String类型的listNodelistNode*prevNext=nullvoid*valueListofStringListlistNode*heHashTableofStringdictEntryvoid*keyvoid*val*nextredisObjecttype=stringvoid*ptr...redisObjecttype=hashvoid*ptr...keydictdictType*typedictht*ht[0]dictht*ht[1]...redis数据组织的整体框架就是hashtable,hashtableofstring只不过将hashtable特例化。它还是以key-value形式存储,对hashtableofstring中对象的一次成功查询要经过两次hash。HashTableofStringdictEntryvosetredis支持的集合有两种,无序集合set和有序集合zset。zset的数据结构比较复杂,尚未弄懂。暂时只知道它基于有序链表和hashtable实现(用hashtable组织元素,链表实现排序),set又是基于zset实现。zset中的元素有一个权重参数score,使得集合中的元素能够按score进行有序排列忽略zset的权重score就可以得到set的实现集合提供了求交集、并集、差集等操作setredis支持的集合有两种,无序集合set和有序集合z优化机制索引优化随着hashtable中key-value对的不断增多,碰撞也越来越多,碰撞链越来越长,势必会影响到查询效率。为了保持查询效率,必须调整hashtable的索引结构,使每个索引上的碰撞链长度不至于太长。内存优化redis是个内存数据库,同等业务量下redis占用的内存越小越好。为了占用更小的内存,必须对内存进行优化。优化机制索引优化索引优化rehash是在hashtable大小不能满足需求,造成过多碰撞后需要进行扩容时的操作。基本思想:新建一张索引表,新表的索引空间为原表的两倍遍历旧表中的所有dictEntry,调用hash函数计算得到每个dictEntry在新表中的索引,并添加到新表上。当所有dictEntry都添加到新表中,启用新表,丢弃旧表索引通过hashFunc(key)%size算得,因为新表索引空间是原来的两倍,在新表中发生碰撞的概率将会小于原表。这样原本在一个碰撞链上的多个dictEntry就可能分布到不同的新索引上,新表碰撞链的平均长度(dictEntry个数/size)理论是旧表的一半索引优化rehash是在hashtable大小不能满足需求,rehashdictdictType*typedictht*ht[0]dictht*ht[1]...dicthtdictEntry**tablesize0usignedlongused...dicthtdictEntry**tablesize=2*size0usignedlongused...01...size0-1dictEntryNULLNULLdictEntry01...size-1ht[1]在平时为NULL,只有当rehash时用于指向新建的table。当在rehash时如果有新的key-value要添加到数据库,添加到新表ht[1]中。hashFunc(key)%sizerehashdictdictType*typedicthtrehashdictdictType*typedictht*ht[0]dictht*ht[1]...dicthtdictEntry**tablesize=2*size0usignedlongused...01...size-1dictEntryNULLNULLdictEntryNULL当旧表ht[0]中的所有dictEntry都rehash到新表后,让ht指向新表,ht[1]指向空,释放旧表的内存。rehash的结果是索引空间增至原来的2倍,碰撞链的平均长度used/size减小。rehashdictdictType*typedicthtrehashrehash是在hashtable需要扩容时进行,什么时候需要扩容?每添加一个dictEntry到hashtable就要判断是否要进行扩容redis默认当dictEntry的个数大于等于表容量size时进行扩容,即平均碰撞链长used/size刚超过1时就扩容,默认扩容到原来的两倍默认的扩容条件可使平均碰撞链长保持在1以下,虽然保证了查询效率,但会频繁rehash,内存消耗大。可通过修改扩容条件减少rehash频率,但要改扩容条件,只有修改源代码rehashrehash是在hashtable需要扩容时进行内存优化redis内存优化的策略有很多,比较常用的有两种,大体思想如下:通过特殊的编码方式存储对象,以节省内存。如把数字字符串以整数方式存储,例如用整数存储数字符串“123”只需要1个字,对于数值较大的数字字符串,分割后用数组存储当满足一定条件时,减小hashtable的容量,再rehash。默认在当used/size比不足10%时,将size改为used,再rehash得到容量是used两倍的新表,新表的容量将小于原表的1/5内存优化redis内存优化的策略有很多,比较常用的有两种,大快照(SnapShot)当前数据的快照存成一个数据文件的持久化机制频率控制:过程:Redis通过fork产生子进程父进程继续处理client请求,子进程负责将快照写入临时文件子进程写完后,用临时文件替换原来的快照文件,然后子进程退出持久化-快照

save900

1

#900秒超过1个key被修改save300

10

#300秒超过10个key被修改save60

10000

#60秒超过一万条被修改快照(SnapShot)持久化-快照

save900优点:-整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的

-对于灾难恢复,可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上

-

性能最大化-相比于AOF机制,如果数据集很大,RDB的启动效率会更高。

持久化-快照

优点:持久化-快照

会产生什么问题呢?每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。另外由于快照方式是在一定间隔时间做一次的,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改如果应用不能丢失任何修改的话,可以采用aof持久化方式

持久化-快照

会产生什么问题呢?持久化-快照

持久化-日志AOF(appendonlyfile)

记录每次的读写操作的文件在Redis配置文件中有一个叫appendonly的选项,可以写yes或no.这个选项就是负责是否开启AOF日志的开关.日志文件通病,如果只增不减的话,那文件将会无限长大,执行bgrewriterof命令,先给当前的所有数据做一个快照.然后再在这个快照的基础上写接下来的日志,删除旧的日志.持久化-日志AOF(appendonlyfile)持久化-日志Bgrewriterof内部实现Redis通过fork一个子进程,遍历数据,写入新临时文件父进程继续处理client请求,子进程继续写临时文件父进程把新写入的AOF写在缓冲区子进程写完退出,父进程接收退出消息,将缓冲区AOF写入临时文件临时文件重命名成appendonly.aof,原来文件被覆盖,整个过程完成

持久化-日志Bgrewriterof内部实现持久化-日志频率控制(redis.conf):

写入磁盘时机命令:#appendonlyyes

//启用aof持久化方式

#appendfsyncalways

//每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用

appendfsynceverysec

//每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐

#appendfsyncno

//完全依赖os,性能最好,持久化没保证save900

1

#900秒超过1个key被修改save300

10

#300秒超过10个key被修改save60

10000

#60秒超过一万条被修改持久化-日志频率控制(redis.conf):save持久化分析快照易丢失数据,AOF模式较为安全,但日志重写时磁盘io开销大,容易导致服务器性能严重下降,并且需要足够的物理内存,这个比较危险,所以在高性能服务器上一般是用主从复制来进行持久化。持久化分析快照易丢失数据,AOF模式较为安全,但日志重写时磁主从架构slaveclientclientclientmastermastermasterslaveslaveslaveCONSISTENTHASH/定容readwrite同步数据SESSION主从架构slaveclientclientclientmas主从复制配置:只需要在配置文件中加入如下配置

slaveof192.168.1.16379

#指定master的ip和端口.存在问题:Slave从库在连接Master主库时,Master会进行内存快照,然后把整个快照文件发给Slave,也就是没有象MySQL那样有复制位置的概念,即无增量复制,这会给整个集群搭建带来非常多的问题。主从复制配置:主从复制主从复制集群假如业务增长很快,很快就会发现当前的容量已经不够了,Redis里面存储的数据很快就会超过物理内存大小,那么如何进行Redis的在线扩容呢?方案一:下一版本的redis自身支持在线扩容redis实例,这一功能正在开发中。方案二:客户端做presharding方案集群假如业务增长很快,很快就会发现当前的容量已经不够了,Re客户端sharding方案Node(redis实例)=hash(key)modN;对客户端需要set或get的每个key,做hash运算再取模,映射到N个实例中的一个,可以搭建多实例集群。问题当系统规模增大或缩小时,需要动态增加或减少redis实例。对于增加实例情况后,N值将会变大,存储在原来实例上的所有key将要重做hash与取模运算(rehash过程),得到自己存储在新的redis实例位置。重做rehash,迁移大量的key工作量非常大,管理复杂。客户端sharding方案Node(redis实例)=hasPresharding方案前提:Redis非常轻量,一个Redis实例占用的内存非常小(1M左右),所以在一台服务器上部署多个实例(32、64、128,1000...)完全没有问题。思路:假设有N台主机,每台主机上部署M个实例,整个系统有T=NxM个实例,扩容前后实例总数不变。Presharding方案前提:Redis非常轻量,一个RePresharding方案前期多个实例部署在一台机器上,后期时每个实例独占一台物理机器内存,通过单机redis实例的内存增加,达到集群整体内存的大幅度上升。由于hash取模算法里的N值前后不变,避免rehash过程redis实例上的key重排。前期业务量小时,可配置少量廉价性能低机器满足业务,后期业务量大,配置很多台高性能高内存机器适应业务增长。Presharding方案前期多个实例部署在一台机器上,后期如何在线迁移扩容1.在新机器上启动好对应端口的Redis实例。2.配置新端口为待迁移端口的从库。3.待复制完成,与主库完成同步后,切换所有客户端配置到新的从库的端口。4.配置从库为新的主库。5.移除老的端口实例。6.重复上述过程迁移好所有的端口到指定服务器上。问题:redis实例增多后,导致运维管理成本增加,各个实例的开启关闭,aof文件与rdb文件的收集管理很繁琐。如何在线迁移扩容1.在新机器上启动好对应端口的Redis实例redis数据库结构设计用户登录系统,记录用户登录信息的一个系统关系型数据库的设计KV数据库Kv数据库记录为:或者redis数据库结构设计用户登录系统,记录用户登录信息的一个关系数据库中复杂多对多关系跨表查询,性能低下。在结构化key-value数据库表现为在内存中对多个集

合的交集与并集运算查找属于ruby又属于web的书redis.sinter("tag.web","tag:ruby")关系型数据库表结构化key-value数据库表关系数据库中复杂多对多关系跨表查询,性能低下。关系型数据库表

Redis与Memcached的比较1.网络IO模型Memcached是多线程,非阻塞IO复用的网络模型。Redis使用单线程的IO复用模型,封装了一个AeEvent事件处理框架。2.内存管理方面Memcached使用预分配的内存池的方式,Redis使用现场申请内存的方式来存储数据,非临时数据是永远不会被剔除的,还可以配置虚拟内存,获得高于物理内存的空间。

Redis与Memcached的比较1.网络IO模型Redis与Memcached的比较3.数据一致性问题Memcached提供了cas命令,可以保证多个并发访问操作同一份数据的一致性问题。Redis没有提供cas命令,并不能保证这点,Redis提供了事务的功能,可以保证一串命令的原子性,中间不会被任何操作打断。4.存储方式及其它方面

Memcached基本只支持简单的key-value存储,不支持枚举,不支持持久化和复制等功能,Redis除key/value之外,支持数据结构,Redis可以直接扫描其dump文件,枚举出所有数据,还同时提供了持久化和复制等功能。Redis与Memcached的比较3.数据一致性问题Redis不足之处支持事务,将多个命令打包执行,但是任一命令有语法错误或key-value数据类型错误照常运行数据结构不支持嵌套其他数据结构,比如类型为list的value不能再嵌套一个list。单线程模型处理所客户请求的命令。对高并发支持不是很好主从做冗余备份时,主机迭机后,不能从多个从机中自动选举出主机当Dump.rdb文件中没有日志文件appendonly.aof最新数据时,不能从日志文件的最新命令的当前位置处增量更新dump.rdb数据文件。Redis不足之处支持事务,将多个命令打包执行,但是任一命令应用场景1.在主页中显示最新的项目列表。LPUSH用来插入一个内容ID,作为关键字存储在列表头部。LTRIM用来限制列表中的项目数最多为5000。2.排行榜。排行榜按照得分进行排序。ZADD命令可以直接实现这个功能,而ZREVRANGE命令可以用来按照得分来获取前100名的用户,ZRANK可以用来获取用户排名应用场景1.在主页中显示最新的项目列表。应用场景3.计数。进行各种数据统计,比如想知道什么时候封锁一个IP地址。INCRBY命令让这些变得很容易,通过原子递增保持计数;GET,SET用来重置计数器;过期属性用来确认一个关键字什么时候应该删除。4.需要精准设定过期时间的应用5.构建队列系统使用list可以构建队列系统,使用sortedset甚至可以构建有优先级的队列系统。应用场景3.计数。应用场景Sina微博关系+数字sina微博中好友关系用hash存储,分为关注fromuid.following与粉丝touid.followerkey为userid,fields为friendsids,value为addtime。添加一个关注:hsetfromuid.followingtouidaddtime

增加一个粉丝:hsettouid.followerfromuidaddtime相互关注:hsinterfromuid.followingtouid.follower应用场景Sina微博关系+数字结束语当你尽了自己的最大努力时,失败也是伟大的,所以不要放弃,坚持就是正确的。WhenYouDoYourBest,FailureIsGreat,SoDon'TGiveUp,StickToTheEnd结束语47谢谢大家荣幸这一路,与你同行It'SAnHonorToWalkWithYouAllTheWay演讲人:XXXXXX时间:XX年XX月XX日

谢谢大家演讲人:XXXXXX48redisredis目录背景Redis简介Redis是什么功能与特点内部实现(单机)整体数据框架基本数据结构优化机制索引优化内存优化持久化主从备份集群Key-value数据库设计与其他数据库对比Redis不足应用场景目录背景主从备份国际上最大的redis用户是谁?在众多的NOSQL数据库与传统的关系数据库中为什么会出现redis?传统的key-value数据库(文档,string类型)有什么不足?----数据结构需求传统的关系型数据库IO操作性能问题?----性能需求传统的内存数据库有什么不足?---可靠性需求背景国际上最大的redis用户是谁?在众多的NOSQL数据库与传背景传统MySQL+Memcached架构遇到的问题1.MySQL需要不断进行拆库拆表,Memcached也需不断跟着扩容,扩容和维护工作占据大量开发时间。2.Memcached与MySQL数据库数据一致性问题。3.Memcached数据命中率低或down机,大量访问直接穿透到DB,MySQL无法支撑。4.跨机房cache同步问题。背景传统MySQL+Memcached架构遇到的问题Redis是一个开源的使用c语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value,多种数据结构的数据库,并提供多种语言(c,c#,java,javascript,perl,php,python,ruby,scala,erlang等)的API,仅有1万行代码。稳定版本发布于2011年3月4日。从2010年3月15日起,Redis的开发工作由VMware主持。应用场景:1.memorycache+mysql不能完全解决web2.0

需求

(mc穿透,跨机房问题,数据一致性

2.大量数据高速读写,数据结构需求。Redis简介Redis是一个开源的使用c语言编写、支持网络、可基于内存亦功能特点数据库数据存储持久化高速读写集群部署动态扩展数据一致性事务性操作主从备份Sche-mefree数据结构高并发vmredis支持支持目前仅单实例部署,但通过客户端做pre-sharding方案可搭建伪集群支持支持但不支持回滚支持,但是主机迭机后,不能自动选举丛机位为主机Key-value模型,支持支持(单线程的IO复用模型)支持一定量的并发数支持功能特点数据库数据存储持久化高速读写集群部署数据一致性事务性性能测试:CPU为Xeon2.80GHz*4内存为4G硬盘为一块400GSATA盘操作系统为64位CentOS5.3版本写测试:写入500万条记录,共耗时524秒,平均每秒写入数据9542笔。磁盘上的数据文件大小134M。读测试:成功读出500万条记录,共耗时184秒,平均每秒读出数据27174笔。性能测试:CPU为Xeon2.80GHz*4写测试:写入整体数据结构redisServerredisDB*db...01...nredisDBintiddict*dict...redisDBintiddict*dict...redisDBintiddict*dict...结构体redisServer对应服务器,字段db指向一个指针数组,数组元素值即各个数据库的入口地址。结构体redisDB对应数据库,用以保存数据库id,字典等信息。整体数据结构redisServerredisDB*db..redisDBredisDBintiddict*dict...dictdictType*typedictht*ht[0]dictht*ht[1]...dicthtdictEntry**tableusignedlongsizeusignedlongused...dictTypeint(*hashFunc)(void*key)...dicthtdictEntry**tableusignedlongsizeusignedlongused...dict即字典,type是字典类型,ditcht是哈希表。函数指针hashFunc记录用以计算hash值的hash函数。redisDBredisDBintiddict*dictDictdictdictType*typedictht*ht[0]dictht*ht[1]...dicthtdictEntry**tableusignedlongsizeusignedlongused...01...size-1dictEntryvoid*keyvoid*val*nextNULLNULL双重指针table指向一个数组,该数组记录各个dictEntry(条目?)的入口地址。size为table的大小,used是dictEntry的个数。每个dictEntry对应一个存储对象,通过key和val指针可找到对应对象的key和value。used就是存储的对象总数。DictdictdictType*typedictht*key和value的值存在哪?dictEntryvoid*keyvoid*val*nextredisObjectunsignedtype:4void*ptr...redisObjectunsignedtype:4void*ptr...keyvalueredisObject可以看成是对要存储的各种对象(如String、List、Set等)的抽象,type(长4bit的整数)用于标识该对象所属的数据结构的种类,ptr指向对象在内存中的地址。key和value的值存在哪?dictEntryvoid*一次查询流程由key找到碰撞链入口的时间性能是O(1)。在碰撞链中遍历查询的时间性能是O(n)。一次查询流程由key找到碰撞链入口的时间性能是O(1)。Redis并不是简单的key-value存储,它实际上是一个数据结构服务器。不仅可以用数据库预定义的数据结构存储的value,还支持这些数据结构的基本操作。

预定义的数据结构有:StringListofStringSetofStringSortedSetofStringHashTableofString基本数据结构Redis并不是简单的key-value存储,它实际上是一个StringString是最简单的类型,值可以是任何种类的字符串(包括二进制数据)。String的长度是int类型,最大长度为1GB每个对象的key都以String存储若数据库中所有value都为String,则Redis就像一个可持久化的memcachedstructsdshdrintlenintfreecharbuf[]redisObjecttype=stringvoid*ptr...StringstructsdshdrintlenintListofStringListlistNode*headlistNode*tailunsignedintlen...listNodeprev=nulllistNode*nextvoid*valuelistNodelistNode*prevlistNode*nextvoid*valueList基于双向链表实现。在头部或尾部添(删)一个结点,时间复杂度为常数级别用List支持的一些操作,如lpush(头添加)、lpop(头删除),rpush、lpop可以很容易地实现栈和队列redis向外部提供的list中listNode保存的value是String类型的listNodelistNode*prevNext=nullvoid*valueListofStringListlistNode*heHashTableofStringdictEntryvoid*keyvoid*val*nextredisObjecttype=stringvoid*ptr...redisObjecttype=hashvoid*ptr...keydictdictType*typedictht*ht[0]dictht*ht[1]...redis数据组织的整体框架就是hashtable,hashtableofstring只不过将hashtable特例化。它还是以key-value形式存储,对hashtableofstring中对象的一次成功查询要经过两次hash。HashTableofStringdictEntryvosetredis支持的集合有两种,无序集合set和有序集合zset。zset的数据结构比较复杂,尚未弄懂。暂时只知道它基于有序链表和hashtable实现(用hashtable组织元素,链表实现排序),set又是基于zset实现。zset中的元素有一个权重参数score,使得集合中的元素能够按score进行有序排列忽略zset的权重score就可以得到set的实现集合提供了求交集、并集、差集等操作setredis支持的集合有两种,无序集合set和有序集合z优化机制索引优化随着hashtable中key-value对的不断增多,碰撞也越来越多,碰撞链越来越长,势必会影响到查询效率。为了保持查询效率,必须调整hashtable的索引结构,使每个索引上的碰撞链长度不至于太长。内存优化redis是个内存数据库,同等业务量下redis占用的内存越小越好。为了占用更小的内存,必须对内存进行优化。优化机制索引优化索引优化rehash是在hashtable大小不能满足需求,造成过多碰撞后需要进行扩容时的操作。基本思想:新建一张索引表,新表的索引空间为原表的两倍遍历旧表中的所有dictEntry,调用hash函数计算得到每个dictEntry在新表中的索引,并添加到新表上。当所有dictEntry都添加到新表中,启用新表,丢弃旧表索引通过hashFunc(key)%size算得,因为新表索引空间是原来的两倍,在新表中发生碰撞的概率将会小于原表。这样原本在一个碰撞链上的多个dictEntry就可能分布到不同的新索引上,新表碰撞链的平均长度(dictEntry个数/size)理论是旧表的一半索引优化rehash是在hashtable大小不能满足需求,rehashdictdictType*typedictht*ht[0]dictht*ht[1]...dicthtdictEntry**tablesize0usignedlongused...dicthtdictEntry**tablesize=2*size0usignedlongused...01...size0-1dictEntryNULLNULLdictEntry01...size-1ht[1]在平时为NULL,只有当rehash时用于指向新建的table。当在rehash时如果有新的key-value要添加到数据库,添加到新表ht[1]中。hashFunc(key)%sizerehashdictdictType*typedicthtrehashdictdictType*typedictht*ht[0]dictht*ht[1]...dicthtdictEntry**tablesize=2*size0usignedlongused...01...size-1dictEntryNULLNULLdictEntryNULL当旧表ht[0]中的所有dictEntry都rehash到新表后,让ht指向新表,ht[1]指向空,释放旧表的内存。rehash的结果是索引空间增至原来的2倍,碰撞链的平均长度used/size减小。rehashdictdictType*typedicthtrehashrehash是在hashtable需要扩容时进行,什么时候需要扩容?每添加一个dictEntry到hashtable就要判断是否要进行扩容redis默认当dictEntry的个数大于等于表容量size时进行扩容,即平均碰撞链长used/size刚超过1时就扩容,默认扩容到原来的两倍默认的扩容条件可使平均碰撞链长保持在1以下,虽然保证了查询效率,但会频繁rehash,内存消耗大。可通过修改扩容条件减少rehash频率,但要改扩容条件,只有修改源代码rehashrehash是在hashtable需要扩容时进行内存优化redis内存优化的策略有很多,比较常用的有两种,大体思想如下:通过特殊的编码方式存储对象,以节省内存。如把数字字符串以整数方式存储,例如用整数存储数字符串“123”只需要1个字,对于数值较大的数字字符串,分割后用数组存储当满足一定条件时,减小hashtable的容量,再rehash。默认在当used/size比不足10%时,将size改为used,再rehash得到容量是used两倍的新表,新表的容量将小于原表的1/5内存优化redis内存优化的策略有很多,比较常用的有两种,大快照(SnapShot)当前数据的快照存成一个数据文件的持久化机制频率控制:过程:Redis通过fork产生子进程父进程继续处理client请求,子进程负责将快照写入临时文件子进程写完后,用临时文件替换原来的快照文件,然后子进程退出持久化-快照

save900

1

#900秒超过1个key被修改save300

10

#300秒超过10个key被修改save60

10000

#60秒超过一万条被修改快照(SnapShot)持久化-快照

save900优点:-整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的

-对于灾难恢复,可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上

-

性能最大化-相比于AOF机制,如果数据集很大,RDB的启动效率会更高。

持久化-快照

优点:持久化-快照

会产生什么问题呢?每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。另外由于快照方式是在一定间隔时间做一次的,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改如果应用不能丢失任何修改的话,可以采用aof持久化方式

持久化-快照

会产生什么问题呢?持久化-快照

持久化-日志AOF(appendonlyfile)

记录每次的读写操作的文件在Redis配置文件中有一个叫appendonly的选项,可以写yes或no.这个选项就是负责是否开启AOF日志的开关.日志文件通病,如果只增不减的话,那文件将会无限长大,执行bgrewriterof命令,先给当前的所有数据做一个快照.然后再在这个快照的基础上写接下来的日志,删除旧的日志.持久化-日志AOF(appendonlyfile)持久化-日志Bgrewriterof内部实现Redis通过fork一个子进程,遍历数据,写入新临时文件父进程继续处理client请求,子进程继续写临时文件父进程把新写入的AOF写在缓冲区子进程写完退出,父进程接收退出消息,将缓冲区AOF写入临时文件临时文件重命名成appendonly.aof,原来文件被覆盖,整个过程完成

持久化-日志Bgrewriterof内部实现持久化-日志频率控制(redis.conf):

写入磁盘时机命令:#appendonlyyes

//启用aof持久化方式

#appendfsyncalways

//每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用

appendfsynceverysec

//每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐

#appendfsyncno

//完全依赖os,性能最好,持久化没保证save900

1

#900秒超过1个key被修改save300

10

#300秒超过10个key被修改save60

10000

#60秒超过一万条被修改持久化-日志频率控制(redis.conf):save持久化分析快照易丢失数据,AOF模式较为安全,但日志重写时磁盘io开销大,容易导致服务器性能严重下降,并且需要足够的物理内存,这个比较危险,所以在高性能服务器上一般是用主从复制来进行持久化。持久化分析快照易丢失数据,AOF模式较为安全,但日志重写时磁主从架构slaveclientclientclientmastermastermasterslaveslaveslaveCONSISTENTHASH/定容readwrite同步数据SESSION主从架构slaveclientclientclientmas主从复制配置:只需要在配置文件中加入如下配置

slaveof192.168.1.16379

#指定master的ip和端口.存在问题:Slave从库在连接Master主库时,Master会进行内存快照,然后把整个快照文件发给Slave,也就是没有象MySQL那样有复制位置的概念,即无增量复制,这会给整个集群搭建带来非常多的问题。主从复制配置:主从复制主从复制集群假如业务增长很快,很快就会发现当前的容量已经不够了,Redis里面存储的数据很快就会超过物理内存大小,那么如何进行Redis的在线扩容呢?方案一:下一版本的redis自身支持在线扩容redis实例,这一功能正在开发中。方案二:客户端做presharding方案集群假如业务增长很快,很快就会发现当前的容量已经不够了,Re客户端sharding方案Node(redis实例)=hash(key)modN;对客户端需要set或get的每个key,做hash运算再取模,映射到N个实例中的一个,可以搭建多实例集群。问题当系统规模增大或缩小时,需要动态增加或减少redis实例。对于增加实例情况后,N值将会变大,存储在原来实例上的所有key将要重做hash与取模运算(rehash过程),得到自己存储在新的redis实例位置。重做rehash,迁移大量的key工作量非常大,管理复杂。客户端sharding方案Node(redis实例)=hasPresharding方案前提:Redis非常轻量,一个Redis实例占用的内存非常小(1M左右),所以在一台服务器上部署多个实例(32、64、128,1000...)完全没有问题。思路:假设有N台主机,每台主机上部署M个实例,整个系统有T=NxM个实例,扩容前后实例总数不变。Presharding方案前提:Redis非常轻量,一个RePresharding方案前期多个实例部署在一台机器上,后期时每个实例独占一台物理机器内存,通过单机redis实例的内存增加,达到集群整体内存的大幅度上升。由于hash取模算法里的N值前后不变,避免rehash过程redis实例上的key重排。前期业务量小时,可配置少量廉价性能低机器满足业务,后期业务量大,配置很多台高性能高内存机器适应业务增长。Presharding方案前期多个实例部署在一台机器上,后期如何在线迁移扩容1.在新机器上启动好对应端口的Redis实例。2.配置新端口为待迁移端口的从库

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论