初步了解Redis


前言

数据库,了解数据库的基本知识都为之后职业发展提供了有力的支持,所以一起学习起来吧。这一系列主要分享我自己学习的总结,因为目前日常工作涉及数据库较少,主要还是学习他人讲解视频,看代码的方式自学为主。过程中有问题,欢迎大家指点讨论。不断更新中…

一、Redis是什么?

Redis(Remote Dictionary Server),开源高性能键值对数据库。非关系型数据库(NoSQL)的一种,作为关系型数据库的一种补充支持,处理的数据面向内存,保证了大数据高并发下的性能
特点:数据间无必然联系、单线程机制、高性能、5种数据类型支持(字符串string、列表list、散列hash、集合set、有序集合类型sorted_set)、持久化支持。

应用:热点搜索、抢购排队、公交到站等及时信息、分布式数据共享、分布式锁

基础操作

set/get
clear//清楚屏幕显示
help//帮助指令,后接空格+tab键,给出可能想查看的推荐;
help @string//看string群组相关的所有方法
help set//跟command,查看该命令使用方法

二、数据类型

Redis中所有的key都只能是string类型,但value类型可以不同,常见的Redis数据类型:string、hash、list、set、sorted_set。

//key命名规范
	表名  : 主键名:  主键值 : 字段名
eg:order :  id  : 89757 : name 

set user:id:89757:bolgs 666
set user:id:89757:fans 888
or
set user:id:89757 {blogs:789,fans:888}

1.string类型

(1)string底层实现

Redis自己构建了一种简单动态字符串SDS。如使用set [key][value]的时候,key和value都是SDS。接下来我们尝试简单阅读源码(版本redis-7.0.11)
本人认为SDS和普通的C字串的最大区别在于,为了减少字符串增长、收缩或者拼接重新分配内存的次数,以及C字符串可能存在的缓存区溢出的可能性,SDS作为Redis自己构建的字符串,内存大小支持动态的变化,这要求SDS首先得时刻知道自己的字符串大小。

......
//SDS有许多不同的实现类型(在初始化的时候也可以看到)但基本内存布局是一致的,以下是其中一种sdshdr64
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; //包含着当前SDS存储字符串的大小
    uint64_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};

//SDS的初始化函数(只保留关注的代码)
sds _sdsnewlen(const void *init, size_t initlen, int trymalloc) {
	
	 sh = trymalloc?
        s_trymalloc_usable(hdrlen+initlen+1, &usable) :
        s_malloc_usable(hdrlen+initlen+1, &usable);//hrdlen是当前选择初始化的SDS类型在内存中的大小	
    sh->len = initlen;//通过结构体记录当前字符串大小
	s = (char*)sh+hdrlen;//字符串被记录的实际位置是当前申请内存偏移hdrlen个字节开始的
	s[initlen] = '\0';//和C字符串一致,以‘\0’作为结尾
    return s;
}
......

粗略地说,首先根据申请的字符串大小决定要使用的结构体(sdshdr64 、sdshdr32等),然后分配结构体大小+字符串长度大小+1的内存块。放入结构体字符串末尾补零后完成SDS初始化。(alloc表示字符串总共申请的大小,后期可用大小可以通过它与length的差值算得)

在此基础上看看Redis是如何使用SDS拼接字符串的

sds sdscat(sds s, const char *t) {
    return sdscatlen(s, t, strlen(t));
}

sds sdscatlen(sds s, const void *t, size_t len) {
    size_t curlen = sdslen(s);//获取SDS存储字符串的长度

    s = sdsMakeRoomFor(s,len);//为即将拼接字符串先确保足够的内存大小
    if (s == NULL) return NULL;
    memcpy(s+curlen, t, len);//从当前字符串之后开始拼接字符
    sdssetlen(s, curlen+len);//设置SDS记录字符串的长度
    s[curlen+len] = '\0';
    return s;
}
//sdsMakeRoomFor函数内部调用_sdsMakeRoomFor(只保留关注代码)
sds _sdsMakeRoomFor(sds s, size_t addlen, int greedy) {
   
    size_t avail = sdsavail(s);//返回可用大小    
    if (avail >= addlen) return s;//当前大小已经够用了,直接返回

    len = sdslen(s);
    sh = (char*)s-sdsHdrSize(oldtype);
    reqlen = newlen = (len+addlen);//当前大小和待添加大小相加得到请求大小
   
   //greedy为1表示要留后手,内存多申请些。为0则表示申请刚刚好即可
    if (greedy == 1) {
        if (newlen < SDS_MAX_PREALLOC)
            newlen *= 2;//请求的大小小于1Mb,新大小直接*2
        else
            newlen += SDS_MAX_PREALLOC;//大于等于1Mb,直接加1Mb
    }
    //判断类型是否相同,也就是SDS初始地址是否需要更换
    if (oldtype==type) {
        newsh = s_realloc_usable(sh, hdrlen+newlen+1, &usable);        
        s = (char*)newsh+hdrlen;
    } else {      
        newsh = s_malloc_usable(hdrlen+newlen+1, &usable);
        if (newsh == NULL) return NULL;
        memcpy((char*)newsh+hdrlen, s, len+1);       
        sdssetlen(s, len);
    } 
    sdssetalloc(s, usable);
    return s;
}

通过阅读以上代码可以看出,SDS在做拼接字符串的时候,考虑到字符串可能频繁得更改是做了预分配的优化的,这样带来的好处是当再次拼接字符串有较大概率现有内存满足需求,从而避免了频繁申请内存带来的性能开销。
同样的惰性释放空间也可以从一定角度解决上面提到的问题。

注意:string的最大存储大小512MB

(2)string常见指令/扩展指令

//常见指令,操作成功返回integer 1,失败返回integer 0
set [key] [value]
get [key]
del [key]
mset [key][value] [key][value] //一次性插入多个数据,和set指令比主要减少了多次请求往返耗时
mget [key][key][key]
strlen [key] //得到value字符长度
append [key]

//扩展指令,这些对string类型进行整数或者单精度的加减都是原子操作,内部转换为数值后进行计算
incr [key] //对数据进行整数+1,不支持ASCI转译
incrby [key] [increment] //increment只能是整数,负数也可以。按照指定的大小增加
incrbyfloat [key] [increment] //increment额外支持小数
decr [key] 
decrby [key] [increment] 

//设置数据生命周期,在生命周期期间重新set,会让时钟失效;但是只是append,时钟结束数据还是会丢失
setex [key][seconds][value]
psetex [key] [milliseconds] [value]

2.hash类型

将一个key对应一堆数据,其中一堆数据也是field-value的映射。
如果field数量较少,存储结构优化为类数组结构,如果数量较多,使用HashMap结构。
hash类型下只能存储string类型,不允许嵌套hash的操作。
与string的区别,一般string主要应用于JSON整体,读为主,hash主要应用于更新,但都不绝对。
在这里插入图片描述

(1)hash底层实现

(2)hash常见指令/扩展指令

//常见操作
hset key field value //设置或修改单个field数据
hget key field
hsetall key field//field和value全部返回,filed占据单数位置,**注意如果内部field过多,全部遍历极影响效率,会成为效率瓶颈**
127.0.0.1:6379> hgetall user01
1) "name"
2) "zhangjunbao"
3) "age"
4) "28"
5) "weight"
6) "65"
7) "height"
8) "178"
hdel key field [field ...]//可删除单个或多个field

hmset key field value [field value ...]
hmget key field [field ...] //此时返回的不带field
127.0.0.1:6379> hmget user01 name age
1) "zhangjunbao"
2) "28"
hlen key //返回field的个数
hexists key field //查看指定filed是否存在,存在返回1,否则0

//扩展操作
hkeys key //返回key所有的field,不可能重复
hvals key //返回key所有的value,可能重复
hincrby key filed increment //没有hdecrby,增加负数实现减
hincrbyfloat key filed increment
hsetnx key field value//filed存在不重复写入了

2.list类型

存储多个string类型数据,并对数据的顺序进行区分,底层使用双向链表存储结构实现。
具有索引的概念,可以使用index查询数,操作可以实现队列或者栈的功能;索引结束为-1。
可以日志实现消息队列等。

(1)list底层实现

(2)list常见指令/扩展指令

//常见操作
lpush key value [value ...] //从左边头插法添加val1、val2、val3...
rpush key value [value ...] //想要实现abc顺序填入就使用rpush即可
lrange key start stop //start开始索引,从0开始,忘记结束的索引可以写-1,代表倒数第一个
lindex key index //从左查看索引为index的value,没有rindex,从最后查看索引填-1即可
llen key //查看共有多少元素
lpop key //从左边弹出第一个元素
rpop key

//扩展操作
lrem key count value //从左往右移除指定个数的 value值

4.set类型

和hash很类似

(1)set底层实现

(2)set常见指令/扩展指令

//常见操作
sadd key member [member ...]
smembers key
srem key member [member ...]
scard key //获取集合数据总量
sismember key memeber //判断集合中是否包含指定数据

//扩展操作
 srandmember key [count] //随机给出set中指定个数的成员
 spop key [count] //随机弹出set中指定个数的成员
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值