Redis积累

3 篇文章 0 订阅
1 篇文章 0 订阅

一、Redis简介

Redis是一个开源的内存中的数据结构存储系统,它可以用作:数据库、缓存和消息中间件。

书籍:《redis设计与实现》

1.1 Redis数据类型:

 

  • 字符串(String)
  • 散列(Hash)
  • 列表(List)
  • 集合(Set)
  • 有序集合(SortedSet或ZSet)
  • Bitmaps
  • Hyperloglogs
  • 地理空间(Geospatial)

 

它支持多种类型的数据结构,如字符串(String),散列(Hash),列表(List),集合(Set),有序集合(Sorted Set或者是ZSet)与范围查询,Bitmaps,Hyperloglogs 和地理空间(Geospatial)索引半径查询。其中常见的数据结构类型有:String、List、Set、Hash、ZSet这5种。

 

1.2 Redis的命令

String

String类型是redis中最简单数据类型,String类型实质是一个String映射到另一个String,同时它也是复杂类型的基类型。String类型的value不得超过512M,可以被使用在很多场合。如果String类型的值是数字类型,则可以使用加法和减法操作,在使用算术运算的时候,其实redis将其String类型解析成Integer类型进行运算的。

 

  • SET mystring “mystring0”     设置值
  • GET mystring                          获取key的值
  • EXISTS mystring                     判断这个key是否存在
  • DEL mystring                           删除key
  • EXPIRE mystring 10000         设置存活时间
  • TTL mystring                           查询还有多久的存活时间
  • SET mystring12 100               设置一个数字
  • INCR mystring12                     自增1
  • DECR mystring12                    自减1
  • INCRBY mystring12 35           自增35
List
Lists类型一般实现有常见的两种:
1.基于Arrays线性数组实现的List数据结构
2.基于Linked List链式节点实现的List数据结构
在每种应用中的实现都是差不多遵循这样的思想,如:java的ArrayList和LinkedList、c语言数据结构的线性链表和链式链表;同样redis中也遵循这样的思想。但是redis中的Lists则采用的是第二种算法。LinkedList的实现决定了它有高性能的插入删除操作,而Arrays型List性能在于查询获取。
因为相对于缓存或者数据库系统而言,在插入或者更新的操作上性能更重要,相对于查询操作,查询所带来的性能损耗更低。平衡性能的确采用Linked List实现更具有优越性。
redis中复合类型的基元素都是String类型。Lists也不例外。Lists类型实质就是一串String元素的序列,redis提供了很多丰富命令,用于操作Lists类型。
1.LPUSH、RPUSH 和 LPOP、RPOP 以及 LRANGE
LPUSH从List左边插入元素,RPUSH从List右边插入元素。LPOP从List左边弹出元素并从List中移除该元素,RPOP从List右边弹出元素并移除。这种操作元素的特点很类似双向队列的实现。所以可以用List类型来作为队列使用,使其用于线程交互。LRANGE是获取List中的范围元素。
  • LPUSH mylist0 name age address phoneNo      从list的左边插入四个元素
  • LRANGE mylist0 0 -1                                     获取list中某些元素(-1表示最后一位)
  • RPOP mylist1                                                从右边移除一个元素
List的PUSH和POP特点决定其可以被用作为队列使用
可以用redis的List实现Product/Consumer模式(生产者/消费者模式),即实现一个mq

Redis在List的基础上还提供了Blocking List(阻塞式List),这种List非常类似于Java中的阻塞队列。采用信号量机制,和Java中的object的wait和notify、condition的信号量实现非常相似。
Redis实现命令BRPOP、BLPOP达到阻塞式List,如果List为空,则不返回客户端陷入等待直达有新的元素加入List才返回或者到达指定的超时时间。
  • BRPOP mylist 10000  如果没有元素,则客户端阻塞等待,10000表示超时时间
Set
Set类型是无序且无重复的String元素的集合。redis也提供了大量对于Sets类型的操作,比如:交集、并集、补集在多个Set之间;测试给定元素是否存在等。
  • SADD myset 0 0 1 2 6 6 6         向set中插入元素,相同则覆盖
  • SISMEMBER myset 0                判断元素是否在set中
  • SMEMBERS myset                   获取set中所有元素
  • SINTER myset myset1 myset2    取多个set的交集
Sorted Set
Sorted Set类型是有序的Set类型,有点像Hashes和Set的混合型数据结构。具有Set的唯一无重复特性,且有序的实现是通过额外的浮点值被称作为score决定,与Hashes的Key有点相似。
Sorted Set的排序规则,假设有两个元素a、b
1.如果a.score > b.score,则a > b;
2.如果a.score = b.score,a > b,则a > b;
  • ZADD mysortset 1 “BACD”            向sortset中添加元素,1表示元素的score
  • ZADD mysortset 2 "BCDE”            
  • ZRANGE mysortset 0 -1  zrange获取Sorted Set中的元素,ZREVRANGE获取Sorted Set中元素并方向输出。
  • ZRANGEBYSCORE mysortset -inf 4 withscores  zrangbyscore根据score获取输出元素, -inf参数表明获取小于指定score的元素
Hash
redis中的Hashes类型类似Java中map,key-value键值对映射型数据结构。redis提供了很多命令用于操作Hashes。
  • HMSET usr:1000 username lxy birth 1991 verified 1          插入元素
  • HMGET usr:1000 username birth verified                        获取元素,可以获取多个key
  • HGET usr:1000 username                                             hget只能获取一个
核心:dict
字典结构是整个Redis的核心数据结构,基本上是其内部结构的缩影。
typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;
} dictEntry;
dictEntry是最核心的字典结构的节点结构,它保存了key和value的内容;另外,next指针是为了解决hash冲突,字典结构的hash冲突解决方法是拉链法,对于hashcode重复的节点以链表的形式存储。
typedef struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask; /*hash表的掩码,总是size-1,用于计算hash表的索引值*/
unsigned long used;
} dictht;
dictht是节点dictEntry的持有者,将dictEntry结构串起来,table就是hash表,其实dictEntry *table[]这样的书写方式更容易理解些,size就是table数组的长度,used标志已有节点的数目。
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
int iterators; /* number of iterators currently running */
} dict;
dict是最外层的字典结构的接口形式,type标志类型,privdata标志其私有数据,dict持有两个dictht结构,一个用来存储数据,一个用来在rehash时使用,rehashidx标志是否正在rehash(因为Redis中rehash是一个渐近的过程,正在rehash的时候rehashidx记录rehash的阶段,否则为-1)。
注:rehash是一个为了让负载因子(load_factor=used/size)控制在一个合理的范围内而重新分配内存和扩展结构的过程。
iterators是一个迭代器,用于记录当前迭代的数目。

 

二、Redis的功能

  • 复制(Replication)
  • LUA脚本(Lua scripting)
  • LRU驱动事件(LRU eviction)
  • 事务(Transactions)
  • 磁盘持久化(Persistence)
  • Redis哨兵(Sentinel)
  • 自动分区(Cluster)

Redis 内置了复制(Replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(Transactions) 和不同级别的磁盘持久化(Persistence),并通过 Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(High Availability)。

2.1 复制 

A Redis可以配置slaveof <B Redis host> <B Redis port>来复制B Redis,A是从节点,B是主节点 

  • 1.从Redis向主Redis发送SYNC命令
  • 2.主Redis收到SYNC命令后执行BGSAVE命令,即fork一个子进程生成RDB文件,在开始生成RDB文件之时并使用一个缓冲区记录客户端的写操作命令
  • 3.主Redis的BGSAVE命令执行完成,主Redis将生成好的RDB文件发送给从Redis,从Redis接收到RDB文件,将自己原有的数据(如果有的话)全部清除干净,将RDB文件加载到内存中,在RDB文件的加载期间,从Redis是阻塞无法处理客户端请求的
  • 4.主Redis将记录在缓冲区的写命令发送给从Redis,从Redis执行主Redis发过来的写操作命令,这样,从Redis的数据库状态就可以跟主Redis当前的数据库状态达成一致
  • 5.接着主、从Redis就使用命令传播操作来同步数据

2.2 LUA脚本 

Redis内置了对LUA脚本的支持,并且在计算过程中保证了脚本中执行的原子性。

对cas的支持

 

2.3 LRU驱动事件 

redis使用LRU机制删除过期时间但是还没过期的key

 

2.4 事务

Redis中的事务(transaction)是一组命令的集合。事务同命令一样都是Redis最小的执行单位,一个事务中的命令要么都执行,要么都不执行。Redis事务的实现需要用到 MULTI 和 EXEC 两个命令,事务开始的时候先向Redis服务器发送 MULTI 命令,然后依次发送需要在本次事务中处理的命令,最后再发送 EXEC 命令表示事务命令结束。

Redis中的事务并没有关系型数据库中的事务回滚(rollback)功能

WATCH命令

WATCH命令可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令(事务中的命令是在EXEC之后才执行的,EXEC命令执行完之后被监控的键会自动被UNWATCH)

PipeLine

redis支持通过PipeLine将一组Redis命令进行组装,通过一次RTT传输给Redis,再将这组Redis命令按照顺序执行并装填结果返回给客户端

 

2.5 磁盘持久化

快照方式和AOF方式

快照方式是在指定的时间间隔内生成数据集的时间点快照

AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。

 

2.6 Redis哨兵

由一个或多个Sentinel实例组成Sentinel系统可以监视一个或多个主Redis服务器,以及主Redis属下的从Redis,当被监视的主Redis进入下线状态时,Sentinel会将它的其中一个从Redis升级为主Redis,其他从Redis复制新的主Redis,当旧的主Redis重新上线时,会以从Redis的角色来复制新的主Redis。

 

2.7 分区Cluster

Redis Cluster本身提供了自动将数据分散到Redis Cluster不同节点的能力,分区实现的关键点问题包括:如何将数据自动地打散到不同的节点,使得不同节点的存储数据相对均匀。

Redis Cluster中有一个16384长度的槽的概念,他们的编号为0、1、2、3……16382、16383。这个槽是一个虚拟的槽,并不是真正存在的。正常工作的时候,Redis Cluster中的每个Master节点都会负责一部分的槽,当有某个key被映射到某个Master负责的槽,那么这个Master负责为这个key提供服务。

 

redis的发布订阅


redis实现mq可以用发布订阅和阻塞List实现

 

Redis也提供了持久化的选项,这些选项可以让用户将自己的数据保存到磁盘上面进行存储。根据实际情况,可以每隔一定时间将数据集导出到磁盘(快照),或者追加到命令日志中(AOF只追加文件),他会在执行写命令时,将被执行的写命令复制到硬盘里面。您也可以关闭持久化功能,将Redis作为一个高效的网络的缓存数据功能使用。

Redis不使用表,他的数据库不会预定义或者强制去要求用户对Redis存储的不同数据进行关联。

数据库的工作模式按存储方式可分为:硬盘数据库和内存数据库。Redis 将数据储存在内存里面,读写数据的时候都不会受到硬盘 I/O 速度的限制,所以速度极快。

Redis采用的是基于内存的采用的是单进程单线程模型的 KV 数据库,由C语言编写,官方提供的数据是可以达到100000+的QPS(每秒内查询次数)。

三、Redis为什么这么快

1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);

2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;

3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;

4、使用多路I/O复用模型,非阻塞IO;

5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

3.1 缓存设计

设计一个缓存系统,不得不要考虑的问题就是:缓存穿透、缓存击穿与失效时的雪崩效.  
1) 缓存击穿:对于一些设置了过期时间的key, 刚好过期的时候,这时候有个高并发的请求,会导致直接访问数据库,危险. 
2) 缓存穿透:查询一个一定不存在的数据,导致直接访问数据库。 解决方法:如果一个查询返回的数据为空,我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。 
3) 缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效。 随机设置过期时间。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值