Ⅰ redis jedispubsub怎么销毁
用一致性哈希算法(consistent hashing),将key和节点name同时hashing,然后进行映射匹配,采用的算法是MURMUR_HASH。
采用一致性哈希而不是采用简单类似哈希求模映射的主要原因是当增加或减少节点时,不会产生由于重新匹配造成的rehashing。一致性哈希只影响相邻节点key分配,影响量小。
Ⅱ 如何使用redis实现订阅发布模式
从上面的官方解释上来看,它的玩法有一点像现实生活中我们听收音机一个道理,要想听收音机,我们要做什么?肯定就是调频啦,只有在正
确的频道上面,我们才能听得到好听的节目,所以说subscribe首先要订阅一个频道(channel),下面我举个例子,开两个client,分别订阅着
msg 这个频道,比如下面这样:
2.publish
到现在为止,这两个subscibe都在监视着msg这个频道,接下来,如果msg频道有消息传出,必定会被subscribe接收到,先我们还是看看
redis手册上怎么用这个命令。
PUBLISH channel message
将信息 message 发送到指定的频道 channel 。
看到上面命令的用法,我也就放心了。
看到么有,publish在msg这个频道上面发送消息后,被subscribe监视到了,然后就被分别打印输出了,好了,到现在为止,最基本的发布
订阅模式就是这样,是不是很简单哈。。。其实呢??? 也就是这么简单呐,但是呢,有时候我们还有这样一个需求,就是我能不能模糊匹
配key呢???举了例子,就是要求订阅china为前缀的所有频道,如果这样也可以做到的话,那确实是很牛逼啦。。。我要是回答的话,当
然啦,强大的redis自然会做到这一点,它提供了的命令就是:Psubscribe。
3. Psubscribe
PSUBSCRIBE pattern [pattern ...]
订阅一个或多个符合给定模式的频道。
每个模式以 * 作为匹配符,比如 it* 匹配所有以 it 开头的频道( it.news 、 it.blog 、 it.tweets 等等), news.* 匹配所有以 news. 开头的频道( news.it 、 news.global.today 等等),诸如此类。
看到上面的解释,你心里可能就在想,这不就是正则匹配么。。。而且前缀“P”就是Pattern的意思,对吧,接下来我就订阅一下所有china为
前缀的channel。
好了,最常用的也就是这三个命令,接下来我们简单分析一下代码。
二: 源码简单分析
其实redis的发布订阅模式,使用RedisServer下面的 pubsub_channels字典 和 pubsub_patterns数组存放的,所有的操作代码都
在pubsub.c文件下,如下图:
1. pubsub_channels
可以看到,它是一个字典结构,通过注释你应该明白,它的key为channel,value为list。
2. pubsub_patterns
同样从注释中,你可以看到,其实它就是存放模式匹配的subscribe的clients列表,对吧,用一个list数组实现。
3. subcribeCommand
通过下面的代码,你是不是在脑子里面很有轮廓了???其实这个pubsub_channels果然就是key=channel,value=list的存放模式,
这个list就是所谓的clients列表,这样的话,你就知道了哪些key挂了哪些clients,对吧,如果再publish的话,只需要遍历一下这个list就知
道结果了。
4. publishCommand
先前也说了,publish的原理很简单,就是找到字典中的channel这个key,获取到clients之后,遍历client的来发送信息。
同样的道理,pubsub_patterns也是差不多的实现,只要大家简单看一下pubsub.c这个源代码文件,差不多都会懂得,没啥好说的,
希望这篇对你有用~
Ⅲ 如何查看redis中消息
redis-cli连接服务器后,使用info命令查看Redis信息和状态:
redis>INFO
#Server
redis_version:2.9.11
redis_git_sha1:937384d0
redis_git_dirty:0
redis_build_id:8e9509442863f22
redis_mode:standalone
os:Linux3.13.0-35-genericx86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.8.2
process_id:4716
run_id:
tcp_port:6379
uptime_in_seconds:362
uptime_in_days:0
hz:10
lru_clock:1725349
config_file:
#Clients
connected_clients:1
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0
#Memory
used_memory:508536
used_memory_human:496.62K
used_memory_rss:7974912
used_memory_peak:508536
used_memory_peak_human:496.62K
used_memory_lua:33792
mem_fragmentation_ratio:15.68
mem_allocator:jemalloc-3.2.0
#Persistence
loading:0
rdb_changes_since_last_save:6
rdb_bgsave_in_progress:0
rdb_last_save_time:1411011131
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
#Stats
total_connections_received:2
total_commands_processed:4
instantaneous_ops_per_sec:0
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:0
migrate_cached_sockets:0
#Replication
role:master
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
#CPU
used_cpu_sys:0.21
used_cpu_user:0.17
used_cpu_sys_children:0.00
used_cpu_user_children:0.00
#Cluster
cluster_enabled:0
#Keyspace
db0:keys=2,expires=0,avg_ttl=0
Ⅳ redis常用命令是什么
1、连接操作相关命令:
type(key):返值类型。
使用的注意事项
1、 Redis和Memcache都是将数据存放在内存中,都是内存数据库。不过memcache还可用于缓存其他东西,例如图片、视频等等。
2、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储。
3、虚拟内存–Redis当物理内存用完时,可以将一些很久没用到的value 交换到磁盘 。
4、过期策略–memcache在set时就指定,例如set key1 0 0 8,即永不过期。Redis可以通过例如expire 设定,例如expire name 10 。
Ⅳ redis 集群支持pubsub吗
Redis本质上也是一种键值数据库的,但它在保持键值数据库简单快捷特点的同时,又吸收了部分关系数据库的优点。从而使它的位置处于关系数据库和键值数 据库之间。Redis不仅能保存Strings类型的数据,还能保存Lists类型(有序)和Sets类型(无序)的数据,而且还能完成排序(SORT) 等高级功能,在实现INCR,SETNX等功能的时候,保证了其操作的原子性,除此以外,还支持主从复制等功能。
Redis支持 publish/subscribe, 通知, key 过期等等特性。
Ⅵ Redis和Memcached的区别
Redis的作者Salvatore Sanfilippo曾经对这两种基于内存的数据存储系统进行过比较:
1、Redis支持服务器端的数据操作:Redis相比Memcached来说,拥有更多的数据结构和并支持更丰富的数据操作,通常在Memcached里,你需要将数据拿到客户端来进行类似的修改再set回去。这大大增加了网络IO的次数和数据体积。在Redis中,这些复杂的操作通常和一般的GET/SET一样高效。所以,如果需要缓存能够支持更复杂的结构和操作,那么Redis会是不错的选择。
2、内存使用效率对比:使用简单的key-value存储的话,Memcached的内存利用率更高,而如果Redis采用hash结构来做key-value存储,由于其组合式的压缩,其内存利用率会高于Memcached。
3、性能对比:由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比Memcached性能更高。而在100k以上的数据中,Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上进行优化,但是比起Memcached,还是稍有逊色。
具体为什么会出现上面的结论,以下为收集到的资料:
1、数据类型支持不同
与Memcached仅支持简单的key-value结构的数据记录不同,Redis支持的数据类型要丰富得多。最为常用的数据类型主要由五种:String、Hash、List、Set和Sorted Set。Redis内部使用一个redisObject对象来表示所有的key和value。redisObject最主要的信息如图所示:
type代表一个value对象具体是何种数据类型,encoding是不同数据类型在redis内部的存储方式,比如:type=string代表value存储的是一个普通字符串,那么对应的encoding可以是raw或者是int,如果是int则代表实际redis内部是按数值型类存储和表示这个字符串的,当然前提是这个字符串本身可以用数值表示,比如:”123″ “456”这样的字符串。只有打开了Redis的虚拟内存功能,vm字段字段才会真正的分配内存,该功能默认是关闭状态的。
1)String
常用命令:set/get/decr/incr/mget等;
应用场景:String是最常用的一种数据类型,普通的key/value存储都可以归为此类;
实现方式:String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr、decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。
2)Hash
常用命令:hget/hset/hgetall等
应用场景:我们要存储一个用户信息对象数据,其中包括用户ID、用户姓名、年龄和生日,通过用户ID我们希望获取该用户的姓名或者年龄或者生日;
实现方式:Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口。如图所示,Key是用户ID, value是一个Map。这个Map的key是成员的属性名,value是属性值。这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field), 也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据。当前HashMap的实现有两种方式:当HashMap的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,这时对应的value的redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。
3)List
常用命令:lpush/rpush/lpop/rpop/lrange等;
应用场景:Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现;
实现方式:Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。
4)Set
常用命令:sadd/spop/smembers/sunion等;
应用场景:Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的;
实现方式:set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。
5)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,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。
2、内存管理机制不同
在Redis中,并不是所有的数据都一直存储在内存中的。这是和Memcached相比一个最大的区别。当物理内存用完时,Redis可以将一些很久没用到的value交换到磁盘。Redis只会缓存所有的key的信息,如果Redis发现内存的使用量超过了某一个阀值,将触发swap的操作,Redis根据“swappability = age*log(size_in_memory)”计算出哪些key对应的value需要swap到磁盘。然后再将这些key对应的value持久化到磁盘中,同时在内存中清除。这种特性使得Redis可以保持超过其机器本身内存大小的数据。当然,机器本身的内存必须要能够保持所有的key,毕竟这些数据是不会进行swap操作的。同时由于Redis将内存中的数据swap到磁盘中的时候,提供服务的主线程和进行swap操作的子线程会共享这部分内存,所以如果更新需要swap的数据,Redis将阻塞这个操作,直到子线程完成swap操作后才可以进行修改。当从Redis中读取数据的时候,如果读取的key对应的value不在内存中,那么Redis就需要从swap文件中加载相应数据,然后再返回给请求方。 这里就存在一个I/O线程池的问题。在默认的情况下,Redis会出现阻塞,即完成所有的swap文件加载后才会相应。这种策略在客户端的数量较小,进行批量操作的时候比较合适。但是如果将Redis应用在一个大型的网站应用程序中,这显然是无法满足大并发的情况的。所以Redis运行我们设置I/O线程池的大小,对需要从swap文件中加载相应数据的读取请求进行并发操作,减少阻塞的时间。
对于像Redis和Memcached这种基于内存的数据库系统来说,内存管理的效率高低是影响系统性能的关键因素。传统C语言中的malloc/free函数是最常用的分配和释放内存的方法,但是这种方法存在着很大的缺陷:首先,对于开发人员来说不匹配的malloc和free容易造成内存泄露;其次频繁调用会造成大量内存碎片无法回收重新利用,降低内存利用率;最后作为系统调用,其系统开销远远大于一般函数调用。所以,为了提高内存的管理效率,高效的内存管理方案都不会直接使用malloc/free调用。Redis和Memcached均使用了自身设计的内存管理机制,但是实现方法存在很大的差异,下面将会对两者的内存管理机制分别进行介绍。
Memcached默认使用Slab Allocation机制管理内存,其主要思想是按照预先规定的大小,将分配的内存分割成特定长度的块以存储相应长度的key-value数据记录,以完全解决内存碎片问题。Slab Allocation机制只为存储外部数据而设计,也就是说所有的key-value数据都存储在Slab Allocation系统里,而Memcached的其它内存请求则通过普通的malloc/free来申请,因为这些请求的数量和频率决定了它们不会对整个系统的性能造成影响Slab Allocation的原理相当简单。 如图所示,它首先从操作系统申请一大块内存,并将其分割成各种尺寸的块Chunk,并把尺寸相同的块分成组Slab Class。其中,Chunk就是用来存储key-value数据的最小单位。每个Slab Class的大小,可以在Memcached启动的时候通过制定Growth Factor来控制。假定图中Growth Factor的取值为1.25,如果第一组Chunk的大小为88个字节,第二组Chunk的大小就为112个字节,依此类推。
当Memcached接收到客户端发送过来的数据时首先会根据收到数据的大小选择一个最合适的Slab Class,然后通过查询Memcached保存着的该Slab Class内空闲Chunk的列表就可以找到一个可用于存储数据的Chunk。当一条数据库过期或者丢弃时,该记录所占用的Chunk就可以回收,重新添加到空闲列表中。从以上过程我们可以看出Memcached的内存管理制效率高,而且不会造成内存碎片,但是它最大的缺点就是会导致空间浪费。因为每个Chunk都分配了特定长度的内存空间,所以变长数据无法充分利用这些空间。如图 所示,将100个字节的数据缓存到128个字节的Chunk中,剩余的28个字节就浪费掉了。
Redis的内存管理主要通过源码中zmalloc.h和zmalloc.c两个文件来实现的。Redis为了方便内存的管理,在分配一块内存之后,会将这块内存的大小存入内存块的头部。如图所示,real_ptr是redis调用malloc后返回的指针。redis将内存块的大小size存入头部,size所占据的内存大小是已知的,为size_t类型的长度,然后返回ret_ptr。当需要释放内存的时候,ret_ptr被传给内存管理程序。通过ret_ptr,程序可以很容易的算出real_ptr的值,然后将real_ptr传给free释放内存。
Redis通过定义一个数组来记录所有的内存分配情况,这个数组的长度为ZMALLOC_MAX_ALLOC_STAT。数组的每一个元素代表当前程序所分配的内存块的个数,且内存块的大小为该元素的下标。在源码中,这个数组为zmalloc_allocations。zmalloc_allocations[16]代表已经分配的长度为16bytes的内存块的个数。zmalloc.c中有一个静态变量used_memory用来记录当前分配的内存总大小。所以,总的来看,Redis采用的是包装的mallc/free,相较于Memcached的内存管理方法来说,要简单很多。
3、数据持久化支持
Redis虽然是基于内存的存储系统,但是它本身是支持内存数据的持久化的,而且提供两种主要的持久化策略:RDB快照和AOF日志。而memcached是不支持数据持久化操作的。
1)RDB快照
Redis支持将当前数据的快照存成一个数据文件的持久化机制,即RDB快照。但是一个持续写入的数据库如何生成快照呢?Redis借助了fork命令的 on write机制。在生成快照时,将当前进程fork出一个子进程,然后在子进程中循环所有的数据,将数据写成为RDB文件。我们可以通过Redis的save指令来配置RDB快照生成的时机,比如配置10分钟就生成快照,也可以配置有1000次写入就生成快照,也可以多个规则一起实施。这些规则的定义就在Redis的配置文件中,你也可以通过Redis的CONFIG SET命令在Redis运行时设置规则,不需要重启Redis。
Redis的RDB文件不会坏掉,因为其写操作是在一个新进程中进行的,当生成一个新的RDB文件时,Redis生成的子进程会先将数据写到一个临时文件中,然后通过原子性rename系统调用将临时文件重命名为RDB文件,这样在任何时候出现故障,Redis的RDB文件都总是可用的。同时,Redis的RDB文件也是Redis主从同步内部实现中的一环。RDB有他的不足,就是一旦数据库出现问题,那么我们的RDB文件中保存的数据并不是全新的,从上次RDB文件生成到Redis停机这段时间的数据全部丢掉了。在某些业务下,这是可以忍受的。
2)AOF日志
AOF日志的全称是append only file,它是一个追加写入的日志文件。与一般数据库的binlog不同的是,AOF文件是可识别的纯文本,它的内容就是一个个的Redis标准命令。只有那些会导致数据发生修改的命令才会追加到AOF文件。每一条修改数据的命令都生成一条日志,AOF文件会越来越大,所以Redis又提供了一个功能,叫做AOF rewrite。其功能就是重新生成一份AOF文件,新的AOF文件中一条记录的操作只会有一次,而不像一份老文件那样,可能记录了对同一个值的多次操作。其生成过程和RDB类似,也是fork一个进程,直接遍历数据,写入新的AOF临时文件。在写入新文件的过程中,所有的写操作日志还是会写到原来老的AOF文件中,同时还会记录在内存缓冲区中。当重完操作完成后,会将所有缓冲区中的日志一次性写入到临时文件中。然后调用原子性的rename命令用新的AOF文件取代老的AOF文件。
AOF是一个写文件操作,其目的是将操作日志写到磁盘上,所以它也同样会遇到我们上面说的写操作的流程。在Redis中对AOF调用write写入后,通过appendfsync选项来控制调用fsync将其写到磁盘上的时间,下面appendfsync的三个设置项,安全强度逐渐变强。
appendfsync no 当设置appendfsync为no的时候,Redis不会主动调用fsync去将AOF日志内容同步到磁盘,所以这一切就完全依赖于操作系统的调试了。对大多数Linux操作系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上。
appendfsync everysec 当设置appendfsync为everysec的时候,Redis会默认每隔一秒进行一次fsync调用,将缓冲区中的数据写到磁盘。但是当这一次的fsync调用时长超过1秒时。Redis会采取延迟fsync的策略,再等一秒钟。也就是在两秒后再进行fsync,这一次的fsync就不管会执行多长时间都会进行。这时候由于在fsync时文件描述符会被阻塞,所以当前的写操作就会阻塞。所以结论就是,在绝大多数情况下,Redis会每隔一秒进行一次fsync。在最坏的情况下,两秒钟会进行一次fsync操作。这一操作在大多数数据库系统中被称为group commit,就是组合多次写操作的数据,一次性将日志写到磁盘。
appednfsync always 当设置appendfsync为always时,每一次写操作都会调用一次fsync,这时数据是最安全的,当然,由于每次都会执行fsync,所以其性能也会受到影响。
对于一般性的业务需求,建议使用RDB的方式进行持久化,原因是RDB的开销并相比AOF日志要低很多,对于那些无法忍数据丢失的应用,建议使用AOF日志。
4、集群管理的不同
Memcached是全内存的数据缓冲系统,Redis虽然支持数据的持久化,但是全内存毕竟才是其高性能的本质。作为基于内存的存储系统来说,机器物理内存的大小就是系统能够容纳的最大数据量。如果需要处理的数据量超过了单台机器的物理内存大小,就需要构建分布式集群来扩展存储能力。
Memcached本身并不支持分布式,因此只能在客户端通过像一致性哈希这样的分布式算法来实现Memcached的分布式存储。下图给出了Memcached的分布式存储实现架构。当客户端向Memcached集群发送数据之前,首先会通过内置的分布式算法计算出该条数据的目标节点,然后数据会直接发送到该节点上存储。但客户端查询数据时,同样要计算出查询数据所在的节点,然后直接向该节点发送查询请求以获取数据。
相较于Memcached只能采用客户端实现分布式存储,Redis更偏向于在服务器端构建分布式存储。最新版本的Redis已经支持了分布式存储功能。Redis Cluster是一个实现了分布式且允许单点故障的Redis高级版本,它没有中心节点,具有线性可伸缩的功能。下图给出Redis Cluster的分布式存储架构,其中节点与节点之间通过二进制协议进行通信,节点与客户端之间通过ascii协议进行通信。在数据的放置策略上,Redis Cluster将整个key的数值域分成4096个哈希槽,每个节点上可以存储一个或多个哈希槽,也就是说当前Redis Cluster支持的最大节点数就是4096。Redis Cluster使用的分布式算法也很简单:crc16( key ) % HASH_SLOTS_NUMBER。
为了保证单点故障下的数据可用性,Redis Cluster引入了Master节点和Slave节点。在Redis Cluster中,每个Master节点都会有对应的两个用于冗余的Slave节点。这样在整个集群中,任意两个节点的宕机都不会导致数据的不可用。当Master节点退出后,集群会自动选择一个Slave节点成为新的Master节点。
Ⅶ redis的subscribe怎么用
发布订阅(pub/sub)是一种消息通信模式,主要的目的是解耦消息发布者和消息订阅者之间的耦合,这点和设计模式中的观察者模式比较相似。pub /sub不仅仅解决发布者和订阅者直接代码级别耦合也解决两者在物理部署上的耦合。redis作为一个pub/sub server,在订阅者和发布者之间起到了消息路由的功能。订阅者可以通过subscribe和psubscribe命令向redis server订阅自己感兴趣的消息类型,redis将消息类型称为通道(channel)。当发布者通过publish命令向redis server发送特定类型的消息时。订阅该消息类型的全部client都会收到此消息。这里消息的传递是多对多的。一个client可以订阅多个 channel,也可以向多个channel发送消息。
参考下这个吧:http://www.oschina.net/question/123890_56301
Ⅷ 如何高效深入的阅读Redis的源码
在这篇文章中, 我将向大家介绍一种我认为比较合理的 Redis 源码阅读顺序, 希望可以给对 Redis 有兴趣并打算阅读 Redis 源码的朋友带来一点帮助。
第 1 步:阅读数据结构实现
刚开始阅读 Redis 源码的时候, 最好从数据结构的相关文件开始读起, 因为这些文件和 Redis 中的其他部分耦合最少, 并且这些文件所实现的数据结构在大部分算法书上都可以了解到, 所以从这些文件开始读是最轻松的、难度也是最低的。
下表列出了 Redis 源码中, 各个数据结构的实现文件:
文件 内容
sds.h 和 sds.c Redis 的动态字符串实现。
adlist.h 和 adlist.c Redis 的双端链表实现。
dict.h 和 dict.c Redis 的字典实现。
redis.h 中的 zskiplist 结构和 zskiplistNode 结构, 以及 t_zset.c 中所有以 zsl 开头的函数, 比如 zslCreate 、 zslInsert 、 zslDeleteNode ,等等。 Redis 的跳跃表实现。
hyperloglog.c 中的 hllhdr 结构, 以及所有以 hll 开头的函数。 Redis 的 HyperLogLog 实现。
第 2 步:阅读内存编码数据结构实现
在阅读完和数据结构有关的文件之后, 接下来就应该阅读内存编码(encoding)数据结构了。
和普通的数据结构一样, 内存编码数据结构基本上是独立的, 不和其他模块耦合, 但是区别在于:
上一步要读的数据结构, 比如双端链表、字典、HyperLogLog, 在算法书上或者相关的论文上都可以找到资料介绍。
而内存编码数据结构却不容易找到相关的资料, 因为这些数据结构都是 Redis 为了节约内存而专门开发出来的, 换句话说, 这些数据结构都是特制(adhoc)的, 除了 Redis 源码中的文档之外, 基本上找不到其他资料来了解这些特制的数据结构。
不过话又说回来, 虽然内存编码数据结构是 Redis 特制的, 但它们基本都和内存分配、指针操作、位操作这些底层的东西有关, 读者只要认真阅读源码中的文档, 并在有需要时, 画图来分析这些数据结构, 那么要完全理解这些内存编码数据结构的运作原理并不难, 当然这需要花一些功夫。
下表展示了 Redis 源码中, 各个内存编码数据结构的实现文件:
文件 内容
intset.h 和 intset.c 整数集合(intset)数据结构。
ziplist.h 和 ziplist.c 压缩列表(zip list)数据结构。
第 3 步:阅读数据类型实现
在完成以上两个阅读步骤之后, 我们就读完了 Redis 六种不同类型的键(字符串、散列、列表、集合、有序集合、HyperLogLog)的所有底层实现结构了。
接下来, 为了知道 Redis 是如何通过以上提到的数据结构来实现不同类型的键, 我们需要阅读实现各个数据类型的文件, 以及 Redis 的对象系统文件, 这些文件包括:
文件 内容
object.c Redis 的对象(类型)系统实现。
t_string.c 字符串键的实现。
t_list.c 列表键的实现。
t_hash.c 散列键的实现。
t_set.c 集合键的实现。
t_zset.c 中除 zsl 开头的函数之外的所有函数。 有序集合键的实现。
hyperloglog.c 中所有以 pf 开头的函数。 HyperLogLog 键的实现。
第 4 步:阅读数据库实现相关代码
在读完了 Redis 使用所有底层数据结构, 以及 Redis 是如何使用这些数据结构来实现不同类型的键之后, 我们就可以开始阅读 Redis 里面和数据库有关的代码了, 它们分别是:
文件 内容
redis.h 文件中的 redisDb 结构, 以及 db.c 文件。 Redis 的数据库实现。
notify.c Redis 的数据库通知功能实现代码。
rdb.h 和 rdb.c Redis 的 RDB 持久化实现代码。
aof.c Redis 的 AOF 持久化实现代码。
选读
Redis 有一些独立的功能模块, 这些模块可以在完成第 4 步之后阅读, 它们包括:
文件 内容
redis.h 文件的 pubsubPattern 结构,以及 pubsub.c 文件。 发布与订阅功能的实现。
redis.h 文件的 multiState 结构以及 multiCmd 结构, multi.c 文件。 事务功能的实现。
sort.c SORT 命令的实现。
bitops.c GETBIT 、 SETBIT 等二进制位操作命令的实现。
第 5 步:阅读客户端和服务器的相关代码
在阅读完数据库实现代码, 以及 RDB 和 AOF 两种持久化的代码之后, 我们可以开始阅读客户端和 Redis 服务器本身的实现代码, 和这些代码有关的文件是:
文件 内容
ae.c ,以及任意一个 ae_*.c 文件(取决于你所使用的多路复用库)。 Redis 的事件处理器实现(基于 Reactor 模式)。
networking.c Redis 的网络连接库,负责发送命令回复和接受命令请求, 同时也负责创建/销毁客户端, 以及通信协议分析等工作。
redis.h 和 redis.c 中和单机 Redis 服务器有关的部分。 单机 Redis 服务器的实现。
如果读者能完成以上 5 个阅读步骤的话, 那么恭喜你, 你已经了解了单机的 Redis 服务器是怎样处理命令请求和返回命令回复, 以及是 Redis 怎样操作数据库的了, 这是 Redis 最重要的部分, 也是之后继续阅读多机功能的基础。
选读
Redis 有一些独立的功能模块, 这些模块可以在完成第 5 步之后阅读, 它们包括:
文件 内容
scripting.c Lua 脚本功能的实现。
slowlog.c 慢查询功能的实现。
monitor.c 监视器功能的实现。
第 6 步:阅读多机功能的实现
在弄懂了 Redis 的单机服务器是怎样运作的之后, 就可以开始阅读 Redis 多机功能的实现代码了, 和这些功能有关的文件为:
文件 内容
replication.c 复制功能的实现代码。
sentinel.c Redis Sentinel 的实现代码。
cluster.c Redis 集群的实现代码。
注意, 因为 Redis Sentinel 用到了复制功能的代码, 而集群又用到了复制和 Redis Sentinel 的代码, 所以在阅读这三个模块的时候, 记得先阅读复制模块, 然后阅读 Sentinel 模块, 最后才阅读集群模块, 这样理解起来就会更得心应手。
如果你连这三个模块都读完了的话, 那么恭喜你, 你已经读完了 Redis 单机功能和多机功能的所有代码了!
下图总结了本文介绍的阅读顺序:
digraph {
node [shape = plaintext]
datastruct [label = "数据结构\n(sds、adlist、dict、t_zset、hyperloglog)"]
encoding_datastruct [label = "内存编码数据结构\n(intset、ziplist)"]
object [label = "数据类型\n(object、t_string、t_list、t_hash、t_set、t_zset、hyperloglog)"]
db [label = "数据库相关\n(db、notify、rdb、aof)"]
client_and_server [label = "客户端与服务器相关\n(ae、networking、redis)"]
multi_server [label = "多机功能\n(replication、sentinel、cluster)"]
//
datastruct -> encoding_datastruct -> object -> db -> client_and_server -> multi_server
}
结语
Redis 的设计非常简洁、优美、精巧和高效, 任何人只要愿意去阅读它的代码的话, 应该都会有所收获的。
希望这篇文章能够给想要阅读 Redis 代码的朋友们带来一些帮助, 也欢迎各位随时和我讨论 Redis 源码方面的问题, 或者跟我分享各位阅读 Redis 源码的心得和经验。
另外我的 Redis 源码注释 项目以及 《Redis 设计与实现》 一书对于理解 Redis 的源代码应该也会有所帮助, 有兴趣的朋友可以自行了解该项目/书本。
黄健宏(huangz)
2014.7.28
Ⅸ php怎么使用 redis pub/sub
一.场景介绍
最近的一个项目需要用到发布/订阅的信息系统,以做到最新实时消息的通知。经查找后发现了redis pub/sub(发布/订阅的信息系统)可以满足我的开发需求,而且学习成本和使用成本也比较低。 :grin:
二.什么是redis pub/sub
资料查看
大家在看我的blog的同时可以打开redis官方对于redis pub/sub的介绍,感觉看英文文档吃力的话 :cry: ,可以看redis中文网的翻译介绍.
Pub/Sub功能(means Publish, Subscribe)即发布及订阅功能
1> 基于事件的系统中,Pub/Sub是目前广泛使用的通信模型,它采用事件作为基本的通信机制,提供大规模系统所要求的松散耦合的交互模式:订阅者(如客户端)以事件订阅的方式表达出它有兴趣接收的一个事件或一类事件;发布者(如服务器)可将订阅者感兴趣的事件随时通知相关订阅者。
2> 消息发布者,即publish客户端,无需独占链接,你可以在publish消息的同时,使用同一个redis-client链接进行其他操作(例如:INCR等)
3> 消息订阅者,即subscribe客户端,需要独占链接,即进行subscribe期间,redis-client无法穿插其他操作,此时client以阻塞的方式等待“publish端”的消息;这一点很好理解,因此subscribe端需要使用单独的链接,甚至需要在额外的线程中使用。
三.redis pub/sub的使用
下面我将配着实图(用我的本地机器环境)来为大家讲解redis的pub/sub怎么去使用 .
没有安装phpredis扩展的或者没有redis服务的,请参考我的另一篇blog,有详细的安装介绍,这里不再赘述了。
1、启动redis服务端:
[caption id="attachment_1777" align="alignnone" width="300"] php redis pub/sub(消息订阅系统)经验总结之一[/caption]
2、新开一个终端,启动redis客户端,并做为subscribe客户端(消息订阅者),订阅一个名字叫test的频道的频道信息:
3、启动redis客户端,并做为publish客户端(消息发布者),发布一个名字叫test的频道,信息是:hello,world
4、再切换到2步骤中的redis客户端窗口,会发现,已经订阅到了刚才发布的 'hello,world'消息:
5、模式匹配订阅
Redis 的Pub/Sub实现支持模式匹配。客户端可以订阅全风格的模式以便接收所有来自能匹配到给定模式的频道的消息。
比如,将接收所有发到 test.name,test.phone,test.address...等等的消息,该这样写:
[shell]PUBSCRIBE test.*[/shell]
在终端回车后,同时再新的窗口里分别发布两个频道的消息,名字分别为:test.name和test.phone,然后切换到订阅端的窗口里,结果如下图所示:
由上图可以看出,在订阅了test.*频道后,一共收到了 test.name和test.phone两个频道的消息,这就是模式匹配订阅。
那么取消订阅匹配该模式的客户端也比较简单:
[shell]PUNSUBSCRIBE test.*[/shell]
6、好,以上的这些简单的demo,就是关于redis pub/sub(Publish/Subscribe,发布/订阅的信息系统)的最基本使用。说了这么多,跟php也没有挂上什么钩,别着急,重要的都往往最后出场。 :lol:
四.php使用redis的pub/sub(发布/订阅的信息系统)
这里我列出一些常用的命令:
phpredis的安装
redis的客户端连接支持多种语言。这里我用的是php的phpredis,它是用c语言编写的,目前已经作为php的一个模块扩展,没有安装的可以参考我的另一篇blog,已经安装的可以忽略此步骤.
命令手册
这里我列出一些常用的:
[shell]Redis::__construct构造函数
$redis = new Redis();
connect, open 链接redis服务
参数
host: string,服务地址
port: int,端口号
timeout: float,链接时长 (可选, 默认为 0 ,不限链接时间)
注: 在redis.conf中也有时间,默认为300
pconnect, popen 不会主动关闭的链接
参考上面
setOption 设置redis模式
getOption 查看redis设置的模式
ping 查看连接状态
get 得到某个key的值(string值)
如果该key不存在,return false
set 写入key 和 value(string值)
如果写入成功,return ture
setex 带生存时间的写入值
$redis->setex('key', 3600, 'value'); // sets key → value, with 1h TTL.
setnx 判断是否重复的,写入值<br />$redis->setnx('key', 'value');
$redis->setnx('key', 'value');
delete 删除指定key的值
返回已经删除key的个数(长整数)
$redis->delete('key1', 'key2');
$redis->delete(array('key3', 'key4', 'key5'));
<span style="font-size: 16px; line-height: 24px;">
[/shell]
更详细的使用请参考这里,我就不写太多,因为我要直接摞代码了. :lol:
publish(消息发布端):pub.php
[php]
/**
* redis sub(消息订阅端)
* @ blog: phping.sinaapp.com
* @date 2016-04-24 15:00
*/
$redis = new Redis();
// 第一个参数为redis服务器的ip,第二个为端口
$res = $redis->connect('127.0.0.1', 6379);
// test为发布的频道名称,hello,world为发布的消息
$res = $redis->publish('test','hello,world');
[/php]
subscribe(消息订阅端): sub.php
[php]
/**
* redis sub(消息订阅端)
* @ blog: phping.sinaapp.com
* @date 2016-04-24 15:00
*/
$redis = new Redis();
$res = $redis->pconnect('127.0.0.1', 6379,0);
$redis->subscribe(array('test'), 'callback');
// 回调函数,这里写处理逻辑
function callback($instance, $channelName, $message) {
echo $channelName, "==>", $message,PHP_EOL;
}
[/php]
开始订阅redis消息
前面已经提到过,消息订阅者,即subscribe客户端,需要独占链接,即进行subscribe期间,redis-client无法穿插其他操作,此时client以阻塞的方式等待“publish端”的消息,所以我们用命令行来执行:
则 订阅消息的redis客户端已经启动,随时等待发布过来的消息并订阅该消息.
发布redis消息
同样,命令行执行消息发布端的脚本即可:
[shell]
php pub.php
[/shell]
切换到消息订阅端的窗口发现终端有输出,如下图:
哈哈,是不是 收到了发布端发布的'hello,world'这条消息呢。
五.总结
在命令执行redis订阅端脚本时,发现在终端会输出:
[shell]
PHPFatalerror: Uncaughtexception'RedisException'withmessage'readerroronconnection'in…
[/shell]
这样的错误是什么原因呢?请看我下一篇blog,本篇主要是讲一些基本的语法,在下一篇我会着重讲解一些在实际开发中遇到过的哪些问题和跳过的哪些坑。。。 :mrgreen:
码字不易,望转载注明出处哈 ;)
Ⅹ redis 的 pubsub 有什么作用
Redis 是什么?
通常而言目前的数据库分类有几种,包括 SQL/NSQL,,关系数据库,键值数据库等等 等,分类的标准也不以,Redis本质上也是一种键值数据库的,但它在保持键值数据库简单快捷特点的同时,又吸收了部分关系数据库的优点。从而使它的位置处于关系数据库和键值数 据库之间。Redis不仅能保存Strings类型的数据,还能保存Lists类型(有序)和Sets类型(无序)的数据,而且还能完成排序(SORT) 等高级功能,在实现INCR,SETNX等功能的时候,保证了其操作的原子性,除此以外,还支持主从复制等功能。
更为详细的描述请参考如下:
http://code.google.com/p/redis/wiki/index
Redis官方也同样提供了一个名为Retwis的项目代码,可以对照着官方学习。
2 Redis用来做什么?
通常局限点来说,Redis也以消息队列的形式存在,作为内嵌的List存在,满足实时的高并发需求。而通常在一个电商类型的数据处理过程之中,有关商品,热销,推荐排序的队列,通常存放在Redis之中,期间也包扩Storm对于Redis列表的读取和更新。
一个详细的示范用例,还请参考本ID的另外一篇博文: Storm 【topN】 排序。期间使用的是:Storm-redis-pubsub的接口
3 Redis的优点
性能极高 – Redis能支持超过 100K+ 每秒的读写频率。
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
4 Redis的缺点
是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
总结: Redis受限于特定的场景,专注于特定的领域之下,速度相当之快,目前还未找到能替代使用产品。