redis的安装与string类型

1. redis的安装

1.1 升级gcc版本

因为centos7.x的gcc版本还是4.8.5,而编译指定的版本是需要5.3以上。

  1. 环境部署与安装scl源

yum install gcc cmake -y --部署安装环境
yum install centos-release-scl scl-utils-build -y --安装scl源
  1. 安装gcc新版本

yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils --安装gcc
​
scl enable devtoolset-9 bash  --更新gcc版本
​
gcc -v --查看gcc版本
1.2 安装redis
tar zxvf redis-6.2.6.tar.gz   --解压源码包
​
cd redis-6.2.6                --进入解压目录
​
mkdir /www/
mkdir /www/server
​
make && make install PREFIX=/www/server/redis          --执行安装命令
​
mkdir /www/server/redis/conf
cp redis.conf /www/server/redis/conf/
cp sentinel.conf /www/server/redis/conf/
vim /www/server/redis/conf/redis.conf

配置下Redis的配置文件:

protected-mode no
daemonize yes
appendonly yes
./bin/redis-server /www/server/redis/conf/redis.conf 启动redis服务
​
ps -ef | grep redis   查看redis进程
​
./bin/redis-cli   启动redis客户端
​
keys *  查看所有的key
127.0.0.1:6379> set name "xx"
OK
127.0.0.1:6379> get name
"starsky"
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> quit
​
​

2.3 配置管理shell文件

这个脚本与之前的nginx脚本一样,都是用来做管理软件之用,我们进入到目录/etc/init.d下:

touch redis

创建了文件redis之后,我们使用vim命令进行编辑写入下面的内容:

#! /bin/sh
# chkconfig: 2345 55 25
​
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
NAMESERVER=redis-server
NAME=redis
REDIS_BIN=/www/server/redis/bin/$NAMESERVER
CONFIGFILE=/www/server/redis/conf/$NAME.conf
PIDFILE=/var/run/redis_6379.pid
ulimit -n 8192
​
case "$1" in
    start)
        echo -n "Staring $NAME..."
                if [ -f $PIDFILE ]; then
                        mPID=`cat $PIDFILE`
                        isStart=`ps ax | awk '{ print $1 }' | grep -e "^${mPID}$"`
                        if [ "$isStart" != '' ]; then
                                echo -e "\e[31m$NAME ( pid `pidof $NAMESERVER`) already running.\e[0m"
                                exit 1
                        fi
                fi
        $REDIS_BIN $CONFIGFILE
        if [ "$?" != 0 ] ; then
            echo -e "\e[31mfailed\e[0m"
            exit
        else
            echo -e "\e[32msuccess\e[0m"
        fi
        ;;
    stop)
        echo -n "Stoping $NAME..."
                if [ -f $PIDFILE ];then
                        mPID=`cat $PIDFILE`
                        isStart=`ps ax | awk '{ print $1 }' | grep -e "^${mPID}$"`
                        if [ "$isStart" = '' ];then
                                echo -e "\e[31m$NAME is not running.\e[0m"
                                exit 1
                        fi
                fi
                mPID=`cat $PIDFILE`
                kill -9 $mPID
​
                if [ "$?" != 0 ] ; then
                    echo -e "\e[31mfailed. Use force-quit\e[0m"
                    exit 1
                else
                    echo -e "\e[32mdone\e[0m"
                fi
                ;;
    status)
    if [ -f $PIDFILE ];then
                        mPID=`cat $PIDFILE`
                        isStart=`ps ax | awk '{ print $1 }' | grep -e "^${mPID}$"`
                        if [ "$isStart" != '' ];then
                                echo -e "\e[36m$NAME ( pid `pidof $NAMESERVER`) already running.\e[0m"
                                exit 1
                        else
                                echo -e "\e[31m$NAME is stopped\e[0m"
                                exit 0
                        fi
                else
                        echo -e "\e[31m$NAME is stopped\e[0m"
                        exit 0
                fi
                ;;
    restart)
        $0 stop
        sleep 1
        $0 start
        ;;
    *)
        echo -e "Usage: $0 {\e[36mstart|stop|restart|status\e[0m}"
        exit 1
    ;;
esac
 #>

测试脚本:

env: /etc/init.d/redis: 权限不够
[root@localhost init.d]# chmod 777 /etc/init.d/redis
[root@localhost init.d]# chkconfig -add /etc/init.d/redis
​
service redis start  # 启动redis
ps -ef | grep redis  # 查看redis进程是否存在,存在则已经启动
​
service redis stop   # 停止redis
ps -ef | grep redis  # 查看redis进程是否存在,不存在则已经停止
​
service redis status # 查看redis状态信息
​

2. string数据类型

2.1 类型介绍

String类型是redis的最基础的数据结构,也是最经常使用到的类型。而且其他的四种类型多多少少都是在字符串类型的基础上构建的,所以String类型是redis的基础。

string 类型的值最大能存储 512MB,这里的String类型可以是简单字符串、复杂的xml/json的字符串、二进制图像或者音频的字符串、以及可以是数字的字符串。

set 命令

描述:该命令用于设置给定 key 的值。如果 key 已经存储其他值, SET 就覆写旧值,且无视类型

127.0.0.1:6379> set name xx
OK
127.0.0.1:6379>

通过set命令给restkey这个key值绑定value,当SET在设置操作成功完成时,才返回 OK

get 命令

描述:该命令用于获取指定 key 的值。如果 key不存在,返回 nil 。如果key对应储存的值不是字符串类型,返回一个错误。

127.0.0.1:6379> get name
"xx"
127.0.0.1:6379>

getset命令

描述:该命令用于获取指定的key的旧值,然后按照新值对key进行赋值。当key中没有旧值的时候返回nil。

127.0.0.1:6379> getset name aa
"bb"
127.0.0.1:6379> get name
"aa"
127.0.0.1:6379>

mget 命令

描述:该命令用于返回多个key的值,当其中某一个KEY的值不存在,返回nil

127.0.0.1:6379> set age 10
OK
127.0.0.1:6379> set sex 1
OK
127.0.0.1:6379> mget name age sex
1) "aa"
2) "10"
3) "1"
127.0.0.1:6379>

decr 命令

描述:对key对应的数字做减1操作。如果key不存在,那么在操作之前,这个key对应的值会被置为0。如果key有一个错误类型的value或者是一个不能表示成数字的字符串,就返回错误

127.0.0.1:6379> decr age
(integer) 9
127.0.0.1:6379> get age
"9"
127.0.0.1:6379>

incr 命令

描述:对存储在指定key的数值执行原子的加1操作,如果指定的key不存在,那么在执行incr操作之前,会先将它的值设定为0。如果指定的key中存储的值不是字符串类型(fix:)或者存储的字符串类型不能表示为一个整数,那么执行这个命令时服务器会返回一个错误(eq:(error) ERR value is not an integer or out of range)。

127.0.0.1:6379> get age
"9"
127.0.0.1:6379> incr age
(integer) 10
127.0.0.1:6379>

更多string命令,参考这里:http://www.redis.cn/commands.html#string

2.2 string简单字符结构
2.2.1 SDS动态字符串

SDS(Simple Dynamic Strings, 简单动态字符串)是 Redis 的一种基本数据结构,主要是用于存储字符串和整数。

SDS数据结构实现(Redis3):

struct sdshdr {
    unsigned int len;
    unsigned int free;
    char buf[];
};

其中,buf 表示数据空间,用于存储字符串;len 表示 buf 中已占用的字节数,也即字符串长度;free 表示 buf 中剩余可用字节数。

好处

  • 用单独的变量 len 和 free,可以方便地获取字符串长度和剩余空间;

  • 内容存储在动态数组 buf 中,SDS 对上层暴露的指针指向 buf,而不是指向结构体 SDS。因此,上层可以像读取 C 字符串一样读取 SDS 的内容,兼容 C 语言处理字符串的各种函数,同时也能通过 buf 地址的偏移,方便地获取其他变量;

  • 读写字符串不依赖于 \0,保证二进制安全。

坏处

对于不同长度的字符串,没有必要使用 len 和 free 这 2 个 4 字节的变量?

4 字节的 len,可表示的字符串长度为 2^32,而在实际应用中,存放于 Redis 中的字符串往往没有这么长,因此,空间的使用上能否进一步压缩?

2.2.2 新的SDS结构

Redis 增加了一个 flags 字段来标识类型,用一个字节(8 位)来存储。

其中:前 3 位表示字符串的类型;剩余 5 位,可以用来存储长度小于 32 的短字符串。

struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 前3位存储类型,后5位存储长度 */
    char buf[]; /* 动态数组,存放字符串 */
};

而对于长度大于 31 的字符串,仅仅靠 flags 的后 5 位来存储长度明显是不够的,需要用另外的变量来存储。sdshdr8、sdshdr16、sdshdr32、sdshdr64 的数据结构定义如下,其中 :

  • len 表示已使用的长度

  • alloc 表示总长度

  • buf 存储实际内容

  • flags 的前 3 位依然存储类型,后 5 位则预留。

struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* 已使用长度,1字节 */
    uint8_t alloc; /* 总长度,1字节 */
    unsigned char flags; /* 前3位存储类型,后5位预留 */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* 已使用长度,2字节 */
    uint16_t alloc; /* 总长度,2字节 */
    unsigned char flags; /* 前3位存储类型,后5位预留 */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; /* 已使用长度,4字节 */
    uint32_t alloc; /* 总长度,4字节 */
    unsigned char flags; /* 前3位存储类型,后5位预留 */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; /* 已使用长度,8字节 */
    uint64_t alloc; /* 总长度,8字节 */
    unsigned char flags; /* 前3位存储类型,后5位预留 */
    char buf[];
};

Redis创建字符串流程

sds sdsnewlen(const void *init, size_t initlen) {
    void *sh;
    sds s;
    // 根据字符串长度计算相应类型
    char type = sdsReqType(initlen);
    // 如果创建的是""字符串,强转为SDS_TYPE_8
    if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
    // 根据类型计算头部所需长度(头部包含 len、alloc、flags)
    int hdrlen = sdsHdrSize(type);
    // 指向flags的指针
    unsigned char *fp;
    // 创建字符串,+1是因为 `\0` 结束符
    sh = s_malloc(hdrlen+initlen+1);
    if (sh == NULL) return NULL;
    if (init==SDS_NOINIT)
        init = NULL;
    else if (!init)
        memset(sh, 0, hdrlen+initlen+1);
    // s指向buf
    s = (char*)sh+hdrlen;
    // s减1得到flags
    fp = ((unsigned char*)s)-1;
    ...
    // 在s末尾添加\0结束符
    s[initlen] = '\0';
    // 返回指向buf的指针s
    return s;
}

创建 SDS 的大致流程是这样的:首先根据字符串长度计算得到 type,根据 type 计算头部所需长度,然后动态分配内存空间。

注意:

  1. 创建空字符串时,SDS_TYPE_5 被强制转换为 SDS_TYPE_8(原因是创建空字符串后,内容可能会频繁更新而引发扩容操作,故直接创建为 sdshdr8)

  2. 长度计算有 +1 操作,因为结束符 \0 会占用一个长度的空间。

  3. 返回的是指向 buf 的指针 s。

2.3 使用场景

1.主页高频访问信息显示限制,例如新浪、微博大V主页显示粉丝数与微博数量

2.限流限速

3.Session集中管理

4.验证码

3.与openresty结合做限流

Openresty介绍: OpenResty是一个基于Nginx与Lua的高性能web平台,由中国人章亦春发起, 其内部集成了大量精良的Lua库、第三方模块以及大多数的依赖项。用于方便搭 建能处理超高并发、扩展性极高的动态Web应用、web服务和动态网关

OpenResty简单理解成就相当于封装了NGINX,并且集成了LUA脚本,开发人 员只需要简单的使用其提供了模块就可以实现相关的逻辑,而不像之前,还需 要在NGINX中编写lua的脚本

3.1 Openresty安装:

1.安装包地址: https://github.com/openresty/openresty/archive/refs/tags/v1.19 .9.1.tar.gz

2.解压安装包: tar –zxvf openresty-1.19.9.1.tar.gz

3.安装: make && make install PREFIX=/www/server/openresty

local function wait()

        ngx.sleep(1)

end


3.2 使用
function limit_check()
local redis = require "resty.redis"
local redisConnect = redis:new()
local ip = "127.0.0.1"
local port = 6379
local password = "root"
local ok , err = redisConnect:connect(ip,port)
if not ok then
ngx.say( '{"code": 6379,"msg": "系统错误,请稍后
再试!"}',err )
ngx.exit(200)
return
end
3.3 缓存 
ocal uri = ngx.var.uri
local uriKey = "req:uri:"..uri
res , err = redisConnect:eval("local res , err = redis.call('incr',KEYS[1]) if res == 1 then 
local resexpire , err = redis.call('expire',KEYS[1],KEYS[2]) end return (res)",2,uriKey,1)
if res > 5 then
return false
else
return true
end
end
3.4 调用
restful = limit_check()
if not restful then
ngx.say( '{"code": 400,"msg": "request is error !"}' )
ngx.exit(200);
return
end

  • 22
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值