2.Redis工作机制和编译安装.md

redis特性

速度快:10W QPS ,基于内存,C语言实现

单线程

持久化

支持多种数据结构

支持多种编程语言

功能丰富:支持Lua脚本,发布订阅,事务,pipline等功能

简单:代码短小精悍(单机核心代码只有23000行左右),单线程开发容易,不依赖外部库,使用简单

主从复制

支持高可用和分布式

redis的单线程

6.0版本之前都是单线程:开一个独立的线程 处理用户的请求,一次性在同一时间一次性只能接收一个指令的操作。

image-20241225170336410

get:好比mysql的select

set:类似mysql的update

单线程速度也非常快 [纯内存、非阻塞、无线程间切换、Epoll实现IO多路复用 ]:10W QPS,因为是基于内存且是C写的。是微秒级

而且单线程还避免了 阻塞问题 mysql里有讲过,mysql有一个锁机制,行级锁和表级锁。有人更新行/表,第二个人就得等待,这就是阻塞。而redis本身就是单线程的,就省掉了多线程的加锁/解锁过程。

也避免了多线程之间的切换过程,

Epoll是I/O多路复用模型,即使单线程也可以在I/O层面做到并发。

并发:一起发出去请求,但是执行可能就是处理的够快,或者有等待 队列机制 来支撑 请求方的并发。

并行:那个时间点的同时进行。

缺点:

单线程,只能工作在一核上,其余的多核就浪费了。应对这种,可以用redis多实例来实现多线程?

其实还有超线程,还有httpd的三种工作模型,还有和cpu核绑定的都是线程。还有我之前端口扫描的多进程和多线程双重并发。

一般内存16G 32G,再大,就拆开来多个redis实例来弄了,多个redis实例,分别各自是单线程的,然后绑到CPU多个核上去。比如有一个很大的数据100G,就推荐用4个redis实例分别绑到不同的核上来处理。

关于redis的单线程进一步说明

image-20241227112427980

还有redis的单线程是 对接用户数据的时候的单线程处理,是读写操作的单线程;其他环节不是单线的:

1、数据的持久化:就需要redis单开一个另外的线程来独立完成

2、主从复制,也单开线程

3、等等,所整体来讲redis是多线程的。

4、一般提到redis就说单线程,指的是用户请求的读写操作的单线程。

image-20241227110421486

这个处理用户请求的就是单线程,所以个get的时间就不能太长

image-20241227110553817

一般处理这些get set sadd hget del 都很快,但确实也有耗时长的时候,就像mysql中select * from large_table;这种大表全表扫描一下就很慢了。

redis也有这种全表查看的动作,这种操作必然会照成性能下降,比如32G的内存,全扫一遍,肯定快不了啦。

所以生成中 禁止 执行(慢)命令

image-20241227110900509

keys *就是说key vlues了,要给动作耗时太长,在实际业务逻辑中可能就会调度查询到其他DB比如请求动作处理太久(判定为redis没有该数据),就让他查mysql去了。

1、优先访问redis

2、redis查找不到了(必然有一个timeout判定的,不然你查一辈子~),就访问mysql

3、redis很多时候作为缓存存在的。

4、redis的性能可达到10W并发,mysql是2K并发,结果redis的一个timeout导致10W并发都打到mysql上去了。mysql必然崩了。

redis 对比 Memcached

image-20241227112310949

memcached 比 redis 唯一一个优点就是块60W的QPS,但是其他各个方面都是弱的较之redis。

redis的应用场景是非常多的

image-20241227113948976

image-20241227114202813

image-20241227114220194

image-20241227114237240

缓存的实现流程

1、写操作,先往mysql里写,一旦写成功了,就会同步到redis里

2、读操作,先到redis里读,一旦读不到,就会去mysql里找,mysql里找到了,就会同步一份给redis。

image-20241227142246463

缓存穿透,缓存击穿和缓存雪崩

穿透

缓存和数据库中 都没有的数据,而用户不断发起请求,比如:发起id为"-1"的数据或id为特别大不存在的数据,这时的用户很可能是攻击者,攻击会导致数据库压力过大。

解放方法:

​ 接口层增加校验,入用户鉴权校验,id做基础校验,id<=0的直接拦截

​ 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30s(设置太长会导致正常情况也没法使用),这样额可以防止攻击用户反复用同一个id暴力攻击。

# 这个key-null很有意思的,key-null必须结合30s这种短时控制,以达到如下效果:

黑客查询id=666的这种实际不存在的商品数据,大量高频请求,为了防止请求到达后端mysql,于是在redis里设置key-null这种键值对,让redis直接有了一个结果返给请求方,并且为了防止id=666的数据后面真的来了,所以30s以后id=666的key-null就老化掉了,于是redis没有数据,就又会转发给mysql了,此时就实现了类似惩罚机制---sw登入多次30s的一个禁止登入一样就有点像。

击穿

缓存中没有,但数据库中有的数据,比如:热点数据的缓存时间到期后,这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库取数据,引起数据库压力瞬间增大,造成业务无法正常响应。

解决方法:

​ 设置热点数据永远不过期。

​ # 个人解读: 乍一看,肯定想 这是错误的思维方式,没有永远不过期的热点数据,也许热点只是不同数据的标签而已,数据A可以携带热点标签大概3个月,或者一直持续保持热度的话题,也许这就是热点数据永不过期的另一层含义了吧。

雪崩

缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是 不同数据都过期了,很多数据都查不到从而查数据库。

image-20241227150708632

解决方法: 缓存数据的过期时间设置随机,防止同一时间大量数据一起过期现象发生

​ 如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中

​ 设置热点数据永不过期。

redis内置了pipeline的功能

jenkins、cicd、gitrunner都会提到流水线pipeline

redis客户端执行一条命令分4个过程:

发送命-->命令排队-->命令执行-->返回结果

这个过程称为RTT(Round trip time 往返时间),mget,mset指令可以一次性的批量对多个数据的只能怪操作,所以有效节约了RTT;但大部分命令(如hgetall)不支持批量操作,需要消耗N次RTT,利用Pipeline技术可以解决这一问题

image-20241227152805909

发送命令--通常走网络传递过去,返回结果--也基本都是互联网传递回来,上海到宁波的也有20ms呢吧,而redis处理也就10ms不要基本就结束了。时间大量消耗在路上了。

如果提高用户体验呢,就是pipeline了,思路就是本来10个命令,一个个发送过去,现在10个命令合成1个执行直接发送过去,这里就是节省了①tcp三次握手的时间②数据报文的头部消耗提高了数据传输的正式有效DATA的容量比③我想到了再来编O(∩_∩)O,哦返回也是10个指令的结果合并一起返回了。④还一个redis处理四个环节,本来是10个命令,就是40次,现在变成一个命令块,也就是4次 : 发送 -- 排队 -- 执行(命令块) --返回

image-20241227154058713

image-20241227154354427

上表说明:延迟列代表 3个环境本地、内网、异地下的用户到redis之间的网络延迟。然后在这种网络延迟下 有pipeline和无pipeline的性能差异是非常大的。

redis安装

https://redis.io/docs/getting-started/installation/

https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/

官网7.4的时候,yum info才6.2

编译安装

https://download.redis.io/releases/

这个可以看到各个版本明细

rc版本不要用,是候选版本也就是测试版

image-20241227163503157

https://redis.io/docs/getting-started/installation/install-redis-from-source/

https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-from-source/

# ubuntu
apt update & apt -y install make gcc libjemalloc-dev libsystemd-dev

# centos rocky
yum -y install gcc make jemalloc-devel systemd-devel

wget https://download.redis.io/releases/redis-7.4.1.tar.gz
tar xvf redis-7.4.1-tar.gz

cd redis-7.4.1
make -j 16 PREFIX=/apps/redis install
# 支持systemd,需要加选项👇
make -j 16 PREFIX=/apps/redis install USE_SYSTEMD=yes

# redis的源码安装无需./configure和make,就是直接make install 👆

echo 'PATH=/apps/redis/bin:$PATH' > /etc/profile.d/redis.sh
. /etc/profile.d/redis.sh

# 创建配置文件、日志、数据等目录
mkdir /apps/redis/{etc,log,data,run}
cp redis.conf /apps/redis/etc/


# 启动文件叫 redis-server
# 也提供了客户端 redis-cli

image-20241227170853074

image-20241227171002772

上图三个WARNING是优化,是系统没有针对redis做优化导致的,希望redis性能更好,就优化掉。

redis默认是6379端口,但是默认是127.0.0.1的,所以不支持远程连接。

image-20241227171255697

新版本里三个WARNING不一定有了,处理方式如下👇

image-20241227171349637

image-20241227171506104

image-20241227171657197

image-20241227171712172

image-20241227171633511

image-20241227171942783

image-20241227172005995

👆编译的时候加上了systemd了,所以service文件就可以加上这个选项了

type=notify,redis 可以通过systemd来发送信号通知,就需要type类型为notify,必须是支持systemd才能写notify,如果不支持systemd就不写,或者写个simple。

性能优化:LimitNOFILE=xxxx,打开文件个数。

image-20241227172725003

redis安装脚本

#!/bin/bash
#
#********************************************************************
#
#********************************************************************

#本脚本支持在线和离线安装

REDIS_VERSION=redis-7.4.1
#REDIS_VERSION=redis-7.0.7
#REDIS_VERSION=redis-7.0.3
#REDIS_VERSION=redis-6.2.6
#REDIS_VERSION=redis-4.0.14

# redis推荐设置密码,不配也没关系
PASSWORD=123456
INSTALL_DIR=/apps/redis

CPUS=`lscpu |awk '/^CPU\(s\)/{print $2}'`

. /etc/os-release

color () {
    RES_COL=60
    MOVE_TO_COL="echo -en \\033[${RES_COL}G"
    SETCOLOR_SUCCESS="echo -en \\033[1;32m"
    SETCOLOR_FAILURE="echo -en \\033[1;31m"
    SETCOLOR_WARNING="echo -en \\033[1;33m"
    SETCOLOR_NORMAL="echo -en \E[0m"
    echo -n "$1" && $MOVE_TO_COL
    echo -n "["
    if [ $2 = "success" -o $2 = "0" ] ;then
        ${SETCOLOR_SUCCESS}
        echo -n $"  OK  "    
    elif [ $2 = "failure" -o $2 = "1"  ] ;then 
        ${SETCOLOR_FAILURE}
        echo -n $"FAILED"
    else
        ${SETCOLOR_WARNING}
        echo -n $"WARNING"
    fi
    ${SETCOLOR_NORMAL}
    echo -n "]"
    echo 
}


prepare(){
    if [ $ID = "centos" -o $ID = "rocky" ];then
        yum  -y install gcc make jemalloc-devel systemd-devel
    else
        apt update 
        apt -y install  gcc make libjemalloc-dev libsystemd-dev
    fi
    if [ $? -eq 0 ];then
        color "安装软件包成功"  0
    else
        color "安装软件包失败,请检查网络配置" 1
        exit
    fi
}

install() {   
    if [ ! -f ${REDIS_VERSION}.tar.gz ];then
        wget http://download.redis.io/releases/${REDIS_VERSION}.tar.gz || { color "Redis 源码下载失败" 1 ; exit; }
    fi
    tar xf ${REDIS_VERSION}.tar.gz  -C /usr/local/src
    cd /usr/local/src/${REDIS_VERSION}
    make -j $CUPS USE_SYSTEMD=yes PREFIX=${INSTALL_DIR} install && color "Redis 编译安装完成" 0 || { color "Redis 编译安装失败" 1 ;exit ; }

    ln -s ${INSTALL_DIR}/bin/redis-*  /usr/local/bin/
    # 放在/usr/local/bin里比 /usr/bin里要好,因为/usr/bin都是系统的。而local/bin通常都是空的,后面自己的东西放里面比较清爽。

    mkdir -p ${INSTALL_DIR}/{etc,log,data,run}

    cp redis.conf  ${INSTALL_DIR}/etc/

    # 这是安装的时候顺便修改配置文件了,首次学习接触,可以先注释掉下面的sed语句:
    #sed -i -e 's/bind 127.0.0.1/bind 0.0.0.0/'  -e "/# requirepass/a requirepass $PASSWORD"  -e "/^dir .*/c dir ${INSTALL_DIR}/data/"  -e "/logfile .*/c logfile ${INSTALL_DIR}/log/redis-6379.log"  -e  "/^pidfile .*/c  pidfile ${INSTALL_DIR}/run/redis_6379.pid" ${INSTALL_DIR}/etc/redis.conf


    if id redis &> /dev/null ;then 
         color "Redis 用户已存在" 1 
    else
         useradd -r -s /sbin/nologin redis
         color "Redis 用户创建成功" 0
    fi

    chown -R redis.redis ${INSTALL_DIR}


    # 这是3个WARNING的优化,首次学习redis,可以先注释掉:
    #cat >> /etc/sysctl.conf <<EOF
#net.core.somaxconn = 1024
#vm.overcommit_memory = 1
#EOF
    #sysctl -p 
    #if [ $ID = "centos" -o $ID = "rocky" ];then
    #    echo 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' >> /etc/rc.d/rc.local
    #    chmod +x /etc/rc.d/rc.local
    #    /etc/rc.d/rc.local 
    #else 
    #    echo -e '#!/bin/bash\necho never > /sys/kernel/mm/transparent_hugepage/enabled' >> /etc/rc.local
    #    chmod +x /etc/rc.local
    #    /etc/rc.local
    #fi


cat > /lib/systemd/system/redis.service <<EOF
[Unit]
Description=Redis persistent key-value database
After=network.target

[Service]
ExecStart=${INSTALL_DIR}/bin/redis-server ${INSTALL_DIR}/etc/redis.conf --supervised systemd
ExecStop=/bin/kill -s QUIT \$MAINPID
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755
LimitNOFILE=1000000

[Install]
WantedBy=multi-user.target

EOF
     systemctl daemon-reload 
     systemctl enable --now  redis &> /dev/null 
     if [ $? -eq 0 ];then
         color "Redis 服务启动成功,Redis信息如下:"  0 
     else
        color "Redis 启动失败" 1 
        exit
     fi
     sleep 2
     redis-cli -a $PASSWORD INFO Server 2> /dev/null
}

prepare 

install

运行上面的安装脚本,安装成功,并且该脚本也显示了redis的信息👇

image-20241227174332816

观察日志

image-20241227175158876

没有做优化,所以监听在127上的

image-20241227175324747

下一篇再 继续 优化之类

Copyright 🌹 © oneyearice@126.com 2022 all right reserved,powered by Gitbook文档更新时间: 2024-12-30 09:48:38

results matching ""

    No results matching ""