关于java:Redis数据结构实战演练看看微博微信购物车抽奖小程序是如何使用的

3次阅读

共计 10876 个字符,预计需要花费 28 分钟才能阅读完成。

大家都晓得,当初只有是个零碎就会有缓存的存在,而且简直所有的零碎都离不开 Redis,可见 Redis 在当初零碎的重要性。

所以,明天咱们就来聊一下 Redis,当然次要聊聊 Redis 在不同业务场景下的应用。

接下来,咱们先从缓存的世界开始,一步步揭开 Redis 的神秘面纱。

1. 缓存发展史 & 缓存分类

1.1 大型网站中缓存的应用

访问量越大,响应力越差,用户体验越差

引入缓存、示意图如下:

高性能

如果用户第一次拜访数据库中的某些数据的话,这个过程是比较慢,毕竟是从硬盘中读取的。然而,如果说,用户拜访的数据属于高频数据并且不会常常扭转的话,那么咱们就能够很释怀地将该用户拜访的数据存在缓存中。

这样有什么益处呢? 那就是保障用户下一次再拜访这些数据的时候就能够间接从缓存中获取了。操作缓存就是间接操作内存,所以速度相当快。

不过,要放弃数据库和缓存中的数据的一致性。如果数据库中的对应数据扭转的之后,同步扭转缓存中相应的数据即可!

高并发:

个别像 MySQL 这类的数据库的 QPS 大略都在 1w 左右(4 核 8g),然而应用 Redis 缓存之后很容易达到 10w+,甚至最高能达到 30w+(就单机 redis 的状况,redis 集群的话会更高)。

QPS(Query Per Second):服务器每秒能够执行的查问次数;

所以,间接操作缓存可能接受的数据库申请数量是远远大于间接拜访数据库的,所以咱们能够思考把数据库中的局部数据转移到缓存中去,这样用户的一部分申请会间接到缓存这里而不必通过数据库。进而,咱们也就进步的零碎整体的并发。

1.2 常见缓存的分类

分布式缓存

分布式缓存次要解决的是单机缓存的容量受服务器限度并且无奈保留通用的信息。因为,本地缓存只在以后服务里无效,比方如果你部署了两个雷同的服务,他们两者之间的缓存数据是无奈独特的。

具备缓存性能的中间件:Redis、Memcache、Tair(阿里、美团)等等

1.3 分布式缓存选型计划比照

Memcache 和 Redis 区别

共同点

  1. 都是基于内存的数据库,个别都用来当做缓存应用。
  2. 都有过期策略。
  3. 两者的性能都十分高。

区别

  1. Redis 反对更丰盛的数据类型(反对更简单的利用场景)。Redis 不仅仅反对简略的 k/v 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。Memcached 只反对最简略的 k/v 数据类型。
  2. Redis 反对数据的长久化,能够将内存中的数据放弃在磁盘中,重启的时候能够再次加载进行应用, 而 Memecache 把数据全副存在内存之中。
  3. Redis 有劫难复原机制。 因为能够把缓存中的数据长久化到磁盘上。
  4. Memcached 没有原生的集群模式,须要依附客户端来实现往集群中分片写入数据;然而 Redis 目前是原生反对 cluster 模式的.
  5. Memcached 是多线程,非阻塞 IO 复用的网络模型;Redis 应用单线程的多路 IO 复用模型。(Redis 6.0 引入了多线程 IO)

置信看了下面的比照之后,咱们曾经没有什么理由能够抉择应用 Memcached 来作为本人我的项目的分布式缓存了。

2. Redis 概述 & 装置配置

2.1 概述

官网:https://redis.io

中武官网地址:http://www.redis.cn

简略来说 Redis 就是一个应用 C 语言开发的数据库,不过与传统数据库不同的是 Redis 的数据是存在内存中的,也就是它是内存数据库,所以读写速度十分快,因而 Redis 被广泛应用于缓存方向。

另外,Redis 除了做缓存之外,Redis 也常常用来做分布式锁,甚至是音讯队列。

Redis 提供了多种数据类型来反对不同的业务场景。Redis 还反对事务、长久化、Lua 脚本、多种集群计划。

Redis 利用场景

  • 缓存应用,加重 DB 压力
  • DB 应用,用于长期存储数据(字典表,购买记录)
  • 解决分布式场景下 Session 拆散问题(登录信息)
  • 工作队列(秒杀、抢红包等等)乐观锁
  • 利用排行榜 zset
  • 签到 bitmap
  • 分布式锁
  • 冷热数据交换

2.3 装置 & 配置

官网:https://redis.io/download

Redis 没有官网的 windows 版本,所以倡议在 linux 零碎下来运行

抉择下载稳固版本、不稳固版本能够尝鲜、然而不举荐在生产环境中应用

装置

第一步:装置 C 语言须要的 GCC 环境

yum install -y gcc-c++
yum install -y wget

第二步:下载并解压缩 Redis 源码压缩包

# 下载
wget https://download.redis.io/releases/redis-6.2.4.tar.gz
mkdir /usr/local/redis
    tar -zxvf redis-6.2.4.tar.gz -C /usr/local/redis

第三步:编译 Redis 源码,进入 redis-6.2.4 目录,执行编译命令, 进行装置

cd  /usr/local/redis/redis-6.2.4/src
make && make install

执行结束后装置胜利!

启动

前端启动
  • 启动命令:redis-server,间接运行 bin/redis-server 将以前端模式启动
  • 敞开命令:ctrl+c
  • 启动毛病:客户端窗口敞开则 redis-server 程序完结,不举荐应用此办法
  • 启动图例:
后端启动(守护过程启动)
  • 第一步:拷贝 redis-6.2.4/redis.conf 配置文件到 Redis 装置目录的 bin 目录
cp redis.conf /usr/local/redis
  • 第二步:批改 redis.conf
vim redis.conf
  • 第三步:批改 redis.conf

(1)批改 daemonize no —> daemonize yes,目标是为了让 redis 启动在 linux 后盾运行

(2)批改 redis 的工作目录:(名称随便)

  • 第四步:启动服务
.redis-server redis.conf

查看过程

  • 后端启动的敞开形式
.redis-cli shutdown

命令阐明

redis-server:启动 redis 服务

redis-cli:进入 redis 命令客户端

redis-benchmark:性能测试的工具

redis-check-aof:aof 文件进行查看的工具

redis-check-dump:rdb 文件进行查看的工具

redis-sentinel:启动哨兵监控服务

Redis 命令行客户端

  • 命令格局
.redis-cli -h 127.0.0.1 -p 6379
  • 参数阐明
-h:redis 服务器的 ip 地址
-p:redis 实例的端口号
  • 默认形式:如果不指定主机和端口也能够 默认主机地址是 127.0.0.1 默认端口是 6379
.redis-cli

2.4 ui

命令行曾经足够弱小,尤其是高版本,弱小到狐疑人生

然而!它并不敌对,业界有很多 ui 可供使用,典型的:Another Redis Desktop Manager

1)开源

源码地址:https://gitee.com/qishibo/Ano…

编译包下载:https://github.com/qishibo/An…

2)反对多平台

Windows

Linux

Mac

3)根本应用

创立连贯:

主页监控:

基本操作:

命令行:

3. 数据类型抉择 & 利用场景

Redis 的 Key 的设计

1、key 名设计

可读性和可管理性

以业务名 (或数据库名) 为前缀(避免 key 抵触),用冒号分隔,比方 业务名: 表名:id

简洁性

保障语义的前提下,管制 key 的长度,当 key 较多时,内存占用也不容忽视,例如:

不要蕴含特殊字符

反例:蕴含空格、换行、单双引号以及其余转义字符

string 字符串类型

  1. 介绍 :string 数据结构是简略的 key-value 类型。尽管 Redis 是用 C 语言写的,然而 Redis 并没有应用 C 的字符串示意,而是本人构建了一种 简略动静字符串(simple dynamic string,SDS)。相比于 C 的原生字符串,Redis 的 SDS 不光能够保留文本数据还能够保留二进制数据,并且获取字符串长度复杂度为 O(1)(C 字符串为 O(N)), 除此之外,Redis 的 SDS API 是平安的,不会造成缓冲区溢出。
  2. 常用命令: set,get,strlen,exists,decr,incr,setex 等等。
  3. 利用场景:个别罕用在须要计数的场景,比方用户的拜访次数、热点文章的点赞转发数量等等。
  • 单值缓存

    SET <font color=’red’>key </font> <font color=’blue’>value</font>

    GET <font color=’red’>key </font>


  • 对象缓存

    MSET user:1:name zimu user:1:balance 1888

    MGET user:1:name user:1:balance

  • 分布式锁(「SET if Not eXists」)

    SETNX <font color=’red’>product:10001</font> true // 返回 1 代表获取锁胜利

    SETNX <font color=’red’>product:10001</font> false // 返回 0 代表获取锁失败

    ……. 执行业务操作

    DEL <font color=’red’>product:10001</font> // 执行完业务 开释锁

    SET <font color=’red’>product:10001</font> true ex 10 nx // 避免程序意外终止导致死锁


  • 计数器

    INCR article:readcount:101

hash 类型(散列表)

  1. 介绍 :hash 相似于 JDK1.8 前的 HashMap,外部实现也差不多(数组 + 链表)。不过,Redis 的 hash 做了更多优化。另外,hash 是一个 string 类型的 field 和 value 的映射表, 特地适宜用于存储对象,后续操作的时候,你能够间接仅仅批改这个对象中的某个字段的值。比方咱们能够 hash 数据结构来存储用户信息,商品信息等等。
  2. 常用命令: hset,hmset,hexists,hget,hgetall,hkeys,hvals 等。
  3. 利用场景: 零碎中对象数据的存储。
  • 对象缓存

    HMSET user {userId}:username zhangfei {userId}:password 123456

    HMSET user 1:username zhangfei 1:password 123456

    HMGET user 1:username 1:password

  • 电商购物车

  • 购物车操作

    1)增加商品 —> hset cart:1001 10088 1

    2) 减少数量 —> hincrby cart:1001 10088 1

    3)商品总数 —> hlen cart:1001

    4)删除商品 —> hdel cart:1001 10088

    5)获取购物车所有商品 —> hgetall cart:1001

长处:

1)同类数据归类整合贮存,不便数据管理

2)相比 String 操作耗费内存和 cpu 更小

3)相比 String 贮存 更节俭空间

毛病:

1)过期性能不能应用在 field 上,只能用在 key 上

2)Redis 集群架构下不适宜大规模应用

list 列表类型

  1. 介绍 list 即是 链表 。链表是一种十分常见的数据结构,特点是易于数据元素的插入和删除并且且能够灵便调整链表长度,然而链表的随机拜访艰难。许多高级编程语言都内置了链表的实现比方 Java 中的 LinkedList,然而 C 语言并没有实现链表,所以 Redis 实现了本人的链表数据结构。Redis 的 list 的实现为一个 双向链表,即能够反对反向查找和遍历,更不便操作,不过带来了局部额定的内存开销。
  2. 常用命令: rpush,lpop,lpush,rpop,lrange、llen 等。
  3. 利用场景: 公布与订阅或者说音讯队列、慢查问。
  • 罕用数据结构

    Stack(栈)= LPUSH(右边放)+ LPOP(右边取)–> FILO

    Quece(队列)= LPUSH(右边放)+ RPOP 左边取)

    BLocking MQ(阻塞队列)= LPUSH(右边放)+ BRPOP(左边阻塞取:没有数据就阻塞!)

  • 微博、朋友圈、公众号等,关注的文章列表展现

子慕老师关注了北京本地宝,京城美味君等公众号,这些订阅号公布音讯时,通过推或拉的形式把音讯 LPUSH 放入 redis 中属于小明的 list 中。其中 key 为 msg:{小明_ID}。当小明要获取大 V 们发的音讯时,应用 LRANGE 命令从队列中获取指定个数的订阅号信息

1)京城美味君发动静,音讯 ID 为 10001

LPUSH msg:{zimu-ID} 10001

2)北京本地宝发动静,音讯 ID 为 10002

LPUSH msg:{zimu-ID} 10002

3)查看最新订阅号音讯

LRANGE msg:{zimu-ID} 0 4

set 汇合类型

  1. 介绍: set 相似于 Java 中的 HashSet。Redis 中的 set 类型是一种无序汇合,汇合中的元素没有先后顺序。当你须要存储一个列表数据,又不心愿呈现反复数据时,set 是一个很好的抉择,并且 set 提供了判断某个成员是否在一个 set 汇合内的重要接口,这个也是 list 所不能提供的。能够基于 set 轻易实现交加、并集、差集的操作。比方:你能够将一个用户所有的关注人存在一个汇合中,将其所有粉丝存在一个汇合。Redis 能够十分不便的实现如独特关注、独特粉丝、独特爱好等性能。这个过程也就是求交加的过程。
  2. 常用命令: sadd,spop,smembers,sismember,scard,sinterstore,sunion 等。
  3. 利用场景: 须要寄存的数据不能反复以及须要获取多个数据源交加和并集等场景
  • 微信抽奖小程序

1)点击 参加抽奖 退出汇合

SADD key {userID}

2)查看排行榜

SMEMBERS key

3)抽取 count 名中奖者

SRANDMEMBER key [count] / SPOP key [count]

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

首先理解一下 set 的汇合操作,如果有三个汇合

交加为:SINTER set1 set2 set3 ==> {c}

并集为:SUNION set1 set2 set3 ==> {a,b,c,d,e}

差集为:SDIFF set1 set2 set3 ==> {a}

差集计算形式:set1 –(set2 并 set3)= {a、b、c} – {b、c、d、e} = {a} 只保留 a 中独自存在的元素

独特关注 A 的人 :能够用交加来实现
我可能意识的人:能够应用差集来实现,把我关注的人求差集

sortedset 有序汇合类型

  1. 介绍: 和 set 相比,sorted set 减少了一个权重参数 score,使得汇合中的元素可能按 score 进行有序排列,还能够通过 score 的范畴来获取元素的列表。有点像是 Java 中 HashMap 和 TreeSet 的结合体。
  2. 常用命令: zadd,zcard,zscore,zrange,zrevrange,zrem 等。
  3. 利用场景: 须要对数据依据某个权重进行排序的场景。比方在直播零碎中,实时排行信息蕴含直播间在线用户列表,各种礼物排行榜,弹幕音讯(能够了解为按音讯维度的音讯排行榜)等信息。
  • Zset 汇合操作实现排行榜

1) 点击新闻,为其分值 +1

ZINCRBY hotNews:20210707 1 iphone13 或有日落金玫瑰金

2)展现当日排行前 10

ZREVRANGE hotNews:20210707 0,9 WITHSCORES

bitmap 位图 类型

  1. 介绍: bitmap 存储的是间断的二进制数字(0 和 1),通过 bitmap, 只须要一个 bit 位来示意某个元素对应的值或者状态,key 就是对应元素自身。咱们晓得 8 个 bit 能够组成一个 byte,所以 bitmap 自身会极大的节俭贮存空间。
  1. 常用命令: setbitgetbitbitcountbitop
  2. 利用场景: 适宜须要保留状态信息(比方是否签到、是否登录 …)并须要进一步对这些信息进行剖析的场景。比方用户签到状况、沉闷用户状况、用户行为统计(比方是否点赞过某个视频)。
# SETBIT 会返回之前位的值(默认是 0)这里会生成 7 个位
127.0.0.1:6379> setbit mykey 7 1
(integer) 0
127.0.0.1:6379> setbit mykey 7 0
(integer) 1
127.0.0.1:6379> getbit mykey 7
(integer) 0
127.0.0.1:6379> setbit mykey 6 1
(integer) 0
127.0.0.1:6379> setbit mykey 8 1
(integer) 0
# 通过 bitcount 统计被被设置为 1 的位的数量。127.0.0.1:6379> bitcount mykey
(integer) 2Copy to clipboardErrorCopied

针对下面提到的一些场景,这里进行进一步阐明。

应用场景一:用户行为剖析 很多网站为了剖析你的爱好,须要钻研你点赞过的内容。

# 记录你喜爱过 001 号小姐姐
127.0.0.1:6379> setbit beauty_girl_001 uid 1

应用场景二:统计沉闷用户

面试题:当初零碎有亿级的沉闷用户,为了加强用户粘性,该如何实现签到、日活统计?

应用工夫作为 key,而后用户 ID 为 offset,如果当日沉闷过就设置为 1

那么我该如果计算某几天 / 月 / 年的沉闷用户呢(暂且约定,统计工夫内只有有一天在线就称为沉闷),有请下一个 redis 的命令

# 对一个或多个保留二进制位的字符串 key 进行位元操作,并将后果保留到 destkey 上。# BITOP 命令反对 AND、OR、NOT、XOR 这四种操作中的任意一种参数
BITOP operation destkey key [key ...]

初始化数据:

127.0.0.1:6379> setbit 20210308 1 1
(integer) 0
127.0.0.1:6379> setbit 20210308 2 1
(integer) 0
127.0.0.1:6379> setbit 20210309 1 1
(integer) 0

统计 20210308~20210309 总沉闷用户数: 1

127.0.0.1:6379> bitop and desk1 20210308 20210309
(integer) 1
127.0.0.1:6379> bitcount desk1
(integer) 1

统计 20210308~20210309 在线沉闷用户数: 2

127.0.0.1:6379> bitop or desk2 20210308 20210309
(integer) 1
127.0.0.1:6379> bitcount desk2
(integer) 2

geo 地理位置类型

概述

Redis 3.2 中减少了对 GEO 类型的反对。GEO,Geographic,地理信息的缩写。该类型,就是元素的 2 维坐标,在地图上就是经纬度。redis 基于该类型,提供了经纬度设置,查问,范畴查问,间隔查问,经纬度 Hash 等常见操作

利用场景:左近的人、摇一摇、左近的车、左近银行站点查问

环境要求

  1. redis 版本须要 3.2 及以上
  2. 如果应用 jedis 操作 redis,须要 jedis 版本为 2.9 及以上
  3. 如果应用 spring data redis 操作 redis,须要 spring data redis 版本为 1.8.0 及以上

redis GEO 常用命令

Tips:
在学习 geo 命令时会应用到经纬度坐标信息,能够在百度地图的拾取坐标零碎中获取测试坐标信息,网址:http://api.map.baidu.com/lbsapi/getpoint/index.html

1. geoadd 命令

为了进行地理位置相干操作,咱们首先须要将具体的地理位置记录起来,这一点能够通过执行 geoadd 命令来实现,该命令的根本格局如下:

GEOADD location-set longitude latitude name [longitude latitude name ...]

此命令用于增加地位信息到汇合中

以下代码展现了如何通过 GEOADD 命令,将武汉、襄阳、宜昌、枝江、咸宁等数个湖北省的市增加到地位汇合 hubeiCities 汇合外面

此处增加武汉的坐标信息到 hubeiCities 汇合中

geoadd hubeiCities 114.32538 30.534535 wuhan

此处增加襄阳、枝江、咸宁的坐标信息到 hubeiCities 汇合中

geoadd hubeiCities 112.161882 32.064505 xiangyang 111.305197 30.708127 yichang 111.583717 30.463363 zhijiang 114.295174 29.885892 xianning

2. geopos 命令

此命令用于依据输出的地位名称获取地位的坐标信息,根本语法如下

GEOPOS location-set name [name ...]

案例:查问襄阳市的地位信息

geopos hubeiCities xiangyang
-- 后果如下【1 为经度 2 为纬度】1) "112.16188341379165649"
2) "32.06450528704699821"

也能够一次查问多个地位的经纬度

geopos hubeiCities xiangyang wuhan
-- 襄阳的经纬度
1) 1) "112.16188341379165649"
   2) "32.06450528704699821"
-- 武汉的经纬度
2) 1) "114.32538002729415894"
   2) "30.53453492166421057"

3. geodist 命令

此命令用于计算两个地位之间的间隔,根本语法如下:

GEODIST location-set location-x location-y [unit]

可选参数 unit 用于指定计算间隔时的单位,它的值能够是以下单位的其中一个:

m 示意单位为米。
km 示意单位为千米。
mi 示意单位为英里。
ft 示意单位为英尺。

案例:别离以默认间隔单位和指定间隔单位计算襄阳和武汉的间隔

-- 不指定间隔单位
127.0.0.1:6381> geodist hubeiCities xiangyang wuhan
"266889.7642"
-- 指定间隔单位 km
127.0.0.1:6381> geodist hubeiCities xiangyang wuhan km
"266.8898"

4. georadius 命令和 georadiusbymember 命令

这两个命令都能够用于获取指定范畴内的元素,也即查找特定范畴之内的其余存在的地点。比方找出地点 A 范畴 200 米之内的所有地点,找出地点 B 范畴 50 公里之内的所有地点等等。

这两个命令的作用一样,只是指定中心点的形式不同:georadius 应用用户给定的经纬度作为计算范畴时的中心点,而 georadiusbymember 则应用贮存在地位汇合外面的某个地点作为中心点。

以下是这两个命令的根本语法

GEORADIUS location-set longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]

GEORADIUSBYMEMBER location-set location radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]

这两个命令的各个参数的意义如下:

m|km|ft|mi 指定的是计算范畴时的单位;

如果给定了 WITHCOORD,那么在返回匹配的地位时会将地位的经纬度一并返回;

如果给定了 WITHDIST,那么在返回匹配的地位时会将地位与中心点之间的间隔一并返回;

在默认状况下,GEORADIUS 和 GEORADIUSBYMEMBER 的后果是未排序的,ASC 能够让查找后果依据间隔从近到远排序,而 DESC 则能够让查找后果依据从远到近排序;

COUNT 参数用于指定要返回的后果数量。

上面通过案例别离演示 georadius 命令和 georadiusbymember 命令

GEORADIUS 案例:
在 hubeiCities 地位汇合中查找间隔经纬度为 112.927076 28.235653(长沙)500km 以内的地位信息,查找后果中应蕴含不超过 5 个地位的坐标信息,间隔信息,并按间隔由近到远排序。
查问代码如下:

127.0.0.1:6381> georadius hubeiCities 112.927076 28.235653 500 km withcoord withdist asc count 5
-- 咸宁  间隔指标地位 226.67 公里  
1) 1) "xianning"
   2) "226.6716"
   3) 1) "114.29517298936843872"
      2) "29.88589217282589772"
-- 枝江  间隔指标地位 279.91 公里
2) 1) "zhijiang"
   2) "279.9154"
   3) 1) "111.58371716737747192"
      2) "30.46336248623112652"
-- 武汉  间隔指标地位 289.38 公里
3) 1) "wuhan"
   2) "289.3798"
   3) 1) "114.32538002729415894"
      2) "30.53453492166421057"
-- 宜昌  间隔指标地位 316.68 公里
4) 1) "yichang"
   2) "316.6777"
   3) 1) "111.30519658327102661"
      2) "30.70812783498269738"
-- 襄阳  间隔指标地位 432.18 公里
5) 1) "xiangyang"
   2) "432.1767"
   3) 1) "112.16188341379165649"
      2) "32.06450528704699821"

GEORADIUSBYMEMBER 案例:
在 hubeiCities 地位汇合中查找间隔襄阳 200km 以内的地位信息【这里指定的指标地位只能是 hubeiCities 中存在的地位,而不能指定地位坐标】,查找后果中应蕴含不超过 2 个地位的坐标信息,间隔信息,并按间隔由远到近排序。
查问代码如下:

127.0.0.1:6381> georadiusbymember hubeiCities xiangyang 200 km withcoord withdist desc count 2
-- 枝江  距襄阳 186.38km
1) 1) "zhijiang"
   2) "186.3784"
   3) 1) "111.58371716737747192"
      2) "30.46336248623112652"
-- 宜昌  距襄阳 171.40km
2) 1) "yichang"
   2) "171.3950"
   3) 1) "111.30519658327102661"
      2) "30.70812783498269738"

好了,明天就先唠到这里,真是越来越领会到了 码字不易的粗浅外延,一不小心,码了 6000+ 字,有点累了

大家如果感觉有帮忙,就请给个 点赞、评论、转发,棘手来个一键三连,哈哈哈

你们的反对是我最大的能源。

加油 打工人!!!

来日文章,大家自行饮用。

往期干货:

本文由传智教育博学谷 – 狂野架构师教研团队公布,转载请注明出处!

如果本文对您有帮忙,欢送关注和点赞;如果您有任何倡议也可留言评论或私信,您的反对是我保持创作的能源

正文完
 0