欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Redis基础之String类型

程序员文章站 2022-03-24 09:13:52
...

 

Redis基础之String类型

类型介绍

String是redis最基本也是最简单的数据类型,典型的key-value结构,value可以是字符串、数值、浮点数。例如:key为name,值为ryan,见下图

 

Redis基础之String类型

常用命令

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.hsds.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

三种编码方式,根据存入的数据是否是整数、大小来决定使用哪种