Redis基础安装以及基本数据结构实战核心揭秘

217 阅读10分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

redis 安装

# 安装gcc
yum install gcc
## 下载
cd /usr/local/
wget https://download.redis.io/releases/redis-6.2.6.tar.gz
tar -zxvf redis-6.2.6.tar.gz
cd redis-6.2.6/
make
## 到此已经安装完成
## 备份配置文件
cp redis.conf redis.conf_bak

## 修改配置文件
daemonize yes ## 后台启动
protected-mode no ## 关闭保护模式,开启的话只有本机才可以访问redis
## 注释掉bind 127.0.0.1,bind绑定的是自己机器网卡的ip,如果有多块网卡可以配多个ip,代表允许客户端通过机器的哪些网卡ip去访问,内网一般可以不配置bind,注释掉即可
# bind 127.0.0.1 

## 启动服务

./src/redis-server ./redis.conf

## 验证是否启动
ps -ef|grep redis

## redis客户端连接
./src/redis-cli

## 退出redis客户端
control+c

redis 数据结构

五种基本数据结构

  • 字符串 string
  • 哈希 hash
  • 列表 list
  • 集合 set
  • 有序集合 zset

image-20220315162100009

高级数据类型

  • 位图 bitmaps
  • HyperLogLog
  • 地理位置 GEO

这里不做过多说明,后续单独讲解。

String 结构

字符串常用操作

SET key value  # 设置字符串键值对
GET key # 获取一个字符串key对应的键值
MSET key value [key value ...] # 批量设置键值对
MGET key [key ...] # 批量获取字符串key对应的键值
SETNX key value # 存入一个不存在的键值对,只有键不存在时才能存入成功
DEL key [key ...] # 删除
EXPIRE key seconds # 设置一个键的过期时间

原子加减

INCR key # 将key中存储的数字加1
DECR key # 将key中存储的数字减1
INCRBY key increment # 将key中存储的值加上increment
DECRBY key decrement # 将key中存储的key减去decrement

String 应用场景

  • 单值缓存

    SET key value / GET key
    
  • 对象缓存

    # 常用命令
    SET key value(json)  
    get key # (反序列化)
    # 批量操作
    MSET user:1:name beifeng user:1:age 18 
    MGET user:1:name user:1:age
    
  • 分布式锁

    SETNX  lesson:1 true # 返回1代表获取锁成功,0代表获取锁失败
    # 执行业务逻辑
    DEL lesson:1
    # 原子操作,防止程序意外终止而导致死锁
    SET lesson:1 true ex 10 nx 
    
  • 计数器

    # 统计文章阅读数量 
    incr readcount  
    get readcount
    
  • web 集群 session 共享

    spring session + redis实现session共享
    
  • 分布式系统全局序列号

    # 批量获取一批ID,程序内部维护自增逻辑,用完了获取下一批,避免频繁请求redis
    INCRBY lessonid 1000
    

hash 结构

hash 常用操作

HSET key field value [field value ...] # 存储一个hash表key的值,支持批量操作
HMSET key field value [field value ...] # 在一个hash表key中批量存储多个键值对
HGET key field # 获取hash表中key对应的filed的键值
HMGET key field [field ...] # 批量获取hash表中key对应的多个filed的键值
HSETNX key field value # 存储一个在hash表中不存在的filed的值
HDEL key field [field ...] # 删除hash表中的filed的键值,支持批量操作
HLEN key # 返回hash表中field的数量
HGETALL key # 返回hash表key中所有的键值
HINCRBY key field increment # hash表中key的field键对应的值加上增量 increment

hash 应用场景

  • 对象缓存

    HSET user 1:name beifeng 1:age 20 
    HMGET user 1:name 1:age
    
  • 电商购物车

    # 以用户ID为key : 商品ID为field,商品数量为field
    # 购物车操作:添加商品
    hset cart:999 100 1
    # 增加商品数量 
    hincrby cart:999 100 1
    # 商品总数 
    hlen cart:999
    # 删除商品 
    hdel cart:999 100
    # 获取购物车所有商品 
    hgetall cart:999
    

hash 结构的优缺点

优点

  • 同类数据归类整合存储,方便数据管理
  • 相比 string 操作消化内存与 cpu 更小
  • 相比 string 更节省存储空间

缺点

  • 过期功能不能用在 field 上,只能用在 key 上
  • redis 集群架构不适合大规模使用

list 结构

list 常用操作

LPUSH key element [element ...] # 将一个或多个值插入到key列表的表头(最左边)
RPUSH key element [element ...] # 将一个或多个值插入到key列表的表位(最右边)
LPOP key [count] # 移除并且返回 key 对应的 list 的第一个元素。[count] 指定弹出的数量
RPOP key [count] # 移除并返回存于 key 的 list 的最后一个元素。[count] 指定弹出的数量
LRANGE key start stop # 返回列表key中指定区间内的元素,区间以偏移量start和stop指定 stop=-1 返回全部
BLPOP key [key ...] timeout # 从key列表表头弹出一个元素,若列表中没有元素,阻塞等待timeout秒,timeout=0,一直阻塞
BRPOP key [key ...] timeout # 从key列表表尾弹出一个元素,若列表中没有元素,阻塞等待timeout秒,timeout=0,一直阻塞

list 应用场景

  • stack

    LPUSH+LPOP

  • QUEUE 对列

    LPUSH+RPOP

  • Blocking MQ 阻塞队列

    LPUSH+BRPOP

  • 微博和公众号的信息流

  • 微博消息和公众号消息

    # 用户A关注了 V1,V2 等大 V
    # V1 发微博,消息ID为 6666
    LPUSH msg:user-A 6666
    # V2 发微博,消息ID为 6667
    LPUSH msg:user-A 6667
    # A 用户查看最新的消息
    LRANGE msg:user-A 0 4
    

Set 结构

Set常用操作

SADD key member [member ...] # 往集合key中加入元素,元素存在则忽略,若key不存在则新建
SREM key member [member ...] # 从集合key中删除元素
SMEMBERS key # 获取集合key中所有元素
SCARD key # 获取集合key的元素个数
SISMEMBER key member # 判断member元素是否存储在集合key中
SRANDMEMBER key [count] # 从集合key中选出count个元素,元素不从key中删除 ,不指定count默认1个
SPOP key [count] # 从集合key中选出count个元素,元素从key中删除,不指定count默认1个

Set 运算操作

SINTER key [key ...] # 交集运算
SINTERSTORE destination key [key ...] # 将交集运算结果存入新集合destination中
SUNION key [key ...] # 并集运算
SUNIONSTORE destination key [key ...] # 将并集结果存入新集合destination中
SDIFF key [key ...] # 差集运算,以第一个key为基准,减去后面集合的并集
SDIFFSTORE destination key [key ...] # 将差集结果存入新集合destination中

Set 应用场景

  • 微信抽奖小程序

    # 参与抽奖的人加入集合
    SADD key {userlD}
    # 查看所有参与抽奖的用户
    SMEMBERS key
    # 抽取count名中奖者
    SRANDMEMBER key [count] / SPOP key [count]
    
  • 微信微博点赞、收藏、标签

    # 点赞
    SADD like:{消息ID} 用户ID
    # 取消点赞
    SREM like:{消息ID} 用户ID
    # 检查用户是否点赞过
    SISMEMBER like:{消息ID} 用户ID
    # 获取点赞的用户列表
    SMEMBERS like:{消息ID}
    # 获取点赞的用户数
    SCARD like:{消息ID}
    
  • 集合之间的操作,交集、差集、并集

  • 集合操作实现微博微信关注模型

    # 蜘蛛侠关注的人
    zhizhuxia-set -> {gangtiexia,mieba}
    # 钢铁侠关注的人
    gangtiexia-set -> {meiguoduizhang,heibao,yingyan,mieba}
    # 奇异博士关注的人
    qiyiboshi-set -> {heiguafu,jinganglang,zhizhuxia}
    # 蜘蛛侠个钢铁侠共同关注的人 --> 取两个集合的交集
    SINTER zhizhuxia-set gangtiexia-set -> {mieba}
    # 蜘蛛侠可能认识的人
    SDIFF gangtiexia-set zhizhuxia-set
    # 蜘蛛侠关注的人也关注他,钢铁侠是否也关注了灭霸
    SISMEMBER gangtiexia-set mieba
    
  • 集合操作实现电商商品筛选

    image-20220317153838597

    SADD  brand:huawei  P40
    SADD  brand:xiaomi  mi-10
    SADD  brand:iPhone iphone12
    SADD os:android  P40  mi-10
    SADD cpu:brand:intel  P40  mi-10
    SADD ram:8G  P40  mi-10  iphone12
    # 交集
    SINTER  os:android  cpu:brand:intel  ram:8G ->  {P40,mi-10}
    

Zset 有序集合结构

Zset 常用操作

ZADD key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...] # 往有序集合key中添加带分值的元素
ZCARD key # 返回有序集合key中元素的个数
ZREM key member [member ...] # 删除有序集合key中的元素member
ZSCORE key member # 返回有序集合key中元素member的分值
ZINCRBY key increment member # 为有序集合key中元素member加上increment分值
ZCARD key # 返回有序集合key中元素个数
ZRANGE key min max [BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES] # 正序获取有序集合key中分值从min到max的元素
ZREVRANGE key start stop [WITHSCORES] # 倒序获取有序集合key从start下标到stop下标的元素

Zset 集合操作

ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX] # 并集操作
ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX] # 交集操作

Zset 集合应用

  • Zset 集合操作实现排行榜

    # 点击新闻
    ZINCRBY  hotNews:20220317  1 乌克兰
    # 展示当日排行前十
    ZREVRANGE  hotNews:20190819  0  9  WITHSCORES
    # 七日搜索榜单计算
    ZUNIONSTORE  hotNews:20190813-20190819  7
    # 展示七日排行前十
    ZREVRANGE hotNews:20190813-20190819  0  9  WITHSCORES
    

其他高级命令

  • 查看redis支持的最大连接数

     CONFIG GET maxclients
    
  • 全量遍历键,keys 用来列出所有满足特定正则字符串规则的 key,当 redis 数据量比较大时,性能比较差,要避免使用。

    keys * 
    keys match*
    
  • scan 渐进式遍历键。注意:scan 并非完美无瑕,如果在遍历的过程中有键的变化(增加、修改、删除),那么遍历可能会碰到如下问题

    • 新增的键可能没有遍历到
    • 遍历出了重复的键的情况
    SCAN cursor [MATCH pattern] [COUNT count] [TYPE type] 
    # cursor 整数值,hash桶的索引值
    # MATCH pattern key 的正则表达式
    # COUNT 一次遍历的key的数量,参考值,底层遍历数量不一定,并不是符合条件的结果数量
    # 第一次遍历时 cursor 值为 0,然后将返回结果中第一个整数作为下一次遍历的 cursor。
    # 一直遍历到返回的 cursor 值为0时结束。
    
  • info 查看 redis 服务运行信息,总共9大块

    Server 服务器运行的环境参数 
    Clients 客户端相关信息 
    Memory 服务器运行内存统计数据 
    Persistence 持久化信息 
    Stats 通用统计数据 
    Replication 主从复制相关信息 
    CPU CPU 使用情况 
    Cluster 集群信息 
    KeySpace 键值对统计数量信息
    ####### 
    connected_clients:2                  # 正在连接的客户端数量
    
    instantaneous_ops_per_sec:789        # 每秒执行多少次指令
    
    used_memory:929864                   # Redis分配的内存总量(byte),包含redis进程内部的开销和数据占用的内存
    used_memory_human:908.07K            # Redis分配的内存总量(Kb,human会展示出单位)
    used_memory_rss_human:2.28M          # 向操作系统申请的内存大小(Mb)(这个值一般是大于used_memory的,因为Redis的内存分配策略会产生内存碎片)
    used_memory_peak:929864              # redis的内存消耗峰值(byte)
    used_memory_peak_human:908.07K       # redis的内存消耗峰值(KB)
    
    maxmemory:0                         # 配置中设置的最大可使用内存值(byte),默认0,不限制,一般配置为机器物理内存的百分之七八十,需要留一部分给操作系统
    maxmemory_human:0B                  # 配置中设置的最大可使用内存值
    maxmemory_policy:noeviction         # 当达到maxmemory时的淘汰策略
    

Redis 的单线程和高性能

Redis 是单线程的吗?

redis的单线程主要是指 redis 的网络 IO 和键值对读写由一个线程来完成,这也是 redis 对外提供键值存储服务的主要流程。但 redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实都是由额外的线程执行的。

Redis 的单线程为什么还能这么快?

因为他所有的数据都存储在**内存**中,所有运算都是内存级别的运算,而且单线程避免了多线程的切换性能损耗问题。

正因为 redis 是单线程,所以要小心使用 redis 指令,对于那些耗时的指令(keys),一定要谨慎操作,一不小心可能会导致 redis 卡顿。

Redis 单线程如何处理那么多的并发客户端链接?

redis 的IO多路复用:redis 利用 epoll 来实现 IO 多路复用,将链接信息和事件放到队列中,依次放到事件分派器,事件分派器将事件分发给事件处理器。

image-20220317170057311