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)

常见命令

  1. set key value [ex seconds][px milliseconds][nx|xx]: 设置给定键和值

  2. get key : 获取值

  3. del key :删除存储在给定键中的值

  4. incr key : 将 key 对应的值加1

  5. decr key : 将 key 对应的值减1

  6. incrby key amount: 将key 对应的值加上整数

  7. decrby key amount:将key 对应的值减去整数

应用场景

  • 缓存:可以将常见的字符串、图片等信息缓存在 redis,mysql 作为持久化层。降低 mysql 的读写压力

  • 计数器: 实现快速计数、查询缓存,同时数据可以异步落地到其他数据源。

  • 共享Session:分布式服务器将用户的 Session 进行集中的管理,每次用户更新或者查询登录信息都直接从 Redis 中集中获取。

Hash

哈希类型指的是 value 本身又是一个键值对结构,比如 value = {{field1, value1}, ... {fieldN, valueN}}。

常见命令

  1. hset hash-key sub-key1 value1 :添加键值对
  2. hget hash-key key1 : 获取制定散列键的值
  3. hgetall hash-key :获取哈希中包含的所有键值对
  4. 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)

常见命令

  1. sadd key value : 向集合中添加一个或者多个成员
  2. scard key : 获取集合中的成员数
  3. smember key member : 返回集合中的所有成员
  4. sismember key member : 判断 member 元素是否是集合 key 的成员

应用场景

  • 标签:给用户添加标签,所有这样有同一标签或者类似的可以推荐关注的事情或者关注的人

zset

有序集合 zset 相对于 set 而言,其内部的元素可以进行排序,它是通过给每个元素设置一个分数来作为排序的依据。

常见命令

  1. zadd zset-key int member1 : 将一个带有给定分值的成员添加到有序的集合中
  2. zrange zset-key 0-1 : 根据元素在有序集合中所处的位置,从有序集合中获取对应的元素
  3. 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,如图所示。

常用命令

  1. pfadd : 用于在基数统计中添加元素,添加成功会返回1

  2. pfcount:用于计算一个或者多个 HyperLogLogs 的独立总数

  3. pfmerge:求出多个HyperLogLogs 的并集并赋值给 destkey

应用场景

  • IP 数 : 用于统计某个时段的 IP 或者用户数

Bitmaps

它本身不是一种数据结构,实际上就是字符串,但是它可以对字符串的位进行操作

Bitmaps 相当于一个以位为单位的数组,数组的每个单元只能存储0 和 1 , 数组的下标在 Bitmaps 中叫做偏移量。

常用命令

  1. setbit key offset value : 设置键和偏移量的值
  2. getbit key offset: 获取键的第 offset 位的值
  3. bitcount key : 统计该键的次数值

应用场景

  • 活跃用户分析: 存储和统计一天中活跃的用户

Geo

Redis3.2版本提供了GEO(地理信息定位)功能,支持存储地理位 置信息用来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能

常用命令

  1. geoadd: 添加地理位置信息

  1. geopos: 获取地理位置信息

  2. geodist: 获取两个地理位置的距离

  3. georadius: 获取范围内的信息位置集合-->附近的人

  4. geohash: 将二维经纬度转换为一维的字符串

  5. 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 执行的步骤:

  1. 根据给定的 key,在数据库字典中查找对应的 redisObject 对象,没找到就返回null
  2. 检查找到的 redisObject 的 type 属性和执行命令所需要的类型是否相同,如果不相同就返回类型错误
  3. 根据 redisObject 的 encoding 属性所指定的编码,选择合适的操作函数来处理底层的数据结构
  4. 最后返回命令的操作结构

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 数据类型和对象机制的更多相关文章

  1. Redis学习笔记一:数据结构与对象

    1. String(SDS) Redis使用自定义的一种字符串结构SDS来作为字符串的表示. 127.0.0.1:6379> set name liushijie OK 在如上操作中,name( ...

  2. StackExchange.Redis学习笔记(一) Redis的使用初探

    Redis Redis将其数据库完全保存在内存中,仅使用磁盘进行持久化. 与其它键值数据存储相比,Redis有一组相对丰富的数据类型. Redis可以将数据复制到任意数量的从机中 Redis的安装 官 ...

  3. Redis学习笔记(4) Redis事务、生存时间及排序

    1. Redis事务 Redis中的事务(transaction)是一组命令的集合,一个事务中的命令要么都执行,要么都不执行.事务的原理是先将属于一个事务的命令发送给Redis,然后再让Redis依次 ...

  4. Redis学习笔记(1) Redis介绍及基础

    1. Redis的特性 (1) 存储结构 Redis(Remote Dictionary Server,远程字典服务器)是以字典结构存储数据,并允许其他应用通过TCP协议读写字典中的内容.Redis支 ...

  5. redis学习笔记之redis简介

    redis简介 Redis是一个开源的,高性能的,基于键值对的缓存与存储系统,通过设置各种键值数据类型来适应不同场景下的缓存与存储需求.同事redis的诸多高层级功能使其可以胜任消息队列,任务队列等不 ...

  6. Redis学习笔记之Redis单机,伪集群,Sentinel主从复制的安装和配置

    0x00 Redis简介 Redis是一款开源的.高性能的键-值存储(key-value store).它常被称作是一款数据结构服务器(data structure server). Redis的键值 ...

  7. Redis学习笔记--五种数据类型的使用场景

    String 1.String 常用命令: 除了get.set.incr.decr mget等操作外,Redis还提供了下面一些操作: 获取字符串长度 往字符串append内容 设置和获取字符串的某一 ...

  8. StackExchange.Redis学习笔记(二) Redis查询 五种数据类型的应用

    ConnectionMultiplexer ConnectionMultiplexer 是StackExchange.Redis的核心对象,用这个类的实例来进行Redis的一系列操作,对于一个整个应用 ...

  9. Redis 学习笔记-5种数据类型的基本操作

    1.string类型 基本操作列表: GET 获取指定键对应的值 SET 设定键值 DEL 删除指定键对应的值(对所有数据类型都有效) > set hello world OK > get ...

  10. Redis学习笔记之Redis的对象

    类型与编码: typedef struct redisObject {                unsigned type:4://类型               unsigned encod ...

随机推荐

  1. 【LeetCode】396. Rotate Function 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址:https://leetcode.com/problems/rotate-fu ...

  2. 带你熟悉鸿蒙轻内核Kconfig使用指南

    摘要:本文介绍了Kconfig的基础知识,和鸿蒙轻内核的图形化配置及进阶的使用方法. 本文分享自华为云社区<鸿蒙轻内核Kconfig使用笔记>,作者: zhushy. 1. Kconfig ...

  3. 编写Java程序,创建Dota游戏中的兵营类,兵营类有一个类成员变量count、一个实例变量name和另一个实例变量selfCount。

    返回本章节 返回作业目录 需求说明: 创建Dota游戏中的兵营类 兵营类有一个类成员变量count.一个实例变量name和另一个实例变量selfCount. count表示的是兵营已经创建士兵的总数: ...

  4. 编写Java程序,模拟五子棋博弈过程中的异常声明和异常抛出

    返回本章节 返回作业目录 需求说明: 模拟五子棋博弈过程中的异常声明和异常抛出,判断用户所下棋子的位置,是否超越了棋盘的边界. 棋盘的横坐标的范围为0-9,纵坐标范围为0-14,如果用户所放棋子的坐标 ...

  5. ElasticSearch+Kibana安装部署

    在安装ElasticSearch时遇到了很多坑,所以在这里做个笔记记录一下. 首先我考虑的是使用docker进行部署,结果发现虚拟机直接内存溢出,我也是无解了,也就是说使用docker部署还得注意容器 ...

  6. CAS学习笔记三:SpringBoot自动配置与手动配置过滤器方式集成CAS客户端

    本文目标 基于SpringBoot + Maven 分别使用自动配置与手动配置过滤器方式集成CAS客户端. 需要提前搭建 CAS 服务端,参考 https://www.cnblogs.com/hell ...

  7. Linux_接收文件(rz)和发送文件(sz)

    我们连接linux通常是使用一些ssh工具进行远程访问连接的,在使用过程中,少不了将本地文件上传到linux或将linux上的文件下载到本地,每次使用ftp比较繁琐而且浪费时间.我们可以使用linux ...

  8. 初识python: 递归函数

    定义: 在函数内,可以调用其他函数,如果一个函数在内部调用自己,返回值中包含函数名,这个函数就是递归函数. 特性: 1.必须要有明确的结束条件: 2.每进入更深一层递归时,问题规模相对上次递归都应该有 ...

  9. python 面向对象:多态和多态性

    很多人喜欢将多态与多态性二者混为一谈,然后百思不得其解,其实只要分开看,就会很明朗. 一.多态 多态指的是一类事物有多种形态.(一个抽象类有多个子类,因而多态的概念依赖于继承) 比如动物有多种形态:人 ...

  10. alias 中使用 awk

    alias hehistory10='history |awk "{print \$2}"|sort|uniq -c|sort -rn|head -10' alias lv='ls ...