Redis RU101课程 Introduction to Redis Data Structures 第3周学习笔记

2024-02-04 12:38

本文主要是介绍Redis RU101课程 Introduction to Redis Data Structures 第3周学习笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Transactions

Introduction

为了保证单条命令的原子性,Redis使用了单线程,命令都是顺序依次执行(unlink是个例外,是异步的)。

事务保证了将多条命令作为一个单元执行。

Using Transactions

事务所有命令:

127.0.0.1:6379> help @transactionsDISCARD -summary: Discard all commands issued after MULTIsince: 2.0.0EXEC -summary: Execute all commands issued after MULTIsince: 1.2.0MULTI -summary: Mark the start of a transaction blocksince: 1.2.0UNWATCH -summary: Forget about all watched keyssince: 2.2.0WATCH key [key ...]summary: Watch the given keys to determine execution of the MULTI/EXEC blocksince: 2.2.0

MULTI相当于begin transaction, MULTI后面的命令会存入命令队列,EXEC一次性执行队列中所有命令,DISCARD放弃队列中所有命令。

可以用2个client来演示事务的隔离性。

multi中不能再嵌套multi,也就是事务不能嵌套。

对于语法错误和数据类型操作错误,不会影响事务中其它的操作,或者说不会引发rollback。其实redis就不支持rollback:

127.0.0.1:6379> set key01 1
OK
127.0.0.1:6379> set key02 a
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby key01 1
QUEUED
127.0.0.1:6379> incrby key02 1
QUEUED
127.0.0.1:6379> exec
1) (integer) 2
2) (error) ERR value is not an integer or out of range
127.0.0.1:6379> get key01
"2"

rollback的开销很大,避免rollback也保证了最小延迟和最大吞吐。

对于系统错误,如内存不够,Redis会保证不会出现部分写和不一致性。

传统数据库交易中的命令是马上执行,因此消耗了系统资源;Redis中是将所有命令放入队列,然后一次性执行。

扩展阅读

  • Redis Clients Handling
  • Transactions

Optimistic Concurrency Control

Optimistic Concurrency Control是指监控1个或多个key的变化,如果改变了就放弃事务。这通常是由于你已经读取了这个值。

Optimistic Concurrency Control是通过WATCH和UNWATCH实现的。WATCH必须在事务开始前,事务结束后,会自动UNWATCH。

WATCH命令不是全局的,只影响到调用它的客户端。

client1> set key01 200
OK
client1> watch key01
OK
client1> multi
OK
client1> incrby key01 1
QUEUED
client2> get key01
"200"
client2> decr key01
(integer) 199
client1> exec
(nil)
client1> get key01
"199"

EXEC何时会失败呢? 这里的失败可认为是DISCARD。当命令有语法错误,或WATCH的key被更改时,都会失败。但如果只是操作错误的数据类型,如对字符串加1,则只是忽略这个命令而已。

Object Storage with Hashes

Introduction

用String和Hash都可以存取对象存储,但前者不能增量存取,后者可以单独操作filed,如读写,测试存在,但TTL是针对整个key来设的。

Storing a Simple Object

Hash的多field加value的结构非常类似JSON,而且灵活的schema避免了alter table操作。

HSET可实现JSON结构。相关命令包括HGET, HMGET和HGETALL(阻塞操作,建议小于100 field时用),HSCAN(非阻塞操作)。

小数据集时用HMGET方便,大数据集时建议HSCAN。

HSET时,会自动创建不存在的field,相关包括HEXISTS, HSETNX。

HINCR,HINCRBYFLOAT可对field执行数字操作。

HDEL可删除filed。

Storing Complex Objects

复杂对象,在关系型数据库里就是主从表,在NOSQL中就是带子文档的JSON。

可以用3种方式实现:

  1. 多个Hash
  2. 多个Hash+Set
  3. 单个Hash

Redis推荐方式3,完全平的方式。好处是简单,支持原子操作,无需事务。实现非常类似于层级文件系统到对象存储文件名的映射,用不同的filed name即可实现。例如假设B为子文档:

hmset key B:C value B:D value

不好的地方是删除子文档时需要删除多个field。还有就是设计上的考虑,子文档放在那个父文档中,后续子文档移动时会涉及很多操作。还有就是对象会比拆开存要大。

第2种方式即将子文档单独存为Hash,它和父文档的关系需要通过应用体现,当然在key命名上也可以提示。这种方式的好处为,可独立存放,可扩展,可单独设置TTL。不好的地方是一个对象需要多个key表示,关系需要维护。然后对象所有key需要存在一个shard,这是后话。

Use Case: Inventory Control

Use Case Overview

售票系统,每个用户可以买多张票,有3场比赛,柔道资格赛,男子100m决赛和女子4x100接力预赛。

Inventory Control

流程参考这个图:

在此流程中,减库存和生成订单需放在一个事务中。

示例程序为uc02-inventory-control/inventory.py

示例程序演示了3个测试场景,依次由简单到复杂:

  • 票不够(仅检查票)
  • 票够钱不够,这里假设用户为joan时,钱总是不够(检查票和钱)
  • 持票超过30秒,票返回库存

7个用户, 数据结构如下:

127.0.0.1:6379> scan 0 match uc02:customer* count 30000
1) "0"
2) 1) "uc02:customer:jim"2) "uc02:customer:joan"3) "uc02:customer:jamie"4) "uc02:customer:amy"5) "uc02:customer:mary"6) "uc02:customer:bill"7) "uc02:customer:fred"127.0.0.1:6379> type uc02:customer:jim
hash127.0.0.1:6379> hgetall uc02:customer:jim
1) "id"
2) "jim"
3) "customer_name"
4) "jim somebody"

有3个比赛的票,数据结构如下, 注意其中的余票数和票价:

127.0.0.1:6379> scan 0 match uc02:event:* count 30000
1) "0"
2) 1) "uc02:event:320-GHI-921"2) "uc02:event:123-ABC-723"3) "uc02:event:737-DEF-911"
127.0.0.1:6379> type uc02:event:320-GHI-921
hash
127.0.0.1:6379> hgetall uc02:event:320-GHI-9211) "sku"2) "320-GHI-921"3) "name"4) "Womens Judo Qualifying"5) "disabled_access"6) "False"7) "medal_event"8) "False"9) "venue"
10) "Nippon Budokan"
11) "category"
12) "Martial Arts"
13) "capacity"
14) "14471"
15) "available:General"
16) "500"
17) "price:General"
18) "15.25"
19) "held:General"
20) "0"127.0.0.1:6379> hgetall uc02:event:123-ABC-7231) "sku"2) "123-ABC-723"3) "name"4) "Men's 100m Final"5) "disabled_access"6) "True"7) "medal_event"8) "True"9) "venue"
10) "Olympic Stadium"
11) "category"
12) "Track & Field"
13) "capacity"
14) "60102"
15) "available:General"
16) "10"
17) "price:General"
18) "25.0"127.0.0.1:6379> hgetall uc02:event:737-DEF-9111) "sku"2) "737-DEF-911"3) "name"4) "Women's 4x100m Heats"5) "disabled_access"6) "True"7) "medal_event"8) "False"9) "venue"
10) "Olympic Stadium"
11) "category"
12) "Track & Field"
13) "capacity"
14) "60102"
15) "available:General"
16) "10"
17) "price:General"
18) "19.5"
19) "held:General"
20) "0"

为防止在订票期间库存发生改变,程序使用WATCH监控了相关的库存(例如uc02:event:123-ABC-723)。

订单的数据结构如下,和库存之间通过sku关联:

127.0.0.1:6379> scan 0 match uc02:sales_order:* count 30000
1) "0"
2) 1) "uc02:sales_order:CUJXZN-ITBCOW"2) "uc02:sales_order:RSNKID-SOCIFH"
127.0.0.1:6379> type uc02:sales_order:CUJXZN-ITBCOW
hash
127.0.0.1:6379> hgetall uc02:sales_order:CUJXZN-ITBCOW1) "order_id"2) "CUJXZN-ITBCOW"3) "customer"4) "bill"5) "tier"6) "General"7) "qty"8) "5"9) "cost"
10) "125.0"
11) "event_sku"
12) "123-ABC-723"
13) "ts"
14) "1606361972"

Python代码建议好好看看。

Reservations

场景2中加入了信用卡验证的环节。因此售票过程分多个事务完成。

首先扣票是第一个事务,扣票成功后会生成临时的持票记录。第二个事务围为信用卡验证环节,如果信用卡验证失败,就将持有票返回票库,成功则生成订单,清空持票记录。

通过调试python(python -m pdb)程序,我们得到持票的数据结构, 包括扣票数量,票级别和扣票时间,field名字的后缀时随机字符串:

127.0.0.1:6379> scan 0 match uc02:ticket_hold* count 30000
1) "0"
2) 1) "uc02:ticket_hold:737-DEF-911"
127.0.0.1:6379> type uc02:ticket_hold:737-DEF-911
hash
127.0.0.1:6379> hgetall uc02:ticket_hold:737-DEF-911
1) "qty:KUEBNB-BKFHGK"
2) "5"
3) "tier:KUEBNB-BKFHGK"
4) "General"
5) "ts:KUEBNB-BKFHGK"
6) "1606368660"

对应的库存记录中,在held:General字段也会记录扣票数量。

Expiration of Reservations

场景3模拟的是自动将持票退回库存的场景。比如购票过程中网断了。程序自动生成了3条持票记录,然后进入循环,每个1秒检查持票记录,超过30秒后自动将票返回票库。直到没有持票就退出循环。注意,这里并没有设TTL。

持票定义如下,可以看到到期时间一次为14秒后,8秒后和立刻:

  holds = {'qty:VPIR6X': 3, 'tier:VPIR6X': tier, 'ts:VPIR6X': int(cur_t - 16),'qty:B1BFG7': 5, 'tier:B1BFG7': tier, 'ts:B1BFG7': int(cur_t - 22),'qty:UZ1EL0': 7, 'tier:UZ1EL0': tier, 'ts:UZ1EL0': int(cur_t - 30)}

因此输出如下:

uc02:event:320-GHI-921
== Create ticket holds, expire > 30 sec, return tickets to inventory
320-GHI-921, Available:485, Reservations:['3', '5', '7']
320-GHI-921, Available:492, Reservations:['3', '5', None]
320-GHI-921, Available:492, Reservations:['3', '5', None]
320-GHI-921, Available:492, Reservations:['3', '5', None]
320-GHI-921, Available:492, Reservations:['3', '5', None]
320-GHI-921, Available:492, Reservations:['3', '5', None]
320-GHI-921, Available:492, Reservations:['3', '5', None]
320-GHI-921, Available:492, Reservations:['3', '5', None]
320-GHI-921, Available:492, Reservations:['3', '5', None]
320-GHI-921, Available:497, Reservations:['3', None, None]
320-GHI-921, Available:497, Reservations:['3', None, None]
320-GHI-921, Available:497, Reservations:['3', None, None]
320-GHI-921, Available:497, Reservations:['3', None, None]
320-GHI-921, Available:497, Reservations:['3', None, None]
320-GHI-921, Available:497, Reservations:['3', None, None]
320-GHI-921, Available:500, Reservations:[None, None, None]

最后的建议是,当持票记录多时,建议从Hash改为Sorted Set,然后将时间戳作为value。这样方便提取最先过期的持票记录。
None, None]
320-GHI-921, Available:497, Reservations:[‘3’, None, None]
320-GHI-921, Available:497, Reservations:[‘3’, None, None]
320-GHI-921, Available:497, Reservations:[‘3’, None, None]
320-GHI-921, Available:500, Reservations:[None, None, None]


最后的建议是,当持票记录多时,建议从Hash改为Sorted Set,然后将时间戳作为value。这样方便提取最先过期的持票记录。

这篇关于Redis RU101课程 Introduction to Redis Data Structures 第3周学习笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/677530

相关文章

Redis分片集群的实现

《Redis分片集群的实现》Redis分片集群是一种将Redis数据库分散到多个节点上的方式,以提供更高的性能和可伸缩性,本文主要介绍了Redis分片集群的实现,具有一定的参考价值,感兴趣的可以了解一... 目录1. Redis Cluster的核心概念哈希槽(Hash Slots)主从复制与故障转移2.

Redis 中的热点键和数据倾斜示例详解

《Redis中的热点键和数据倾斜示例详解》热点键是指在Redis中被频繁访问的特定键,这些键由于其高访问频率,可能导致Redis服务器的性能问题,尤其是在高并发场景下,本文给大家介绍Redis中的热... 目录Redis 中的热点键和数据倾斜热点键(Hot Key)定义特点应对策略示例数据倾斜(Data S

redis+lua实现分布式限流的示例

《redis+lua实现分布式限流的示例》本文主要介绍了redis+lua实现分布式限流的示例,可以实现复杂的限流逻辑,如滑动窗口限流,并且避免了多步操作导致的并发问题,具有一定的参考价值,感兴趣的可... 目录为什么使用Redis+Lua实现分布式限流使用ZSET也可以实现限流,为什么选择lua的方式实现

Redis中管道操作pipeline的实现

《Redis中管道操作pipeline的实现》RedisPipeline是一种优化客户端与服务器通信的技术,通过批量发送和接收命令减少网络往返次数,提高命令执行效率,本文就来介绍一下Redis中管道操... 目录什么是pipeline场景一:我要向Redis新增大批量的数据分批处理事务( MULTI/EXE

Redis中高并发读写性能的深度解析与优化

《Redis中高并发读写性能的深度解析与优化》Redis作为一款高性能的内存数据库,广泛应用于缓存、消息队列、实时统计等场景,本文将深入探讨Redis的读写并发能力,感兴趣的小伙伴可以了解下... 目录引言一、Redis 并发能力概述1.1 Redis 的读写性能1.2 影响 Redis 并发能力的因素二、

Redis中的常用的五种数据类型详解

《Redis中的常用的五种数据类型详解》:本文主要介绍Redis中的常用的五种数据类型详解,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Redis常用的五种数据类型一、字符串(String)简介常用命令应用场景二、哈希(Hash)简介常用命令应用场景三、列表(L

Redis解决缓存击穿问题的两种方法

《Redis解决缓存击穿问题的两种方法》缓存击穿问题也叫热点Key问题,就是⼀个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击,本文给大家介绍了Re... 目录引言解决办法互斥锁(强一致,性能差)逻辑过期(高可用,性能优)设计逻辑过期时间引言缓存击穿:给

Redis中如何实现商品秒杀

《Redis中如何实现商品秒杀》:本文主要介绍Redis中如何实现商品秒杀问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录技术栈功能实现步骤步骤一:准备商品库存数据步骤二:实现商品秒杀步骤三:优化Redis性能技术讲解Redis的List类型Redis的Set

Redis如何实现刷票过滤

《Redis如何实现刷票过滤》:本文主要介绍Redis如何实现刷票过滤问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录引言一、概述二、技术选型三、搭建开发环境四、使用Redis存储数据四、使用SpringBoot开发应用五、 实现同一IP每天刷票不得超过次数六

Java进阶学习之如何开启远程调式

《Java进阶学习之如何开启远程调式》Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,:本文主要介绍Java进阶学习之如何开启远程调式的相关资料,需要的朋友... 目录概述Java远程调试的开启与底层原理开启Java远程调试底层原理JVM参数总结&nbsMbKKXJx