- 类型介绍
- 常用命令
- set
- get
- setex
- setnx
- append
- incr
- decr
- 类型应用
- 数据行缓存
- 锁
- 基本数据结构
- sds结构
- t_string文件
- 编码方式
类型介绍
String是redis最基本也是最简单的数据类型,典型的key-value结构,value可以是字符串、数值、浮点数。例如:key为name,值为ryan,见下图
常用命令
set
设置一个key的value
redis>set name "ryan"
OK
get
获取一个key的value
redis> get name"ryan"
setex
设置key对应字符串value,并且设置key在给定的seconds时间之后超时过期
redis> setex name 10"ryan"
OK
setnx
将key设置值为value,如果key不存在
redis> setnx name"ryan"
(integer) 1
redis> setnx name "ryan"
(integer) 0
append
如果key已经存在,并且值为字符串,那么这个命令会把value追加到原来值(value)的结尾。 如果key不存在,那么它将首先创建一个空字符串的key,再执行追加操作
redis> append name"ryan"
(integer) 4
redis> append name "zhang"
(integer) 9
incr
对存储在指定key的数值执行原子的加1操作,如果指定的key不存在,那么在执行incr操作之前,会先将它的值设定为0
redis> incr age(integer) 1
decr
对key对应的数字做减1操作。如果key不存在,那么在操作之前,这个key对应的值会被置为0
redis>set age 25
OK
redis> decr age
(integer) 24
类型应用
数据行缓存
将从db中取出的数据缓存,以php为例,将从db中取出的关联数组转化为json串存入redis(php扩展基于phpredis)
<?php
/**
* 缓存数据行示例
* @param array $data db取出的关联数组
* @return bool
*/
function cacheData($data) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$key = 'data:'.$data['id'];//id为数据行的主键
$value = json_encode($data);
return $redis->set($key, $value);
}
锁
在多任务环境下,多个任务对同一个资源进行操作时会有操作错乱的问题,因此当某一个任务拿到资源的操作权,我们对其进行加锁,对资源操作完后再释放锁,这样在加锁期间其他的任务等待锁释放后再操作资源。
<?php
class Lock {
private $_redis;
public function __construct() {
$this->_redis = new Redis();
$this->redis->connect('127.0.0.1', 6379);
}
public function acquire($lockName, $acquireTime = 10, $lockTimeout = 10) {
$value = uniqid();
$key = 'lock:'.$lockName;
$lockTimeout = intval($lockTimeout);
$end = time() + $acquireTime;
while (time() < $end) {
if ($this->_redis->setnx($key, $value)) {
$this->_redis->expire($key, $lockTimeout);
return $value;
} elseif (!$this->_redis->ttl($key)) {
$this->_redis->expire($key, $lockTimeout);
}
usleep(1000);
}
return false;
}
public function release($lockName, $value) {
$key = 'lock:'.$lockName;
if ($this->_redis->get($key) == $value) {
$this->_redis->del($key);
return true;
}
return false;
}
}
$lockObj = new Lock();
$lock = $lockObj->acquire('test');
if ($lock) {
//do something...
$lockObj->release('test', $lock);
}
基本数据结构
sds结构
sds是redis底层的数据结构,也是redis最核心、最基本的数据结构;redis存储字符串类型的数据都会使用该数据结构。
在redis源码中sds.h和sds.c两个文件为sds的实现。
redis4.0中的sds结构会根据存入数据的大小选择,所以定义了存储不同长度的结构体
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* 字符串使用长度 */
uint8_t alloc; /* 除头和空终止符的长度 */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];/* 存放字符的数组 */
};
struct __attribute__ ((__packed__)) sdshdr16 {
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 {
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 {
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[];
};
根据传入数据的大小确定选用的结构体类型
static inline char sdsReqType(size_t string_size) {
if (string_size < 1<<5)
return SDS_TYPE_5;//使用sdshdr5
if (string_size < 1<<8)
return SDS_TYPE_8;//使用sdshdr8
if (string_size < 1<<16)
return SDS_TYPE_16;//使用sdshdr16
#if (LONG_MAX == LLONG_MAX)
if (string_size < 1ll<<32)
return SDS_TYPE_32;//使用sdshdr32
#endif
return SDS_TYPE_64;//使用sdshdr64
}
t_string文件
t_string.c封装了关于字符串的上层操作,如set操作会调用setCommand方法
void setCommand(client *c) {
int j;
robj *expire = NULL;
int unit = UNIT_SECONDS;
int flags = OBJ_SET_NO_FLAGS;
//解析命令参数
for (j = 3; j < c->argc; j++) {
char *a = c->argv[j]->ptr;
robj *next = (j == c->argc-1) ? NULL : c->argv[j+1];
if ((a[0] == 'n' || a[0] == 'N') &&
(a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
!(flags & OBJ_SET_XX))
{
flags |= OBJ_SET_NX;
} else if ((a[0] == 'x' || a[0] == 'X') &&
(a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
!(flags & OBJ_SET_NX))
{
flags |= OBJ_SET_XX;
} else if ((a[0] == 'e' || a[0] == 'E') &&
(a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
!(flags & OBJ_SET_PX) && next)
{
flags |= OBJ_SET_EX;
unit = UNIT_SECONDS;
expire = next;
j++;
} else if ((a[0] == 'p' || a[0] == 'P') &&
(a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
!(flags & OBJ_SET_EX) && next)
{
flags |= OBJ_SET_PX;
unit = UNIT_MILLISECONDS;
expire = next;
j++;
} else {
addReply(c,shared.syntaxerr);
return;
}
}
c->argv[2] = tryObjectEncoding(c->argv[2]);//对设置值得转换
//调用公用创建方法
setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);
}
编码方式
- OBJ_ENCODING_RAW
- OBJ_ENCODING_INT
- OBJ_ENCODING_EMBSTR
三种编码方式,根据存入的数据是否是整数、大小来决定使用哪种