基础数据结构

redis的所有操作都是原子的,这得益于redis是单线程的结构。redis有5种基本的数据结构,分别是:

  • string:字符串
  • k-v:键值对
  • list:链表
  • hash:哈希
  • set:集合
  • zset:有序集合

string是动态字符串,通过预分配空间减少内存的频繁分配操作,string在长度小于1M时,是成倍的增加现有空间;超过1M时,每次增加1M的空间,最大长度512MB

键值对相当于一个字典,支持增删改查,从键值对的角度来看,redis本身就可以看成一个字典。k-v结构中,k是字符串,v可以是字符串或者是整数。整数的情况下,可以使用incrby命令实现自增操作。每个k-v可以设置超时时间,使用expire命令可以设置,比如expire myKey 5,则myKey在5秒后自动销毁。自增的大小,必须在signed long之间,超过范围会报错。

list是链式结构,双端插入删除的复杂度是O(1)O(1)O(1),索引定位的复杂度是O(n)O(n)O(n)。基本操作除了插入,索引下标之外,还有ltrim操作,例如ltrim myList 1 3是只保留myList范围1-3的数据。但是,这个复杂度也是接近O(n)O(n)O(n)的,比如ltrim myList 1 -1就是O(n)O(n)O(n)的复杂度。-1表示最后一个元素,其余同理。链表在元素个数较少的情况下,会是ziplist的结构,即元素存储在一个内存块中,这样提高空间利用率,减少内存碎片。

hash是字典的结构,它的内部本身存储了很多键值对。

hash
k
value
k
value
k
value

hash的键值通过桶哈希的方式存储。当哈希冲突时,redis采取渐进式rehash操作。这个方式同时保留新旧两个hash表,在后续的定时任务或者操作中,旧表的内容会渐进式地移动到新的表,移动完成后,旧表删除,保留新表。

set就相当于C++中的std::unordered_set<T>结构,内部键值是无序唯一的,可以一次添加多个,或者删除多个,可以统计个数,判定是否是对应的成员等。

zset结构是带有权重的k-v操作,每个k存储的v是一个score,该结构使用了跳表实现,元素根据score实现有序,我们可以根据k来索引元素,也可以根据score来划分区间等操作。基本结构如下:

key
key
score
key
score
key
score

zset可以用来存放粉丝列表,比如key表示idscore表示时间等。

所有的容器,如果不存在,则操作时立刻新建一个;如果操作后没有元素了,则立刻删除。

分布式锁

分布式锁,不是我们传统意义上说的锁,比如在内存中使用锁的结构来保证同时只能有一个线程访问临界区;而是使用一个信号量的机制,设置一个标记,来表示当前是否有分布式的进程操作这个数据。

最简单的操作:

setnx mylock true

这相当于建立了一个锁,使用完成后,需要取消,命令是:

del mylock

不过,这么做有缺陷,比如设置完成锁后,且进程获取到了锁,但是进程此时由于某些原因挂了,那么就形成了死锁。一般的解决方案是设置超时时间,代码示例:

set mylock true ex 5 nx

这条命令把设置锁和设置超时结合起来,方式操作被终端,引发死锁。这是这是5秒的超时时间。上面的命令如果分开写,则是:

setnx mylock
expire mylock 5

注意,如果一个锁超过了设置的超时时间,但是使用锁的任务获取锁后,在超时时间过了之后还在操作临界区,而此时又有新的分布式进程设置了锁,那么就会出现问题。旧的进程会删除掉新进程的锁,这样会造成不安全的因素。

可重入锁可以采用引用计数的方式实现,比较麻烦,而且需要精确考虑超时时间,一般不推荐使用。这里给出可重入锁的基本使用方式,没有时间过期的方式:


如果加锁失败,可以选择直接抛出异常,或者sleep定时时间不断重试,或者放入异步队列中等待重试。

延时队列

异步消息队列

该队列用于生产者和消费者模式,比如有多个分布式生产者和消费者,消费者获取生产者的数据,但是没有顺序要求。这更适合只有一组消费者的情况。

p1
cargo queue......
p2
c1
c2

在这种情况下,可以使用list作为异步队列,使用lpushrpop读取数据。

如果队列空了,而且消费者比较多,那么可能出现消费者不断轮询redis服务器的情况,此时造成消费者CPU消耗高,同时造成redis的QPS过高。

这种情况,最简单的方式是消费者定时检测,每次轮询sleep(1)。不过容易造成延迟。另一个方式,使用listblpop或者brpop进行操作,这是阻塞读,直到有数据放入队列中。注意,如果阻塞时间过长,redis可能会切断客户端的连接。这需要我们检测到异常时进行重试。一般redis或者有关的库可以配置空闲时间。

延时队列

使用zset可以设置延时队列,每次取出超时最近的消息。

def delay():msg = str(uuid.uuid4())value = json.dump()retry_ts = time.time() + 5redis.zadd("delay_queue", retry_ts, value)# loop函数会有多个线程执行,为了保证可用性
def loop():while True:values = redis.zrangebyscore("delay_queue", 0, time.time(), start=0, num=1)if not values:time.sleep(1)continuevalue = values(0)success = redis.zrem("delay_queue", value)  # 只有一个线程能成功执行删除操作if success:msg = json.load(value)  # 执行删除成功的才可以真正获取数据handle_msg(msg)

redis队列不保证数据的可靠性

消息不保证可靠,应该是消息被发送出去,消费者是否接收到消息redis不做保证,不像一般的mq,会有ack机制,要求消费者收到消息进行ack确认,超时未确认mq会再次投递消息,而redis没有这个机制。

高级结构

位图

相当于位操作。位图本身是自动扩展的,如果偏移量超过当前的内容返回,会自动填充。注意位图的填充模式,数组顺序和位图顺序是相反的。比如:

h                  w
01101000   01100101    # 从右向左
0110100001100101       # 从左向右
bitcount # 统计指定范围1的个数
bitops   # 统计指定范围第一个1出现的位置
bitfield + get | set | incrby # 对区间进行操作
bitfield + overflow # 可以指定溢出的操作方式。默认是截断的

HyperLogLog

适用于不精确的统计情景。比如网页统计用户的访问量UV,需要对用户的ID进行去重操作。这个结构占用12KB的空间,一般来说,可能有0.x%的误差。只能添加,不能删除

pfadd hll user1  # 添加数据 O(1)
pfadd count hll # 统计数据 O(1)
pfmerge hll0 hll1 hll2 # 1 2合并到0上,O(N)

布隆过滤器

布隆过滤器,是一个不太精确的set结构,用于去重,但是节约90%的空间。基本原理是把数据哈希映射到比特位,之后根据比特位进行判断是否存在,如下图:


可以参考:https://www.cnblogs.com/zhanggguoqi/p/10571225.html

布隆过滤器,如果判断某个值存在,则此时该值可能不存在;但是如果判定一个值不存在,则一定不存在。

我们可以设置过滤器的错误率和容量。如果超过容量,那么误判率会快速上升。

redis笔记1---基础相关推荐

  1. 【《Redis深度历险》读书笔记(1)】基础:万丈高楼平地起 ——Redis 5种基础数据结构

    [时间]2021.11.16 [题目][<Redis深度历险>读书笔记(1)]基础:万丈高楼平地起 --Redis 基础数据结构 本栏目是<Redis深度历险:核心原理和应用实践&g ...

  2. 极客时间 Redis核心技术与实战 笔记(基础篇)

    Redis 概览 Redis 知识全景图 Redis 问题画像图 基础篇 基本架构 数据结构 数据类型和底层数据结构映射关系 全局哈希表 链式哈希解决哈希冲突 渐进式 rehash 不同数据结构查找操 ...

  3. 【Redis笔记】缓存穿透与缓存击穿以及应对方法

    [Redis笔记]缓存穿透与缓存击穿以及应对方法 一.缓存穿透 1. 缓存穿透概念 2. 缓存穿透解决方法 示例代码 二.缓存击穿 1. 缓存击穿概念 2. 缓存击穿解决方法 方法一:互斥锁 示例代码 ...

  4. 兄弟连NoSQL视频教程 redis笔记

    兄弟连NoSQL视频教程 redis笔记 ================================课程目录====================================== 01.N ...

  5. html5教程 w3cschool,W3Cschool学习笔记——HTML5基础教程

    HTML5 建立的一些规则:新特性应该基于 HTML.CSS.DOM 以及 JavaScript. 减少对外部插件的需求(比如 Flash) 更优秀的错误处理 更多取代脚本的标记 HTML5 应该独立 ...

  6. 狂神说Redis笔记

    以下是狂神Redis笔记,个人觉得总结的很好,故收藏一下,日后再总结一下自己的笔记 ⭐学习时间2022.1.4-2022.1.6 一.Nosql概述 为什么使用Nosql 1.单机Mysql时代 90 ...

  7. 初学Oracle的笔记(2)——基础内容(实时更新中..)

    续 初学Oracle的笔记(1)--基础内容(实时更新中..) 1.oracle中创建一张表,写法与sql server中的一样. SQL> create table Course 2 ( cn ...

  8. Docker:学习笔记(1)——基础概念

    Docker:学习笔记(1)--基础概念 Docker是什么 软件开发后,我们需要在测试电脑.客户电脑.服务器安装运行,用户计算机的环境各不相同,所以需要进行各自的环境配置,耗时耗力.为了解决这个问题 ...

  9. 学习MSCKF笔记——四元数基础

    学习MSCKF笔记--四元数基础 学习MSCKF笔记--四元数基础 1. 四元数基本性质 1.1 加法 1.2 乘法 1.3 共轭 1.4 模 1.5 逆 1.6 单位四元数 1.7 指数 1.8 对 ...

  10. 笔记-项目管理基础知识-复习要点

    1. 项目的特点 临时性(一次性).独特性.渐进明确性 2. 项目目标包括成果性目标和约束性目标 ●笔记-项目管理基础知识-项目目标 3. 项目目标的特点 多目标性.有限性.层次性 4. ▲项目管理5 ...

最新文章

  1. [JVM]常用JVM工具使用
  2. a good approach to make demonstrations at the baidu netdisk
  3. 《面向对象程序设计》第一次作业
  4. 这个Python库助你发现网络图的社区结构
  5. MySQL relay log 详细参数解释
  6. [转载] 快速学习-Mybatis框架概述
  7. python菜单栏_pyqt5——菜单和工具栏
  8. Android布局控件之LinearLayout详解
  9. OpenCV初探 —— 将OpenCV窗口链接在MFC的PictureControl控件中
  10. C# winform+ springboot + mybatis 分页查询
  11. 性能分析:处理器、磁盘I/O、进程、网络分析方法 http://www.cnblogs.com/fnng/archive/2012/10/30/2747246.html...
  12. 红帽子linux拨号上网,centos6.5宽带拨号上网的方法
  13. Python爬取某短视频热点
  14. leetcode 组合总和IV(Java)
  15. 数据库服务器如何备份详细教程!
  16. 80亿美元侵权诉讼的随想
  17. CNN 入门讲解:什么是全连接层
  18. 请描述定时器初值的计算方式_51波特率发生器定时器初值计算方法[转载]
  19. 快手服务器协议,快手协议56云服务器
  20. 将一个数组中的值按逆序重新存放。例如,原来的顺序为8,6,5,4,1。要求改为1,4,5,6,8。输出逆序后数组的整数,每两个整数之间用空格分隔。

热门文章

  1. 利用cookie爬取QQ邮箱的python脚本
  2. 如何在Linux中的virtualbox中安装Windows 10
  3. R语言如何并行处理[parallel package][向量化操作并行优化]
  4. 为什么 npm 要为每个项目单独安装一遍 node_modules?
  5. [转]香农信息论与毒药称球问题
  6. 【oracle】获取近30天日期、近5年、近6个月
  7. 敏捷开发般若敏捷系列之九:敏捷开发与本能反应
  8. MySQL之SQL优化详解(一)
  9. html css 样式中100%width 仍有白边解决办法
  10. jQuery中的筛选(六):first()、last()、has()、is()、find()、siblings()等