目录
1 关于 Redis 使用的一点想法
1.1 进行缓存前, 需考虑
1.2 进行缓存后, 需考虑
1.3 缓存使用一段时间后
2 编写 Redis 数据库层规范建议
2.1 选择适合的 redis 客户端
2.2 规范化定义 key 的名称并初始化
2.3 选择合适的数据结构
2.4 规范化定义操作方法
2.5 开始愉快的调用之旅
1 关于 Redis 使用的一点想法
1.1 进行缓存前, 需考虑
(1)该数据属于短暂保留, 例如只保留三天, 七天或者一个月, 此时建议采用缓存;
(2)该数据在某一个时间段请求量很大, 此时建议采用缓存;
(3)随着用户使用, 数据不断变化, 更新操作比较频繁, 此时建议采用缓存;
(4)如果数据量不大, 且和应用性能提升不大, 数据需要长久保留, 此时不建议采用 Redis 进行缓存, 直接使用 MySQL 等关系型数据库存储即可;
(5)如果数据量很大, 但是过了一段时间后, 该数据几乎没有什么价值, 此时建议采用缓存, 并设定过期时间和定时清理该数据的脚本, 这样处理可以减轻存储空间, 也便于优化系统的数据库层.
1.2 进行缓存后, 需考虑
(1)缓存该 key 在极端情况下, 占用系统内存会有多大? 或者说存储的记录大概会达到什么数量级?
(2)缓存该 key, 过期时间是否方便设置? 如果不方便设置, 是否可以隔段时间考虑转存到 MySQL 等关系型数据库中, 从而清理缓存, 释放内存空间.
(3)缓存该 key, 思考一下手动删除缓存数据的脚本如何编写, 缓存的所有数据, 如何区分出有价值的数据进行保留, 无价值的便利用脚本进行自动化删除.
(4)使用缓存后, 要思考选择恰当的数据结构来完成代码构建. 因为一个适合的数据结构不仅使得代码变得更加优雅, 后期维护也很方便.
1.3 缓存使用一段时间后
如果发现某一个 key 占用内存很大, 超出预料, 提供的优化建议:
(1)分析该 key 的具体实际功能, 和目前的需求, 看能否在后续缓存数据时, 添加过期时间设定;
(2)考虑在取出缓存数据的时候, 能否转存到 MySQL 等关系型数据库, 如果能够转存成功, 则在此处可以进行立即执行删除该条缓存数据的方法; 后续取数据时, 可采用先查询 Redis 数据库, 未查到再次查询一下 MySQL 等关系型数据库;
(3)依据已经缓存的数据, 看能够依据数据中的字段或者相关属性对已经缓存的数据进行过滤查询, 把那些不重要的数据通过脚本进行手动删除处理.
2 编写 Redis 数据库层规范建议
2.1 选择适合的 redis 客户端
例如, 定义了以下两个客户端:
- # -*- coding: utf-8 -*-"
- from django.conf import settings
- import redis
- redis_db_client = redis.StrictRedis(
- host=settings.REDIS['redis_db_host'],
- port=settings.REDIS['redis_db_port'],
- db=settings.REDIS['redis_db_db'],
- socket_connect_timeout=4,
- socket_timeout=2,
- decode_responses=True,
- )
- redis_hot_client = redis.StrictRedis(
- host=settings.REDIS['redis_hot_host'],
- port=settings.REDIS['redis_hot_port'],
- db=settings.REDIS['redis_hot_db'],
- socket_connect_timeout=2,
- socket_timeout=2,
- decode_responses=False,
- )
此时, 可以依据 key 的设计和作用, 选择合适的客户端来操作. 其中不同的客户端对应的端口和具体数据库不同, 以上客户端定义仅作参考.(PS: 以上定义是基于 Django 框架的配置文件来使用, 其它 Python 框架也可以类似定义)
2.2 规范化定义 key 的名称并初始化
在定义 redis 操作 key 的名称建议采用大写字母加下划线组成; 在初始化 key 对象时, 最好能够设定过期时间.
Key 定义的类示例:
- class RedisKey:
- """RedisKey 类对象"""
- def __init__(self, prefix, ex=None):
- self.prefix = prefix
- self.ex = ex
- # 生成 key 值
- def __call__(self, key):
- return self.prefix + str(key)
- 则定义一个 key 的示例:
- USER_PULL_URL = RedisKey(prefix='user_pull_url:', ex=8 * 60 * 60)
- 其中参数 prefix 为 key 存储在 Redis 数据中具体的键名, 可以通过调用__call___方法来为 key 添加后缀, 例如 user_pull_url:1808 表示一个键名; ex 为该 key 对象定义的过期时间设定, 等到具体编写该 key 的添加操作时, 调用参数 ex 来设定 key 的具体过期时间.
- 其中 key 存放的文件, 要依据选择的客户端存放在指定的文件中, 这样方便查看和管理.
- 2.3 选择合适的数据结构
- Redis 数据库包含 String(字符串),Hash(哈希表),List(列表),Set(集合),SortedSet(有序集合)五种数据结构. 下面简单介绍一下这五种数据结构的特性:
- String(字符串): 添加数据时, 采用 key-value 格式进行存储. key 是定义的键名, 在上面 (2) 中已有说明. value 是具体要存储的数据, 该数据的类型是 String 类型. 遇到的需求中, 例如需要存储某用户的在线时长, 可以采用 key_user_id 组成键名, 具体时长存储在 value 中, 此时可选择 String 数据结构来存储, 比较方便.
- Hash(哈希表): 添加数据时, 采用 key-value 格式来进行存储, 不过这里的 value 表示一张哈希表. 可以这样做比喻, key 比作关系型数据库中的表名, value 存储该关系型表中的所有行的数据记录.
- List(列表): 添加数据时, 采用 key-value 格式来进行存储, 不过这里的 value 表示一个具体的列表. 该列表的功能可类似 C++ 数据结构列表一样, 有出表操作, 计算列表长度操作, 依据下标获取某个元素的功能, 获取指定区间内的列表元素, 从列表头部插入元素或者尾部插入元素等.
- Set(集合): 添加数据时, 采用 key-value 格式来进行存储, 这里的 value 存储和 Hash(哈希表)存储类型, 而 Set(集合)区别在于在添加数据记录时, 会自动过滤掉相同值得记录, 如果插入多条记录相同得数据, 在 Set(集合)存储得 value 中只会找到一条记录.
- SortedSet(有序集合): 添加数据时, 采用 key-value 格式来进行存储. 存储的方式实现功能和 Set(集合)基本相同, 但是其唯一突出的特点就是在存数据的时候要存储元素值对应的 score 值, 也就是依据 score 值的大小来对 value 中元素进行排序存储. 后续有查询操作时, 可以很方便的返回 value 中元素的排序序列.
- 2.4 规范化定义操作方法
- 每一个服务层, 建议单独创建一个 cache.py 文件, 专门用于存放操作 Redis 数据库层的方法, 此类的功能可以类比 models.py 文件.
- 每一个 key 使用其键名创建符合代码规范的类名, 然后在该 key 对应的类里面, 定义操作的 redis_client 和 redis_key, 最后通过 cliet 和 key 定义相关数据的添加, 修改, 查询和删除的方法.
- 最后, 最重要的一点建议: 该 cache.py 中定义的操作方法建议只在该服务层中被其它类中方法体调用. 这样的好处, 可以让我们对于该 key 在以后的数据管理上有可控的预估操作, 也使得代码调用变得更加规范.
- 此处给出一个示例:
- 定义一个 key:
- 键名初始化:
- USER_SESSION = RedisKey(prefix='user_session:', ex=4 * 60 * 60)
- 选择客户端:
- redis_hot_client
- 在 cache.py 中定义的类和相关操作方法:
- class CacheUserSession: # 类名和 key 的名称对应
- """
- 原始数据类型: dict
- 存储数据类型: bytes
- 数据说明: 将 user_dict 序列化成二进制数据, 存入 Redis 中
- """
- db = redis_hot_client
- key_prefix = rediskey.USER_SESSION
- @classmethod
- def get(cls, user_id: IdInt):
- key = cls.key_prefix(user_id) # key 的具体初始化值
- user_dict = cls.db.get(key)
- if user_dict:
- user_dict = pickle.loads(user_dict)
- return user_dict
- @classmethod
- def delete(cls, user_id: IdInt): # key 的删除方法定义
- key = cls.key_prefix(user_id)
- cls.db.delete(key)
- logger.info("Delete UserSession:{}".format(user_id))
- @classmethod
- def set(cls, user_id: IdInt, user_dict: dict):
- key = cls.key_prefix(user_id)
- data = pickle.dumps(user_dict)
- cls.db.set(key, data, ex=cls.key_prefix.ex) # 注意设定过期时间
- logger.info("Set UserSession:{}, {}".format(user_id, user_dict))
此处, 给出一个很好用的存储数据的途径. 通过以下途径, 可以把一个字典格式的数据当作字符串存入 Redis 数据库, 取出后可以重新解析出字典格式. 通过该途径, 可以较好的把 Redis 当作关系型数据库存储一样, 一条数据记录可以存储多个属性的值.
例如, 一个学生, 包含学号, 姓名, 性别, 年级, 专业等属性. 如果采用关系型数据库存, 以学号作为主键, 其它属性作为列名进行设计, 通过学号即可查询出该学生的所有信息. 但是, 一个学生的多个属性信息如何存储到 Redis 数据库中, 并且取出来能够很好的使用呢?
采用的策略: 学号唯一, 可作为 key 值, 姓名, 性别, 年级, 专业联合成一个字典, 在存入 Redis 数据中之前先转换为指定结构的字符串格式, 取出后再解析成字典格式. 这样操作就能很好的解决这个需求.
为什么要转换为字符串格式存入 Redis 数据库呢? 因为不管什么数据结构的数据存入到 Redis 数据库中后, 它取出都是字符串格式.
下面请看具体代码示例(具体 cache.py 中 CacheStudent 类实现代码不给出噢):
- import json
- value_data = json.dumps({
- name: "xiaoming",
- gender: "male",
- grade: "2014",
- profession: "软件工程",
- }) # 通过 json 模块把字典转换为指定格式的字符串
- CacheStudent.set(student_id, value_data) # 通过定义好的写入方法, 把指定的学生数据存入
- student_data = json.loads(CacheStudent.get(student_id)) # 取出指定学号学生的数据后, 使用 json 模块的 loads 方法解析该字符串变成字典格式
- print(studnet_data["name"]) # 打印该学生的姓名信息
- print(studnet_data["gender"])
- print(studnet_data["grade"])
- print(studnet_data["profession"])
看到上述的实现, 是不是发现 Redis 存储可以当作关系型数据存储来用?
2.5 开始愉快的调用之旅
此处调用, 可适当选择时机何时进行数据删除操作. 比如取出后, 可以选择把最终存储的数据转存到 MySQL 数据库中, 然后调用 cache.py 中的删除方法, 对该数据记录进行删除操作.
来源: https://www.cnblogs.com/liuzhen1995/p/9374786.html