1. 前言
在前面的两篇文章:浅谈redis数据结构之列表,浅谈redis数据结构之哈希,考虑到这两种数据结构的通用性及重要性,所以本篇专门为这两种数据结构的应用场景做个补充。
2. 应用场景之文章列表
假设有这样一个需求:每个用户有属于自己的文章列表,现需要展示(分页)文章列表。此时可以考虑使用列表,因为列表不但是有序的,同时支持按照索引范围获取元素。
我们利用redis的列表数据结构和哈希数据结构做个简单的实现。假设每篇文章有三个属性:标题(title)、时间(timestamp)、内容(content),每篇文章使用哈希结构存储。
具体的操作如下所示:
向文章列表(用户)添加文章,user:{id}:articles 作为用户文章列表的键(Key)。操作如下所示:
分页获取文章列表(用户)。例如:获取用户id=1的前3篇文章,操作如下所示:
使用列表类型保存和获取文章列表会存在两个问题:
第一,如果每次分页获取的文章个数较多,我们会想到可以利用hgetall操作,但是如果需要多次执行此命令。此时可以考虑使用Pipeline进行批量获取,或者考虑将文章数据序列化为字符串类型,使用mget批量获取。
第二,分页获取文章列表时,lrange命令在列表两端性能较好,但是如果列表较大,获取列表中间范围的元素性能会变差。此时可以考虑将列表做二级拆分,或者使用Redis 3.2的quicklist内部编码实现,它结合ziplist和linkedlist的特点,获取列表中间范围的元素时也可以高效完成。
3. 应用场景之关系型数据表
假设有一个关系型的数据表,存储的是用户的基础数据。其中用户的属性作为表的列,每条用户的信息作为行,如下表所示:
id | name | age | city |
1 | tom | 23 | beijing |
2 | mike | 30 | tianjin |
这样的表结构,对于小伙伴们在日常的工作或学习中,是再熟悉不过了。如果我们把这些用户的基础数据,按照redis的哈希数据结构进行存储的话,就如下图所示:
对比这两种形式,使用字符串序列化缓存用户信息,哈希类型变得更加直观,并且在更新操作上会更加便捷。可以将每个用户的id定义为键后缀,多对field-value对应每个用户的属性,类似如下代码:
public UserInfo getUserInfo(long id) { // 用户id作为key后缀 String userRedisKey = "user:info:" + id; // 使用hgetall获取所有用户信息映射关系 Object 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; }
需要注意的是哈希类型和关系型数据库有两点不同之处:
第一,哈希类型是稀疏的,而关系型数据库是完全结构化的,例如哈希类型每个键可以有不同的field,而关系型数据库一旦添加新的列,所有行都要为其设置值(即使为 NULL),如图所示:
第二,关系型数据库可以做复杂的关系查询,而使用Redis去模拟关系型复杂查询开发困难,维护成本高。
4. 应用场景之数据缓存方式
就拿上一节的用户数据举例,截止到已经介绍的redis数据结构,以有三种缓存数据的方式,下面比较下这三种缓存数据的方式:
4.1 原生字符串类型
给用户信息的每一个属性分配一个键。操作如下所示:
- 优点:简单直观,每个属性都支持更新操作。
- 缺点:占用过多的键,内存占用量较大,同时用户信息内聚性比较差,所以此种方案一般不会在生产环境使用。
4.2 序列化字符串类型
将用户信息序列化后用一个键保存。操作如下所示:
- 优点:简化编程,如果合理的使用序列化可以提高内存利用率。
- 缺点:序列化和反序列化 有一定的开销,同时每次更新属性都需要把全部数据取出进行反序列化,更新后再序列化到Redis中。
4.3 哈希类型
每个用户属性使用一对field-value,但是只用一个键保存。操纵如下所示:
- 优点:简单直观,如果使用合理可以减少内存空间的使用。
- 缺点:要控制和减少哈希在ziplist和hashtable两种内部编码的转换,hashtable会消耗更多内存。
相关推荐
Redis应用场景--Redis作者谈Redis应用场景
去年我写的培训用教材,redis应用场景简介,简单列举了一些Redis的使用场景。 发现下载积分居然无法调整……
Redis作为一个典型的非关系型数据库,目前来说在企业级应用中使用广泛。它十分适合存储少、访问量巨大的场景,所有数据全部in-memory保证了数据的高速访问。
redis简介和应用场景介绍redis简介和应用场景介绍redis简介和应用场景介绍redis简介和应用场景介绍redis简介和应用场景介绍redis简介和应用场景介绍redis简介和应用场景介绍redis简介和应用场景介绍redis简介和应用...
redis常见应用场景, 帮我们设计系统架构时排查redis使用场景
非常好的redis学习资料: 1、数据类型与实现 2、持久化与复制 3、容量规划 4、适用场景 5、相关参数与优化
redis适用场景:“redis适合的场景有:1、缓存;2、排行榜;3、计数器;4、分布式会话;5、分布式锁;6、 社交网络;7、最新列表;8、消息系统。”
Redis特性和应用场景
Redis集群以及应用场景
数据类型详解 以及 redis适用场景场合 数据类型详解 以及 redis适用场景场合
redis合集(深入了解redis,redis实践,redis适用场景及实现,redis学习笔记整理,redis命令参考手册,python简明教程.chm,redis_cmd.chm)
想学习redis的朋友,可以下载参考学习,全面的redis代码,希望对你有所帮助!
redis的使用场景.doc 处理高并发的缓存技术 什么时候需要使用到此技术
redis基本使用已经应用场景
Redis是一个key-value存储系统,现在在各种系统中的使用越来越多,大部分情况下是因为其高性能的特性,被当做缓存使用,这里介绍下Redis经常遇到的使用场景。下面话不多说了,来一起看看详细的介绍吧。 Redis特性 ...
redis集群搭建(一主一从一哨兵)
redis使用场景及数据结构.docx
官方提供的数据表明,在一个普通的Linux机器上,Redis读写速度分别达到81000/s和110000/s。 数据结构 可以将Redis看做“数据结构服务器”。目前,Redis支持5种数据结构。 持久化 由于所有数据保持在内存中,所以对...
tp6 redis应用方案应用方案应用方案
一组Redis实际应用中的异常场景及其根因分析和解决方案.docx