前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【数据库】Redis基础篇

【数据库】Redis基础篇

作者头像
用户3467126
发布2019-07-03 18:28:37
3810
发布2019-07-03 18:28:37
举报
文章被收录于专栏:爱编码爱编码爱编码

简介

Redis是一个开源(BSD许可)的内存数据结构存储,用作数据库、缓存和消息代理。它支持诸如字符串、散列、列表、集、带范围查询的排序集、位图、hyperloglog、带半径查询和流的地理空间索引等数据结构。

Redis具有内置的复制、Lua脚本、LRU清除、事务和不同级别的磁盘持久性,并通过Redis Sentinel和Redis集群的自动分区提供高可用性。

原理架构

Redis使用单线程架构和I/O多路复用模型来实现高性能内存数据库服务。

单线程模型

因为Redis是单线程来处理命令的,所以一条命令从客户端达到服务端不会立刻被执行。所有命令都会进入一个队列中,然后逐个被执行,因此不会产生并发问题。

为什么单线程还能这么快

1. 纯内存访问,Redis将所有数据放在内存中,内存的响应时长大 约为100纳秒,这是Redis达到每秒万级别访问的重要基础。2.非阻塞I/O,Redis使用epoll作为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多的时间,如下图所示

• 3.单线程避免了线程切换和竞态产生的消耗。

注:阻塞操作是非常影响Redis性能的,下一篇文章总结。

API使用场景

命令语法可以到下面地址查,本节仅仅说使用场景。

字符串

1. 缓存功能 Redis作为缓存层,MySQL作为存储层,绝大部分请求的数据都是从Redis中获取。由于Redis具有支撑高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用。

类似下面这样子的伪代码

// 从MySQL获取用户信息
userInfo = mysql.get(id);
// 将userInfo序列化,并存入Redis
redis.setex(userRedisKey, 3600, serialize(userInfo));
// 返回结果
return userInfo

2. 计数 Redis作为文章点赞数计数的基础组件,用户每一次点赞,相应的点赞数就会自增1。

long incrLikeCounter(long id) {
    key = "article:like:" + id;
    return redis.incr(key);
}

3. 共享Session Redis将用户的Session进行集中管理,每次用户更新或者查询登录信息都直接从Redis中集中获取。

4. 限速 很多应用出于安全的考虑,会在每次进行登录时,让用户输入手机验证码,从而确定是否是用户本人。但是为了短信接口不被频繁访问,会限制用户每分钟获取验证码的频率。类似如下伪代码

phoneNum = "138xxxxxxxx";
key = "shortMsg:limit:" + phoneNum;
// SET key value EX 60 NX
isExists = redis.set(key,1,"EX 60","NX");
if(isExists != null || redis.incr(key) <=5){
// 通过
}else{
// 限速
}

哈希

关系型数据表记录的两条用户信息,用户的属性作为表的列,每条用户信息作为行。

相比于使用字符串序列化缓存用户信息,哈希类型变得更加直观,并且在更新操作上会更加便捷。可以将每个用户的id定义为键后缀,多对fieldvalue对应每个用户的属性。

类似如下伪代码:

UserInfo getUserInfo(long id){
// 用户id作为key后缀
userRedisKey = "user:info:" + id;
// 使用hgetall获取所有用户信息映射关系
userInfoMap = redis.hgetAll(userRedisKey);
UserInfo userInfo;
if (userInfoMap != null) {
// 将映射关系转换为UserInfo
userInfo = transferMapToUserInfo(userInfoMap);
} else {
// 从MySQL中获取用户信息
userInfo = mysql.get(id);
// 将userInfo变为映射关系使用hmset保存到Redis中
redis.hmset(userRedisKey, transferUserInfoToMap(userInfo));
// 添加过期时间
redis.expire(userRedisKey, 3600);
}
return userInfo;
}

列表

列表是一种比较灵活的数据结构,它可以充当栈和队列。

相关命令时间复杂度表:

消息队列 Redis的lpush+brpop命令组合即可实现阻塞队列,生产者客户端使用lrpush从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞式的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。如图所示:

口诀:

•lpush+lpop=Stack(栈)•lpush+rpop=Queue(队列)•lpsh+ltrim=Capped Collection(有限集合)•lpush+brpop=Message Queue(消息队列)

集合

集合类型比较典型的使用场景是标签(tag)。

例如一个用户可能对娱乐、体育比较感兴趣,另一个用户可能对历史、新闻比较感兴趣,这些兴趣点就是标签。 有了这些数据就可以得到喜欢同一个标签的人,以及用户的共同喜好的标签,这些数据对于用户体验以及增强用户黏度比较重要。

例如一个电子商务的网站会对不同标签的用户做不同类型的推荐,比如对数码产品比较感兴趣的人,在各个页面或者通过邮件的形式给他们推荐最新的数码产品,通常会为网站带来更多的利益。

相关命令时间复杂度表:

标签实现基本思路

1. 给用户添加标签

sadd user:1:tags tag1 tag2 tag5
sadd user:2:tags tag2 tag3 tag5
...
sadd user:k:tags tag1 tag2 tag4
...

2. 给标签添加用户

sadd tag1:users user:1 user:3
sadd tag2:users user:1 user:2 user:3
...
sadd tagk:users user:1 user:2
...

3. 使用sinter命令,计算用户共同感兴趣的标签

sinter user:1:tags user:2:tags

注:1,2步应该在同一个事务(下一篇文章再讲)中执行,否则会导致数据不正确。

更多组合应用:

•sadd=Tagging(标签)•spop/srandmember=Random item(生成随机数,比如抽奖)•sadd+sinter=Social Graph(社交需求)

有序集合

它保留了集合不能有重复成员的特性,给每个元素设置一个分数(score)作为排序的依据。

场景

排行榜系统

例如视频网站需要对用户上传的视频做排行榜,榜单的维度可能是多个方面的:按照时间、按照播放数量、按照获得的赞数。

本节使用赞数这个维度,记录每天用户上传视频的排行榜。

主要需要实现以下4个功能

1. 添加用户赞数

//获得一个赞。
zadd user:ranking:2016_03_15 mike 3   
//第一个赞之后自增。
zincrby user:ranking:2016_03_15 mike 1

•2. 取消用户赞数

zrem user:ranking:2016_03_15 mike

•3. 展示获取赞数最多的十个用户

zrevrangebyrank user:ranking:2016_03_15 0 9

44.展示用户信息以及用户分数

hgetall user:info:tom
zscore user:ranking:2016_03_15 mike
zrank user:ranking:2016_03_15 mike

总结

Redis还有什么场景,欢迎各位大神指教。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-04-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱编码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 单线程模型
  • 为什么单线程还能这么快
  • 字符串
  • 哈希
  • 列表
  • 集合
  • 有序集合
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档