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 的一种基本数据结构,主要是用于存储字符串和整数。

redis 存的 stringset 如何取_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