Redis 学习笔记(一)redis 数据类型和对象机制
Redis 简介
Redis 是(key-value)的 NoSQL 数据库,所有的 key 都是 String ,它的 value 可以是 String、hash、list、set、zset(有序集合)、Bitmaps(位图)、HyperLogLog、GEO(地理信息定位)等数据类型,这些类型都支持 push/pop、add/remove 及取交集和差集。而且这些操作都是原子性的。
Redis 的数据是缓存在内存中,但是 Redis 会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件中。在此基础上实现了 master-slave (主从)同步
主从复制
Redis 提供复制功能,能实现多个相同数据的 Redis 副本,复制功能是分布式 Redis 的基础
删除数据命令
# 删除指定的 key 数据
del key
# 根据 value 选择非阻塞删除,也就是现在是将 keys 从 keyspace 元数据中删除,真正的删除会在后续异步操作
unlink key
Redis 的数据类型
String
redis 中最基本的数据结构,所有的 key 都是 String 。String 类型的 Value 可以是 String、数字、jpg图片或者序列化的对象(值不能超过 512MB)
常见命令
set key value [ex seconds][px milliseconds][nx|xx]
: 设置给定键和值get key
: 获取值del key
:删除存储在给定键中的值incr key
: 将 key 对应的值加1decr key
: 将 key 对应的值减1incrby key amount
: 将key 对应的值加上整数decrby key amount
:将key 对应的值减去整数
应用场景
- 缓存:可以将常见的字符串、图片等信息缓存在 redis,mysql 作为持久化层。降低 mysql 的读写压力
计数器: 实现快速计数、查询缓存,同时数据可以异步落地到其他数据源。
共享Session:分布式服务器将用户的 Session 进行集中的管理,每次用户更新或者查询登录信息都直接从 Redis 中集中获取。
Hash
哈希类型指的是 value 本身又是一个键值对结构,比如 value = {{field1, value1}, ... {fieldN, valueN}}。
常见命令
hset hash-key sub-key1 value1
:添加键值对hget hash-key key1
: 获取制定散列键的值hgetall hash-key
:获取哈希中包含的所有键值对hdel hash-key sub-key1
: 在哈希中移除这个键
应用场景
- 缓存:能够更加直观,相比 String 来说更加节省空间。比如缓存用户信息
List
Redis 中的 List 采用双端链表来实现,可以用来存储多个有序的字符创,列表最多可以存储 2^32 - 1 个元素(element)。可以对列表两端插入(push)和弹出(pop),还可以获取制定范围的元素列表,获取指定索引下标的元素等。列表是一种比较灵活的数据结构,它可以充当栈和队列的角色。
常见命令
rpush, lpush 分别是右边和左边插入,linsert 命令会从列表中找到等于某个值的元素,在其前或者后插入新的元素。可以转换成其他的数据结构:
- lpush+lpop=Stack(栈)
- lpush+rpop=Queue(队列)
- lpush+ltrim=Capped Collection(有限集合)
- lpush+brpop=Message Queue(消息队列)
应用场景
- 消息队列:
lpush + brpop
组合可以实现阻塞队列,生产者使用 lpush 从左侧插入元素,多个消费者使用 brpop 阻塞式抢列表尾部的元素。保证消费的负载均衡和高可用性
set
set 类型是用来保存多个字符串元素,但是 set 中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。它的底层是通过哈希表来实现的,因此添加、删除、查找的复杂度都是 O(1)
常见命令
sadd key value
: 向集合中添加一个或者多个成员scard key
: 获取集合中的成员数smember key member
: 返回集合中的所有成员sismember key member
: 判断 member 元素是否是集合 key 的成员
应用场景
- 标签:给用户添加标签,所有这样有同一标签或者类似的可以推荐关注的事情或者关注的人
zset
有序集合 zset 相对于 set 而言,其内部的元素可以进行排序,它是通过给每个元素设置一个分数来作为排序的依据。
常见命令
zadd zset-key int member1
: 将一个带有给定分值的成员添加到有序的集合中zrange zset-key 0-1
: 根据元素在有序集合中所处的位置,从有序集合中获取对应的元素zrem zset-key member1
: 如果给定元素存在于有序集合中,就移除该元素
应用场景
- 排行榜:榜单可以按照用户关注数,更新时间等打分,并做排行
HyperLogLogs
HyperLogLog并不是一种新的数据结构(实际类型为字符串类 型),而是一种基数算法,通过HyperLogLog可以利用极小的内存空间 完成独立总数的统计,比如注册IP u数,每日访问IP 数等等。它是一个基于基数估算的算法,只能比较准确的估算出基数,可以使用少量固定的内存去存储并识别集合中的唯一元素。而且这个估算的技术并不一定准确,它是一个带有0.81%标准错误的近似值(对于一些可以接受容错的业务场景可以忽略不计)
例如2016-03-06的访问用户是 uuid-1、uuid-2、uuid-3、uuid-4,2016-03-05的访问用户是uuid-4、uuid-5、uuid-6、uuid-7,如图所示。
常用命令
pfadd : 用于在基数统计中添加元素,添加成功会返回1
pfcount:用于计算一个或者多个 HyperLogLogs 的独立总数
pfmerge:求出多个HyperLogLogs 的并集并赋值给 destkey
应用场景
- IP 数 : 用于统计某个时段的 IP 或者用户数
Bitmaps
它本身不是一种数据结构,实际上就是字符串,但是它可以对字符串的位进行操作
Bitmaps 相当于一个以位为单位的数组,数组的每个单元只能存储0 和 1 , 数组的下标在 Bitmaps 中叫做偏移量。
常用命令
setbit key offset value
: 设置键和偏移量的值getbit key offset
: 获取键的第 offset 位的值bitcount key
: 统计该键的次数值
应用场景
- 活跃用户分析: 存储和统计一天中活跃的用户
Geo
Redis3.2版本提供了GEO(地理信息定位)功能,支持存储地理位 置信息用来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能
常用命令
- geoadd: 添加地理位置信息
geopos: 获取地理位置信息
geodist: 获取两个地理位置的距离
georadius: 获取范围内的信息位置集合-->附近的人
geohash: 将二维经纬度转换为一维的字符串
zrem: 删除地理位置信息(实际上是利用 zset 中的命令实现对位置信息的删除)
应用场景
- 附近的人
- 推算两地之间的距离
Redis 的数据结构
为什么 Redis 会设计 RedisObject 对象,因为操作数据类型的命令除了要对键的类型进行检查以外,还需要根据数据类型的不同编码进行多态处理,所以 Redis 构建了自己的类型系统,主要有:
- redisObject 的对象机制
- redisObject 对象的类型检查和多态
- 对 redisObject 进行分配、共享和销毁的机制
redisObject 的对象机制
/*
* Redis对象
*/
typedef struct redisObject {
//类型
unsigned type:4;
//编码方式
unsigned encoding:4;
//LRU 记录最后一次访问时间
unsigned lru:LRU_BITS;
//引用计数
int refcount;
//指向底层数据结构实例
void *ptr
} robj;
type属性
记录了对象所保存的值类型,也就是常用的五个数据类型
/*
* 对象类型
*/
#define OBJ_STRING 0 // 字符串
#define OBJ_LIST 1 // 列表
#define OBJ_SET 2 // 集合
#define OBJ_ZSET 3 // 有序集
#define OBJ_HASH 4 // 哈希表
encoding 属性
记录了对象所保存的值的编码,表示数据类型对应的编码类型
/*
* 对象编码
*/
#define OBJ_ENCODING_RAW 0 /* Raw representation */
#define OBJ_ENCODING_INT 1 /* Encoded as integer */
#define OBJ_ENCODING_HT 2 /* Encoded as hash table */
#define OBJ_ENCODING_ZIPMAP 3 /* 注意:版本2.6后不再使用. */
#define OBJ_ENCODING_LINKEDLIST 4 /* 注意:不再使用了,旧版本2.x中String的底层之一. */
#define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
#define OBJ_ENCODING_INTSET 6 /* Encoded as intset */
#define OBJ_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
#define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */
#define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */
* ptr 指针
它指向实际保存值的数据结构,而数据结构类型是由前面的 encoding 和 type 两个属性来决定。如下图,数据类型和编码类型决定指向实际的数据结构。
lru 属性
记录的是对象最后一次被命令程序访问的时间,那么如何实现对对象的回收,这里引入一个概念:空转时长
空转时长,也就是当前系统时间减去 键的值对象的 LRU 时间。如果服务器用于回收内存的算法是 Volatile-lru 或者 allkeys-lru。那么当服务器占用的内存树超过了 maxmemory 选项所设置的上限值时,空转时长较高的那部分键会优先被服务器所释放。
refcount 属性
用于计数,对指向这个对象的引用计数。
比如创建了一个值为 100 的 key A ,使用 OBJECT REFCOUNT 命令查看 key A 的值对象的引用计数 refcount ,发现引用计数为 2,说明这个值对象被两个程序所引用,两个程序共享了这个值对象的 key
那么当对象的 refcount 值为 0 时,这个对象将会被内存回收释放,这也是对象的销毁机制。(对应 JVM 里面的引用计数法标记)
redis 命令的类型检查和多态
redis 当执行一个处理数据类型命令时,比如 LPOP key
命令redis 执行的步骤:
- 根据给定的 key,在数据库字典中查找对应的 redisObject 对象,没找到就返回null
- 检查找到的 redisObject 的 type 属性和执行命令所需要的类型是否相同,如果不相同就返回类型错误
- 根据 redisObject 的 encoding 属性所指定的编码,选择合适的操作函数来处理底层的数据结构
- 最后返回命令的操作结构
redisObject 对象共享和销毁
共享对象的出现是为了避免重复分配的麻烦。通过 refcount 来表示对象所引用的次数。比如创键一个 值为 100 的 key A,然后再创建一个值为 100 的 key B ,这个时候共享对象的引用计数值变为了 3
redis> SET A 100
OK
redis> SET B 100
OK
redis> OBJECT REFCOUNT A
(integer) 3
此外共享对象不单单只有字符串键可以使用, 那些在数据结构中嵌套了字符串对象的对象(linkedlist
编码的列表对象、 hashtable
编码的哈希对象、 hashtable
编码的集合对象、以及 zset
编码的有序集合对象)都可以使用这些共享对象。
为什么redis 不共享包含value 为字符串的对象?
当服务器考虑将一个共享对象设置为键的值对象时, 程序需要先检查给定的共享对象和键想创建的目标对象是否完全相同, 只有在共享对象和目标对象完全相同的情况下, 程序才会将共享对象用作键的值对象, 而一个共享对象保存的值越复杂, 验证共享对象和目标对象是否相同所需的复杂度就会越高, 消耗的 CPU 时间也会越多:
- 如果共享对象是保存整数值的字符串对象, 那么验证操作的复杂度为 O(1) ;
- 如果共享对象是保存字符串值的字符串对象, 那么验证操作的复杂度为 O(N) ;
- 如果共享对象是包含了多个值(或者对象的)对象, 比如列表对象或者哈希对象, 那么验证操作的复杂度将会是 O(N^2) 。
因此, 尽管共享更复杂的对象可以节约更多的内存, 但受到 CPU 时间的限制, Redis 只对包含整数值的字符串对象进行共享。
引用计数及对象的销毁
前面谈到过,redisObject 中带有一个 refcount 属性,表示这个对象被引用了多少次。
- 当对象被新程序共享时,其 refcount 值加1;
- 当使用完一个对象后或者消除一个对象的引用后,程序将对象的 refcount 值减1
- 当对象的 refcount 降为0 时,这个 redisObject 结构以及它所引用的数据结构的内存都会被释放
参考资料
- 《redis 开发与运维》 付磊
- 《redis 设计与实现》 黄健宏
- https://pdai.tech/md/db/nosql-redis/db-redis-x-redis-object.html
Redis 学习笔记(一)redis 数据类型和对象机制的更多相关文章
- Redis学习笔记一:数据结构与对象
1. String(SDS) Redis使用自定义的一种字符串结构SDS来作为字符串的表示. 127.0.0.1:6379> set name liushijie OK 在如上操作中,name( ...
- StackExchange.Redis学习笔记(一) Redis的使用初探
Redis Redis将其数据库完全保存在内存中,仅使用磁盘进行持久化. 与其它键值数据存储相比,Redis有一组相对丰富的数据类型. Redis可以将数据复制到任意数量的从机中 Redis的安装 官 ...
- Redis学习笔记(4) Redis事务、生存时间及排序
1. Redis事务 Redis中的事务(transaction)是一组命令的集合,一个事务中的命令要么都执行,要么都不执行.事务的原理是先将属于一个事务的命令发送给Redis,然后再让Redis依次 ...
- Redis学习笔记(1) Redis介绍及基础
1. Redis的特性 (1) 存储结构 Redis(Remote Dictionary Server,远程字典服务器)是以字典结构存储数据,并允许其他应用通过TCP协议读写字典中的内容.Redis支 ...
- redis学习笔记之redis简介
redis简介 Redis是一个开源的,高性能的,基于键值对的缓存与存储系统,通过设置各种键值数据类型来适应不同场景下的缓存与存储需求.同事redis的诸多高层级功能使其可以胜任消息队列,任务队列等不 ...
- Redis学习笔记之Redis单机,伪集群,Sentinel主从复制的安装和配置
0x00 Redis简介 Redis是一款开源的.高性能的键-值存储(key-value store).它常被称作是一款数据结构服务器(data structure server). Redis的键值 ...
- Redis学习笔记--五种数据类型的使用场景
String 1.String 常用命令: 除了get.set.incr.decr mget等操作外,Redis还提供了下面一些操作: 获取字符串长度 往字符串append内容 设置和获取字符串的某一 ...
- StackExchange.Redis学习笔记(二) Redis查询 五种数据类型的应用
ConnectionMultiplexer ConnectionMultiplexer 是StackExchange.Redis的核心对象,用这个类的实例来进行Redis的一系列操作,对于一个整个应用 ...
- Redis 学习笔记-5种数据类型的基本操作
1.string类型 基本操作列表: GET 获取指定键对应的值 SET 设定键值 DEL 删除指定键对应的值(对所有数据类型都有效) > set hello world OK > get ...
- Redis学习笔记之Redis的对象
类型与编码: typedef struct redisObject { unsigned type:4://类型 unsigned encod ...
随机推荐
- LeetCode 1482. 制作 m 束花所需的最少天数
LeetCode 1482. 制作 m 束花所需的最少天数 题目 给你一个整数数组 bloomDay,以及两个整数 m 和 k . 现需要制作 m 束花.制作花束时,需要使用花园中 相邻的 k 朵花 ...
- 晴天小猪历险记之Hill(Dijkstra优先队列优化)
描述 这一天,他来到了一座深山的山脚下,因为只有这座深山中的一位隐者才知道这种药草的所在.但是上山的路错综复杂,由于小小猪的病情,晴天小猪想找一条需时最少的路到达山顶,但现在它一头雾水,所以向你求助. ...
- A1. 道路修建 Small(BNUOJ)
A1. 道路修建 Small Time Limit: 1000ms Memory Limit: 131072KB 64-bit integer IO format: %lld Java cl ...
- Codeforces 849A:Odds and Ends(思维)
A. Odds and Ends Where do odds begin, and where do they end? Where does hope emerge, and will they e ...
- (原创)WinForm中莫名其妙的小BUG——RichTextBox自动选择字词问题
一.前言 使用WinForm很久了,多多少少会遇到一些小BUG. 这些小BUG影响并不严重,而且稍微设置一下就能正常使用,而且微软一直也没有修复这些小BUG. 写本系列文章,是为了记录一下这些无伤大雅 ...
- 【Java例题】5.4 排序集合的使用
4.排序集合的使用.使用TreeSet模拟一个一维整数数组.其中,一维整数数组元素由Random类随机产生.最后显示排序后的结果. package chapter6; import java.util ...
- 基于Spring MVC + Spring + MyBatis的【外包人力资源管理系统】
资源下载:https://download.csdn.net/download/weixin_44893902/45600390 练习点设计:模糊查询.删除.新增 一.语言和环境 实现语言:JAVA语 ...
- 编写Java程序_输入本部门五位员工的薪资,并根据用户输入的序号为指定员工进行提薪。若用户输入序号出现越界,则提示错误。
要求说明: 输入本部门五位员工的薪资,并根据用户输入的序号为指定员工进行提薪.若用户输入序号出现越界,则提示错误. 运行效果如图: 实现代码: import java.util.Scanner; pu ...
- 编写Java程序,使用JTable表格组件展现人员信息列表
返回本章节 返回作业目录 需求说明: 使用JTable组件显现人员信息列表 实现思路: 创建一个JTable对象. 创建一个JScrollPane对象(显示横向和纵向滚动条). 将表格添加到滚动面板. ...
- Roslyn+T4+EnvDTE项目完全自动化(3) ——生成c++代码
C++语法复杂,写一个示例通过T4可生成c++代码 需求:数据库,生成c++增,删,改,查代码 数据生成c++类,包含所有字段 自动识别数据的主键Key 查询生成赋值类字段,类型转换 通过类自动生成s ...