Redis常用数据结构和存储结构-String

redis数据类型

String、Hash、Set、List、ZSet、Hyperloglog、Geo、Streams

1. String字符串
存储类型

可以用来存储int(整数)、float(单精度浮点数)、String(字符串)

操作命令

1)赋值:SET key value。如 set hello world

2)取值:GET key。如 get hello。返回是world
3)自增:INCR key。就是 Mysql的AUTO_INCREMENT。每次执行 INCR key时,该key的值都会+1.若key不存在,则先建立一个0,然后+1,返回 1。如果值不是整数则报错。该操作是原子操作。
4)自减:DECR key。将指定 key 的值减少 1。 如 DECR num,就是 num-1
5)自增 N:INCRBY key increment 用来给指定 key 的值加 increment。如 INCRBY num 5 就是 num+5
6)自减N:DECRBY key increment 用来给指定 key 的值减 increment。如 DECRBY num 5 就是 num-5
7)增加浮点数:INCRBYFLOAT key increment。
8)向尾部追加:APPEND key value。如set test:key 123、append test:key 456、get test:key 就是 123456
9)获取长度:STRLEN key。
10)同时给多个 key 赋值:MSET title 这是标题 description 这是描述 content 这是内容。
11)同时获取多个 key 的值:MGET title description content
12)位操作之获取:GETBIT key offset。如字符 a 在 redis 中的存储为 01100001(ASCII为98),那么 GETBIT key 2 就是 1,GET key 0 就是 0。
13)位操作之设置:SETBIT key offset value。如字符 a 在 redis 中的存储为01100001(ASCII为98),那么 SETBIT key 6 0,SETBIT key 5 1 那么 get key 得到的是b。因为取出的二进制为 01100010。
14)位操作之统计:BITCOUNT key [start] [end]:BITCOUNT
key 用来获取 key 的值中二进制是 1 的个数。而 BITCOUNT key start end 则是用来统计key的值中在第
start 和 end 之间的子字符串的二进制是 1 的个数(好绕啊)。
15)位操作之位运算:BITOP operation
resultKey key1 key2。operation 是位运算的操作,有 AND,OR,XOR,NOT。resultKey
是把运算结构存储在这个 key 中,key1 和 key2 是参与运算的 key,参与运算的 key 可以指定多个。

SDS
定义:

SDS是redis自己实现的字符串类型,其底层实现是简单动态字符串sds(simple dynamic string)
它类似于Java中的ArrayList,它采用预分配冗余空间的方式来减少内存的频繁分配。

代码:
struct  sdsher{
//记录buf中已保存字符的长度
//等于SDS所保存的字符串的长度
int  len;
//记录buf数组中未使用字节的数量
int free;
//字节数组,用于保存字符串
char buf[];
};

在redis3.2中对数据结构作出了修改,针对不同长度范围定义了不同的结构

typedef char *sds;      

struct __attribute__ ((__packed__)) sdshdr5 {     // 对应的字符串长度小于 1<<5
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {     // 对应的字符串长度小于 1<<8
    uint8_t len; /* used */                       //目前字符创的长度
    uint8_t alloc;                                //已经分配的总长度
    unsigned char flags;                          //flag用3bit来标明类型,类型后续解释,其余5bit目前没有使用
    char buf[];                                   //字符数组,以'\0'结尾
};
struct __attribute__ ((__packed__)) sdshdr16 {    // 对应的字符串长度小于 1<<16
    uint16_t len; /* used */
    uint16_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {    // 对应的字符串长度小于 1<<32
    uint32_t len; /* used */
    uint32_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {    // 对应的字符串长度小于 1<<64
    uint64_t len; /* used */
    uint64_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
}; 
那么为什么要使用SDS来实现字符串呢?
1)常数复杂度获取字符串长度:O(1)

C语言中没有字符串,只有用字符数组char[]实现。字符串获取字符串长度必须遍历数组,时间复杂度为O(N),使用SDS则直接获取对象len属性,拿到长度值。可以确保获取字符串长度的操作不会成为Redis的性能瓶颈

2)杜绝缓冲区溢出

C字符串不记录自身长度和空闲空间,容易造成缓冲区溢出,使用SDS则不会,SDS拼接字符串之前会先通过free字段检测剩余空间能否满足需求,不能满足需求的就会扩容

3)减少修改字符串时带来的内存重分配次数

使用C字符串的话:
每次对一个C字符串进行增长或缩短操作,长度都需要对这个C字符串数组进行一次内存重分配,比如C字符串的拼接,程序要先进行内存重分配来扩展字符串数组的大小,避免缓冲区溢出,又比如C字符串的缩短操作,程序需要通过内存重分配来释放不再使用的那部分空间,避免内存泄漏
使用SDS的话:
通过SDS的len属性和free属性可以实现两种内存分配的优化策略:空间预分配和惰性空间释放
1.针对内存分配的策略:空间预分配
在对SDS的空间进行扩展的时候,程序不仅会为SDS分配修改所必须的空间,还会为SDS分配额外的未使用的空间
这样可以减少连续执行字符串增长操作所需的内存重分配次数,通过这种预分配的策略,SDS将连续增长N次字符串所需的内存重分配次数从必定N次降低为最多N次,这是个很大的性能提升!
2.针对内存释放的策略:惰性空间释放
在对SDS的字符串进行缩短操作的时候,程序并不会立刻使用内存重分配来回收缩短之后多出来的字节,而是使用free属性将这些字节的数量记录下来等待将来使用,通过惰性空间释放策略,SDS避免了缩短字符串时所需的内存重分配次数,并且为将来可能有的增长操作提供了优化!

4)二进制安全

为了确保数据库可以二进制数据(图片,视频等),SDS的API都是二进制安全的,所有的API都会以处理二进制的方式来处理存放在SDS的buf数组里面的数据,程序不会对其中的数据做任何的限制,过滤,数据存进去是什么样子,读出来就是什么样子,这也是buf数组叫做字节数组而不是叫字符数组的原因,以为它是用来保存一系列二进制数据的

通过二进制安全的SDS,Redis不仅可以保存文本数据,还可以保存任意格式是二进制数

String编码
127.0.0.1:6379> 
127.0.0.1:6379> set foo bar
OK
127.0.0.1:6379> 
127.0.0.1:6379> object encoding foo  // 查看某个Redis键值的编码
"embstr"
127.0.0.1:6379> 
127.0.0.1:6379>

Redis 的每个键值内部都是使用一个名字叫做 redisObject 这个 C语言结构体保存的

  • type:表示键值的数据类型,包括 String、List、Set、ZSet、Hash
  • encoding:表示键值的内部编码方式

在这里插入图片描述

具体编码通过字符串长度、类型来区别,比如上面的embstr
1、int,存储8个字节的长整型(long,2^63-1)
2、embstr,embstr格式的SDS,存储小于44个字节的字符串
3、raw,SDS,存储大于44个字节的字符串

int和embstr什么时候转换成row

int不再是整型 - raw
int大小超过long的范围 - embstr
embstr超过44个字节
修改了embstr - raw

String存储结构图

在这里插入图片描述

String应用场景
  • 缓存热点数据
  • 分布式共享数据
  • 分布式锁,setNx,不存在时才会添加成功
  • 全局id
  • 计数器
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值