A. Redis --- 八种数据类型(基本命令)
String、Hash、List、Set和Zset。
等同于java中的, Map<String,String> string 是redis里面的最基本的数据类型,一个key对应一个value。
应用场景 :String是最常用的一种数据类型,普通的key/value存储都可以归为此类,如用户信息,登录信息和配置信息等;
实现方式 :String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr、decr等操作(自增自减等原子操作)时会转成数值型进行计算,此时redisObject的encoding字段为int。
Redis虽然是用C语言写的,但却没有直接用C语言的字符串,而是自己实现了一套字符串。目的就是为了提升速度,提升性能。 Redis构建了一个叫做简单动态字符串(Simple Dynamic String),简称SDS。
Redis的字符串也会遵守C语言的字符串的实现规则,即 最后一个字符为空字符。然而这个空字符不会被计算在len里头。
Redis动态扩展步骤:
Redis字符串的性能优势
常用命令 :set/get/decr/incr/mget等,具体如下;
ps:计数器(字符串的内容为整数的时候可以使用),如 set number 1。
补充:
等同于java中的: Map<String,Map<String,String>> ,redis的hash是一个string类型的field和value的映射表, 特别适合存储对象。 在redis中,hash因为是一个集合,所以有两层。第一层是key:hash集合value,第二层是hashkey:string value。所以判断是否采用hash的时候可以参照有两层key的设计来做参考。并且注意的是, 设置过期时间只能在第一层的key上面设置。
应用场景 :我们要存储一个用户信息对象数据,其中包括用户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,此时redisObject的encoding字段为int。
常用命令 :hget/hset/hgetall等,具体如下:
等同于java中的 Map<String,List<String>> ,list 底层是一个链表,在redis中,插入list中的值,只需要找到list的key即可,而不需要像hash一样插入两层的key。 list是一种有序的、可重复的集合。
应用场景 :Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现;
实现方式 :Redis list的实现为一个 双向链表 ,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括 发送缓冲队列 等也都是用的这个数据结构。
常用命令 :lpush/rpush/lpop/rpop/lrange等,具体如下:
性能总结 :
它是一个字符串链表,left、right都可以插入添加。
等同于java中的 Map<String,Set<String>> ,Set 是一种无序的,不能重复的集合。并且在redis中,只有一个key它的底层由hashTable实现的,天生去重。
应用场景 :Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动去重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且 set提供了判断某个成员是否在一个set集合内的重要接口 ,这个也是list所不能提供的;如保存一些标签的名字。标签的名字不可以重复,顺序是可以无序的。
实现方式 :set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。
常用命令 :sadd/spop/smembers/sunion等,具体如下:
ZSet(Sorted Set:有序集合) 每个元素都会关联一个double类型的分数score,分数允许重复,集合元素按照score排序( 当score相同的时候,会按照被插入的键的字典顺序进行排序 ),还可以通过 score 的范围来获取元素的列表。
应用场景 :Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以 通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。 当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。
底层实现 : zset 是 Redis 提供的一个非常特别的数据结构,常用作排行榜等功能,以用户 id 为 value ,关注时间或者分数作为 score 进行排序。实现机制分别是 zipList 和 skipList 。规则如下:
zipList:满足以下两个条件
skipList:不满足以上两个条件时使用跳表、组合了hash和skipList
为什么用skiplist不用平衡树?
主要从内存占用、对范围查找的支持和实现难易程度这三方面总结的原因。
拓展:mysql为什么不用跳表?
常用命令 :zadd/zrange/zrem/zcard等;
官网地址: https://redis.io/commands/geoadd
可以用来推算两地之间的距离,方圆半径内的人。
关于经度纬度的限制: https://www.redis.net.cn/order/3685.html
一般我们使用Hyperloglog做基数统计。
什么是基数?就是一个集合中不重复的数的个数。
集合A:{1,3,5,7,9,7}
集合B:{1,3,5,7,9}
AB集合的基数都是5
应用:统计网站的访问量(一个人访问网站很多次仍然算作一次)。
优点:占用的内存是固定的,找2^64次方个数的基数,只需要12KB内存。
缺点:有0.81%的错误率,可以忽略不计
概述: bitmap 存储的是连续的二进制数字(0 和 1),通过 bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。 我们知道 8 个 bit 可以组成一个 byte,所以 bitmap 本身会极大的节省储存空间。
应用场景: 适合需要保存状态信息(比如是否签到、是否登录...)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。
针对上面提到的一些场景,这里进行进一步说明。
使用场景一:用户行为分析 很多网站为了分析你的喜好,需要研究你点赞过的内容。
使用场景二:统计活跃用户
使用时间作为 key,然后用户 ID 为 offset,如果当日活跃过就设置为 1
那么我该如果计算某几天/月/年的活跃用户呢(暂且约定,统计时间内只有有一天在线就称为活跃),有请下一个 redis 的命令
使用场景三:用户在线状态
对于获取或者统计用户在线状态,使用 bitmap 是一个节约空间效率又高的一种方法。
只需要一个 key,然后用户 ID 为 offset,如果在线就设置为 1,不在线就设置为 0。
补充 :
巨人的肩膀:
https://www.cnblogs.com/Small-sunshine/p/11687809.html
https://mp.weixin.qq.com/s/CMu7oXVIKp2s-PXTdMlimA
B. redis鏀鎸佹湇锷$阌佸畾钖
Redis链韬涓嶆敮鎸佹湇锷$阌佸畾銆
Redis鏄涓绉嶉珮镐ц兘镄勫唴瀛樻暟鎹搴掳纴瀹冭骞挎硾鐢ㄤ簬钖勭嶉渶瑕佸揩阃熻诲啓鏁版嵁镄勫満鏅銆傜劧钥岋纴Redis骞舵病链夊湪链嶅姟绔鎻愪緵鏄惧纺镄勯挛瀹氭満鍒躲
鍦ㄥ疄璺典腑锛屽紑鍙戣呴氩父浣跨敤Redis镄勫叾浠栫壒镐ф潵瀹炵幇阌佸畾镄勬晥鏋溿备緥濡傦纴浣犲彲浠ヤ娇鐢≧edis镄凷ETNX锛圫et if Not Exists锛夊懡浠ゆ潵灏濊瘯銮峰彇涓涓阌併傝繖涓锻戒护浼氩皾璇曡剧疆涓涓阌鍊煎癸纴濡傛灉阌宸茬粡瀛桦湪锛屽垯璁剧疆澶辫触锛涘惁鍒欙纴璁剧疆鎴愬姛銆傞氲繃杩欑嶆柟寮忥纴浣犲彲浠ュ湪涓涓鍒嗗竷寮忕幆澧冧腑瀹炵幇涓绉嵝滃厛鍒板厛寰椻濈殑阌佸畾链哄埗銆傚綋涓涓杩涚▼銮峰彇浜嗛挛涔嫔悗锛屽叾浠栬繘绋嫔氨镞犳硶鍐嶈幏鍙栧悓涓涓阌侊纴鐩村埌阌佽閲婃斁銆
鍏蜂綋瀹炵幇镞讹纴鍙浠ヨ繖镙锋搷浣滐细
1. 杩涚▼灏濊瘯浣跨敤SETNX锻戒护璁剧疆涓涓阌佸畾镄勯敭锛堟瘆濡傚彨锅"lock"锛夛纴濡傛灉杩欎釜阌宸茬粡瀛桦湪锛岄偅涔堣存槑宸茬粡链夊叾浠栬繘绋嬭幏鍙栦简阌侊纴杩欎釜杩涚▼灏遍渶瑕佺瓑寰呮垨钥呭皾璇曞仛鍏朵粬澶勭悊銆
2. 濡傛灉SETNX锻戒护鎴愬姛锛岄偅涔堣繘绋嫔氨銮峰彇浜嗛挛锛屽彲浠ヨ繘琛屼竴浜涢渶瑕佺嫭鍗犺祫婧愮殑镎崭綔銆
3. 鍦ㄥ畬鎴愰渶瑕佺殑镎崭綔钖庯纴杩涚▼搴旇ヤ娇鐢―EL锻戒护鍒犻櫎杩欎釜阌佸畾镄勯敭锛屼互閲婃斁阌併傝繖镙凤纴鍏朵粬杩涚▼灏辫兘澶熻幏鍙栭挛骞惰繘琛屾搷浣滀简銆
闇瑕佹敞镒忕殑鏄锛岃繖鍙鏄涓绉嶅熀纭镄勯挛瀹氩疄鐜版柟寮忥纴瀹冨苟娌℃湁澶勭悊涓浜涘彲鑳藉嚭鐜扮殑闂棰桡纴姣斿傝繘绋嫔湪鎸佹湁阌佺殑镞跺椤穿婧冿纴瀵艰嚧阌佹棤娉曡閲婃斁銆傞拡瀵硅繖浜涢梾棰桡纴浣犲彲鑳介渶瑕佹洿澶嶆潅镄勮В鍐虫柟妗堬纴姣斿备娇鐢≧edis镄凟XPIRE锻戒护涓洪挛璁剧疆涓涓杩囨湡镞堕棿锛屾垨钥呬娇鐢≧edis镄勪簨锷★纸MULTI/EXEC锛夋潵淇濊瘉阌佸畾鍜屾搷浣灭殑铡熷瓙镐с傜劧钥岋纴杩欎簺閮介渶瑕佷綘镄勫簲鐢ㄧ▼搴忔潵澶勭悊锛岃屼笉鏄鐢卢edis链嶅姟绔鐩存帴鎻愪緵銆
浠ヤ笂淇℃伅浠呬綔鍙傝冿纴浣犲彲浠ユ牴鎹鍏蜂綋闇姹傚拰鐜澧冩潵璁捐″拰瀹炵幇阃傚悎浣犵殑阌佸畾链哄埗銆
C. Redis 学习总结(3) Redis 哨兵模式
在实际开发中不会仅仅部署一个 Redis 服务器,为了获得高可用,Redis 哨兵模式 则是高可用的一种选择。
本文先介绍下 哨兵模式,再介绍了如何在 springboot 项目中使用。
这意味着使用 Sentinel (哨兵模式),您可以创建一个 Redis 部署,它可抵抗某些类型的故障(进行故障迁移)而无需人工干预。
它有这些功能:
Sentinel 的分布式特性
Redis Sentinel 是一个分布式系统,多个 Sentinel 进程协同工作,有这些优势:
部署前需要了解:
三个节点的基本配置
法定人数和仲裁
在配置 哨兵模式时,要指定一个 quorum,它可理解为“法定人数”。
假设有3 个 哨兵,法定人数为2。那么:
哨兵和副本的自动发现
Sentinel 与其他 Sentinel 保持连接,以便相互检查彼此的可用性并交换消息。
但是,您不需要在您运行的每个 Sentinel 实例中配置其他 Sentinel 地址的列表,因为 Sentinel 使用 Redis 实例的 Pub/Sub 功能来发现正在监视相同主节点和副本的其他 Sentinel。
类似地,您不需要配置附加到主服务器的副本地址在哪里,因为 Sentinel 会通过查询 Redis 自动发现它们。
参考我的另一篇文章:
一般需要三个节点,每个节点有一个 redis 和一个哨兵。
下面再分别描述。
我这里按三个 节点,先配置 redis 的主从复制。1个节点作为 master ,2个副本。
配置节点1:master
这里的 redis 作为 master 主redis,其他两个节点作为从节点。
我的文件夹名字叫 box1,这里编辑一个 box1/redis.conf 文件,主要配置内容如下:
配置节点2:副本
编辑一个 box2/redis.conf 文件,主要配置内容如下:
配置节点3:副本
编辑一个 box3/redis.conf 文件,主要配置内容如下:
分别启动这三个redis
命令行执行 redis-server ,并指定 配置文件的路径参数。
如何查看“主从复制”是否配置成功?
使用 info replication 命令,操作如下:
副本节点设置为只读?
从 Redis 2.6 开始,副本已被默认设置为 只读,无需额外配置。.
一般情况下,至少会需要三个哨兵对redis 进行监控,我们可以通过修改端口启动多个sentinel 服务。
第一个哨兵:
哨兵的 默认端口是 26379 ,这里不改。
第二个哨兵:
修改哨兵端口。
第三个哨兵:
修改哨兵端口。
启动哨兵
使用 redis-sentinel 命令,分别启动这三个哨兵
哨兵的自动发现
当三个哨兵都启动后,在各个哨兵的打印日志里可以看到, 三个哨兵已互相发现了彼此的存在 。
至此,配置完毕了,我们有三个 redis,和三个哨兵,看下截图。
模拟 master 宕机
按 ctrl+c 停止 master ,其位于 6379 。停止后,从日志可以看到,哨兵和 redis副本先努力继续连接 6379,反复几次失败后,开始选举出新的 master。截图如下:
至此,配置完毕。
我们看下 springboot 项目的客户端如何配置 以访问 哨兵模式的 redis。
Redis 哨兵支持
对于处理高可用Redis,Spring Data Redis 已经支持Redis Sentinel,使用RedisSentinelConfiguration,如下例所示:
Jedis 和 Lettuce 两种 redis 驱动都可以支持。
RedisSentinelConfiguration 也可以用可以 通过 PropertySource 来设置,它允许您设置以下属性:
配置application.yml
比如我这里修改我的 application.yml 文件如下:
我的配置文件示例: https://github.com/vir56k/java_demo/tree/master/redis-sentinel
我的 springboot 配置实例: https://github.com/vir56k/java_demo/tree/master/redis-sentinel/springboot_redis_demo
Redis官网 sentinel 介绍
https://redis.io/topics/sentinel
spring-data/data-redis
https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#redis:sentinel
https://www.cnblogs.com/jaycekon/p/6237562.html
END
D. redis 命令执行过程
redis数据淘汰原理
redis过期数据删除策略
redis server事件模型
redis cluster mget 引发的讨论
redis 3.x windows 集群搭建
redis 命令执行过程
redis string底层数据结构
redis list底层数据结构
redis hash底层数据结构好闹
redis set底层数据结构
redis zset底层数据结构
redis 客户端管理
redis 主从同步-slave端
redis 主从同步-master端
redis 主从超时检测
redis aof持久化
redis rdb持久化
redis 数据恢复过程
redis TTL实现原理
redis cluster集群建立
redis cluster集群选主
这篇文章的目的是为了描述redis server在处理client命令的执行过程,大概包括流程图、源码、以及redis的命令格式说明,redis的通信协议参考自redis的则颂 官网 。
整个redis的server端命令执行过程就如下面这个流程图:
nread = read(fd, c->querybuf+qblen, readlen);负责读取命令数,通过processInputBuffer进行下一步处理。
核心在于processInlineBuffer处理内联命令,processMultibulkBuffer处理批量命令包括get/set等,核心的processCommand用于执行命令。
执行命令的过程其实主要是寻找命令对应的执行函数,通过lookupCommand查找对应的执行命令,通过call执行命令。
负责执行命令 c->cmd->proc 并更新统计信息,执行完成后负责同步数据 propagate 。
主要是负责同步数据到AOF文件和slave节点,feedAppendOnlyFile负责友盯罩同步到AOF文件,replicationFeedSlaves负责同步
AOF涉及的缓存有多份,包括
包含了命令和对应执行函数的映射关系,应该看上去很清晰命令。
协议的一般格式如下,注意前面的*或者$等字符,结尾的 是分隔符。
其中, 回复中的第二个元素为空。