redis数据结构
Redis是一种存储key-value的内存型数据库,它的key都是字符串类型,value支持存储5种类型的数据:String(字符串类型)、List(列表类型)、Hash(哈希表类型、即key-value类型)、Set(无序集合类型,元素不可重复)、Zset(有序集合类型,元素不可重复)。
针对这5种数据类型,Redis在底层都是使用的redisObject对象表示的。redisObject有3个重要的属性:type、encoding、ptr。
其中,type表示value的数据类型,也就是我们上面说的5种数据类型(REDIS_STRING、REDIS_LIST、REDIS_HASH、REDIS_SET、REDIS_ZSET);encoding表示value的编码,即底层使用了哪种数据结构;ptr是一个指向保存value的底层数据结构的指针。
其中type和ptr属性不用做过多的解释,一看就知道什么意思,本篇文章主要分析value的encoding编码,也就是不同数据类型的value对应的底层数据结构是什么以及数据结构的原理分析。
Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的 *** 作,而且这些 *** 作都是原子性的。
在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改 *** 作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
codis是在redis官方集群方案redis-cluster发布之前便已被业界广泛使用的redis集群解决方案。
codis server :这是进行了二次开发的 Redis 实例,其中增加了额外的数据结构,支持
数据迁移 *** 作,主要负责处理具体的数据读写请求。
codis proxy :接收客户端请求,并把请求转发给 codis server。
Zookeeper 集群 :保存集群元数据,例如数据位置信息和 codis proxy 信息。
codis dashboard 和 codis fe :共同组成了集群管理工具。其中,codis dashboard 负
责执行集群管理工作,包括增删 codis server、codis proxy 和进行数据迁移。
而 codis fe 负责提供dashboard的Web *** 作界面,便于我们直接在 Web 界面上进行集群管理
codis proxy本身支持 Redis 的 RESP 交互协议,所以只需和proxy建立连接,就相当于和
整个codis集群建立连接,codis proxy 接收到请求,就会查询请求数据和 codis server 的
映射关系,并把请求转发给相应的 codis server 进行处理。当 codis server 处理完请求后,
会把结果返回给codis proxy,proxy 再把数据返回给客户端。
codis集群共有1024个slot,编号从0-1023,可以自定义或者自动均匀分配到各个server上,
使用 CRC32 算法计算数据 key 的哈希值然后对1024取模,得到slot编号,然后通过slot
和server的映射关系请求到具体实例。
Slot 和 codis server 的映射关系称为数据路由表,我们在 codis dashboard 上分配好路由表后,
dashboard 会把路由表发送给 codis proxy,同时,dashboard 也会把路由表保存在
Zookeeper 中。codis-proxy 会把路由表缓存在本地,当它接收到客户端请求后,直接查询
本地的路由表进而完成请求的转发。
跟redis cluster的区别是,codis的把映射关系存在zookeeper和proxy中,redis cluster
是通过各个实例相互通信进而存在各个实例本地,当实例较多时,可能会受到网络状况
影响而且整个集群的网络资源消耗相较之下也比较大。
codis按照slot粒度进行数据迁移,比如从serverA迁移至serverB,会从A要迁移的slot中
随机选择一个数据,发送给B,A得到确认消息后,删除本地数据。重复这个过程直到
迁移完毕。
迁移方式有两种,一种是同步迁移,一种是异步迁移。
同步迁移时,源server是阻塞的,由于迁移过程中需要数据的序列化、网络传输、删除
等等 *** 作,如果是bigkey则会阻塞较长时间。
所以通常采用异步迁移。异步迁移模式下,源server发送完迁移数据之后,就可以继续接受
处理请求,等到目标server返回ack之后,再执行删除 *** 作,这个过程中,为了防止数据
不一致的情况,被迁移的数据是只读模式。
对于bigkey的数据,异步迁移采用拆分指令的方式,对bigkey中的每个元素用一条指令
进行迁移,而不是等待整个数据序列化完毕再迁移,进而避免了需要序列化大量数据而
阻塞的风险,因为整个bigkey的数据是被拆分的,如果迁移期间server宕机,便会破坏
迁移的原子性,所以codis对bigkey的数据设置了一个过期时间,如果原子性遭到破坏,
目标server的数据就会过期后删除以保证迁移的原子性。可以通过异步迁移命令
SLOTSMGRTTAGSLOT-ASYNC 的参数numkeys 设置每次迁移的 key 数量,以提升
bigkey异步迁移的速度。
最后分享一下我的一些架构设计心得,通过redis cluster和codis的slot设计,把原本
数百上千万的key和实例的映射抽象成slot和实例的映射,大大减少了映射量,进而把
slot和key的映射分担到各个实例中,这种粒度的抽象,值得借鉴,另外codis与redis cluser
不同的是,codis通过zk作为第三方储存系统,保存了集群实例、状态和slot分配等信息,
这样避免了实例间的大量心跳来同步集群状态进而减少实例间的网络通信量,对于实现
大规模的集群是有益的设计,网络带宽可以集中在客户端请求上面,需要注意的是需要保证
zk的通信带宽,毕竟集群的信息同步都交给了zk
一 单机安装
解压redis
进入文件夹执行mke and make install
1
2
# prefix指定安装路径
make && make prefix=/opt/hjf/redis-2818 install
配置环境变量
以后自动打开redis服务
进入解压包的utils目录, 执行:
1
/install_serversh
进入redis服务器:
1
redis-cli
二shell命令
1 strings
1 set:设置
SET key value [EX seconds] [PX milliseconds] [NX|XX]
EX: 过期时间(秒)
PX: 过期时间(毫秒)
NX: 键不存在时, 才能设置(只能创建, 不能修改)
XX: 键存在时, 才能设置(只允许修改, 不允许创建)
普通模式
1
2
# 默认永久保存
set k1 aa
设置生命周期
1
2
# 5s后自动删除
set k3 c ex 5
过时后就获取不了了
只允许创建, 不允许修改
1
2
# 相当于: setnx k4 abc
set k4 abc nx
只允许修改, 不允许创建
1
set k5 123 xx
2 get:获取
GET key
1
get k1
3 keys: 获取key列表
KEYS pattern
1
2
keys
keys k
4 setnx: 如果key不存在,则执行
SETNX key value
相当于只允许创建,而不允许修改。
1
2
setnx k3 5
set k3 5 nx #等价
5 strlen 获取长度:
STRLEN key
1
strlen k6
6 清空库
flushall 清空所有库的数据:
1
flushall
flushdb: 清空当前库数据:
1
flushdb
7 expire 设置过期时间:
EXPIRE key seconds
1
expire k1 5
8 type: 查看key的类型
1
TYPE key
虽然所有的数据类型都是以“string”存储,但是有属性加一区别:
9 自增/自减
incr / incrby: 增加
INCR key: 自增+1
INCRBY key num: 自增 + num
decr / decrby:减少
DECR key: 自减 - 1
INCRBY key num: 自减 - num
注意: 在遇到数值 *** 作时,redis会将字符串类型转换成数值
由于INCR等指令本身就具有原子 *** 作的特性,所以我们完全可以利用redis的INCR、INCRBY、DECR、DECRBY等指令来实现原子计数的效果,假如,在某种场景下有3个客户端同时读取了mynum的值(值为2),然后对其同时进行了加1的 *** 作,那么,最后mynum的值一定是5。不少网站都利用redis的这个特性来实现业务上的统计计数需求。
10 getset 返回旧值,并设置新的值:
GETSET key value
11 append: 追加
APPEND key value
12 getrange获取子字符串
GETRANGE key start end
2 集合:
1 无序集合
sadd: 创建集合
1
sadd set1 s1 s2 s3 s2 s4 s1
smembers: 读取集合元素
1
smembers set1
sismember : 判断元素是否在集合中
如果在, 则返回1; 如果不在, 则返回0
1
sismember set1 "one"
srandmember : 随机获取集合元素
1
srandmember set1
mset:同时设置多组key-value
MSET key value [key value …]
1
mset key1 value1 key2 value2
sdiff: 差集
1
sdiff num1 num2
交集:sinter
1
sinter num1 num2
并集:sunion
1
sunion num1 num2
2 有序集合
zadd: 添加元素
ZADD key score member [score member …]
1
2
zadd set2 1 one
zadd set2 2 two 3 three 4 four 5 five
zrange: 获取元素
获取所有元素
1
zrange set2 0 -1
获取指定范围内的元素
1
zrange set2 2 4
列出所有元素,同时列出其位置
1
zrange set2 0 -1 withscores
3 哈希
hashes存的是字符串和字符串值之间的映射,比如一个用户要存储其全名、姓氏、年龄等等,就很适合使用哈希。
hmset: 建立哈希,并赋值
1
hmset user1 user zhangsan age 20 gender m
hgetall: 列出哈希的内容
1
hgetall user1
hget: 获取哈希中的某一个值
1
hget user1 age
hset: 更改哈希中的某一个值
1
hset user1 user lisi
hkeys: 获取key列表
1
hkeys user1
hvals: 获取value列表
1
hvals user1
4 列表
lpush: 向列表头部(左侧)插入元素, 返回当前列表元素个数
1
2
lpush l1 a
lpush l1 b c
rpush: 向列表尾部(右侧)插入元素, 返回当前列表元素个数
1
2
rpush l1 c
rpush l1 d
lrange: 获取列表元素
1
2
3
4
# 获取编号0到倒数第一个元素
lrange l1 0 -1
# 获取编号0到编号2的
lrange l1 0 2
rpop: d出列表尾部(右侧)的元素
1
rpop l1
lpop: d出头部(左侧)的元素
1
lpop l1
lrem : 删除指定值
LREM key count value
如果count < 0, 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。
如果count > 0, 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。
如果count = 0, 移除表中所有与 VALUE 相等的值。
1
lrem l3 -2 a
1
lrem l3 -2 c
1
lrem l3 0 b
Redis 的流水线功能允许客户端一次将多个命令请求发送给服务器,并将被执行的多个命令请求的结果在一个命令回复中全部返回 给客户端,使用这个功能可以有效地减少客 户端在执行多个命令时需要与服务器进行通信的次数,多个命令执行的数据会以列表的形式返回
redigo客户端使用Send和Do方法来实现流水线事务
1与M *** 作的对比
pipeline:多条命令发送到服务端多条命令在队列中排队不是原子 *** 作,返回的结果是顺序的
M *** 作:是原子 *** 作
2注释:
SUBSCRIBE channel [channel …] // 订阅频道
示例 :
SUBSCRIBE news::it
PSUBSCRIBE pattern [pattern] // 订阅一个或多个模式
订阅一个或多个模式, pattern 参数可以包含 glob 风格的匹配符,比如:
示例 :
PSUBSCRIBE news::[ie]t
UNSUBSCRIBE [channel [channel …]] // 退订指定频道
PUNSUBSCRIBE [pattern [pattern…]] // 退订指定模式
PUBLISH channel message
将消息发送至指定的频道,命令返回接收到消息的 订阅者数量。
PUBSUB CHANNELS [pattern]
PUBSUB NUMSUB [channel-1 channel-N]
PUBSUB NUMPAT
Redis 的事务功能允许用户将多个命令包裹起来,然后一次性地、按 顺序地执行被包裹的所有命令。在事务执行的过程中,服务器不会中断事务而改去执行其他命令请求,只有在事务包裹的所有命令都被执行完毕之后,服务器才会去处理其他命令请求
通过事务, 我们可以让 Redis 一次性地执行多个命令, 并且确保事务中的命令要么就全部都 执行,要么就一个都不执行
MULTI // 开始一个新的事务
DISCARD // 放弃事务
EXEC // 执行事务中的所有命令
按照命令被入队到事务队列中的顺序,执行事务队列中的所有命令。命令的复杂度为队列中所有命令的复杂度之和。命令的返回值是一个列表,列表里包含了事 务队列中所有被执行命令的返回值
与流水线对比:
使用 WATCH 来防止竞争条件:
位图可以直接 *** 作数据保存的二进制数据位的值
getbit key offset
注:对key所存储的字符串值,获取指定偏移量上的位
setbit key offset value
注:对key所存储的字符串值,设置或清除指定偏移量上的位(bit)返回值为该位在setbit之前的值,value只能取0或1,offset从0开始
bitcount key [start end]
注:获取位图指定范围中位值为1的个数,如果不指定start与end,则取所有
bitpos key tartget [start end]
注:计算位图指定范围第一个等于target值的偏移量(位置)
基于算法,使用极小空间完成独立数量统计的功能,本质还是一个字符串
pfadd key element1 [element2]
注:向HyperLogLog中添加元素
pfcount key1 [key2]
注:计算HyperLogLog的独立总数
pfmerge hyperloglogKey key1 [key2]
注:合并多个hyperLogLog到hyperloglogKey中
功能:存储经纬度、计算两地距离、范围计算等,基于ZSet实现
geoadd key longitude latitude elementName [lon lat elementName]
注:增加经纬度元素
geopos key element1 [element2]
注:获取经纬度元素
geodist key member1 member2 [unit]
注:获取两个经纬度元素的距离
unit取值范围
注:以给定的经纬度为中心,返回包含的位置元素当中,与中心距离不超过给定最大距离的所有位置元素。
georadiusbymember key member radius unit [withcoord][withdist][withhash][COUNT count][sort][store key][storedist key]
注:以给定的元素为中心,返回包含的位置元素当中,与中心距离不超过给定最大距离的所有位置元素。
慢查询相关配置:
它决定了慢查询日志最多能保存多少条日志,slow log本身是一个内存中的FIFO队列,当队列大小超过slowlog-max-len时,最旧的一条日志将被删除,而最新的一条日志加入到slow log中。
2slowlog-log-slower-than
它决定要对执行时间大于多少微妙(microsecond , 1秒=1,000,000 微妙)的查询进行记录
动态配置:
config set slowlog-max-len 1000
config set slowlog-log-slower-than 1000
相关命令:
slowlog get [n]
注:获取慢查询列表中的慢查询信息
slowlog len
注:获取慢查询队列长度
slowlog reset
注:清空慢查询队列
一、string
string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。
string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg或者序列化的对象。
string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。
命令: SET 和 GET 命令
二、hash
Redis hash 是一个键值(key=>value)对集合。
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
HMSET, HGET 命令,HMSET 设置了两个 field=>value 对, HGET 获取对应 field 对应的 value。
三、list
列表是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)。
列表最多可存储 232 - 1 元素 (4294967295, 每个列表可存储40多亿)。
lpush 设置值,lrange取值
四、set
redis的set是string的无序集合。集合通过哈希表实现。
添加一个string元素到key对应的set集合中,用 sadd命令。返回1表示成功,0表示在集合中已存在,返回错误表示key对应的set不存在。
查看用smembers 命令
集合内元素的唯一性,第二次插入的元素将被忽略。
集合中最大的成员数为 232 - 1(4294967295, 每个集合可存储40多亿个成员)。
五、zset
redis的zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
添加元素到集合,元素在集合中存在则更新对应score:zadd key score member。
Redis数据模型
Redis的外围由一个键、值映射的字典构成。与其他非关系型数据库主要不同在于:Redis中值的类型不仅限于字符串,还支持如下抽象数据类型:
1、字符串列表
2、无序不重复的字符串集合
3、有序不重复的字符串集合
4、键、值都为字符串的哈希表
值的类型决定了值本身支持的 *** 作。Redis支持不同无序、有序的列表,无序、有序的集合间的交集、并集等高级服务器端原子 *** 作。
一、String | 字符串类型
Redis的字符串类型,可以存储字符串、整数或浮点数,如果存储的是整数或者浮点数,还能执行自增或自减 *** 作。Reids的string类型是二进制的,可以包含任何数据,比如一个序列化的对象、一个、字节流等,不过存储大小上限是512M。
Redis底层定义了自己的一种数据结构。
二、List | 列表类型
Redis的列表类型和许多编程语言中的列表类型类似,可以有序地存储多个字符串,支持从列表的左端和右端推入或d出元素,Redis列表的底层实现是压缩列表,Redis内容自己实现的数据结构和双端链表。
将一个或者多个value值插入列表的表头。如果 key 不存在,会创建一个空列表并执行 LPUSH *** 作。当 key
存在但不是列表类型时,返回一个错误。
三、set | 集合类型
Redis的集合以无序的方式存储多个不同的元素,这里要注意的是无序和不同。除了对集合能快速执行添加、删除、检查一个元素是否在集合中之外,还可以对多个集合执行交集、并集和差集运算。
Redis的集合类型底层实现主要是通过一种叫做字典的数据结构。不过Redis为了追求极致的性能,会根据存储的值是否是整数,选择一种intset的数据结构。当满足一定条件后,会切换成字典的实现。
四、hash | 散列表(哈希表)
Redis的hash类型其实就是一个缩减版的redis。它存储的是键值对,将多个键值对存储到一个redis键里面。
hash类型的底层主要也是基于字典这种数据结构来实现的。
五、zset | 有序集合
有序集合相比较于集合,多个有序两个字,我们知道set集合类型存储的元素是无序的,那Redis有序集合是怎么保证有序的使用分值,有序集合里存储这成员与分值之间的映射,并提供了分值处理命令,以及根据分值的大小有序地获取成员或分值的命令。
Redis有序集合的实现使用了一种叫跳跃表的数据结构(简称跳表,可自行查阅),同时也使用到了前面提到的压缩列表。也是满足一定条件的话,会自行转换。
以上就是关于redis数据结构全部的内容,包括:redis数据结构、redis架构模式(4) Codis、redis如何在shell中建表等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)