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

相关文章

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示

Redis持久化机制之RDB与AOF的使用

《Redis持久化机制之RDB与AOF的使用》:本文主要介绍Redis持久化机制之RDB与AOF的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Redis持久化机制-RDB与AOF一、RDB持久化机制1、RDB简介2、RDB的工作原理3、RDB的优缺点4

基于Redis实现附近商铺查询功能

《基于Redis实现附近商铺查询功能》:本文主要介绍基于Redis实现-附近商铺查询功能,这个功能将使用到Redis中的GEO这种数据结构来实现,需要的朋友可以参考下... 目录基于Redis实现-附近查询1.GEO相关命令2.使用GEO来实现以下功能3.使用Java实现简China编程单的附近商铺查询4.Red

Redis高可用-主从复制、哨兵模式与集群模式详解

《Redis高可用-主从复制、哨兵模式与集群模式详解》:本文主要介绍Redis高可用-主从复制、哨兵模式与集群模式的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录Redis高可用-主从复制、哨兵模式与集群模式概要一、主从复制(Master-Slave Repli

Redis中RedisSearch使用及应用场景

《Redis中RedisSearch使用及应用场景》RedisSearch是一个强大的全文搜索和索引模块,可以为Redis添加高效的搜索功能,下面就来介绍一下RedisSearch使用及应用场景,感兴... 目录1. RedisSearch的基本概念2. RedisSearch的核心功能(1) 创建索引(2

Redis中HyperLogLog的使用小结

《Redis中HyperLogLog的使用小结》Redis的HyperLogLog是一种概率性数据结构,用于统计唯一元素的数量(基数),本文主要介绍了Redis中HyperLogLog的使用小结,感兴... 目录 一、HyperlogLog 是什么?️ 二、使用方法1. 添加数据2. 查询基数China编程3.

Redis中的数据一致性问题以及解决方案

《Redis中的数据一致性问题以及解决方案》:本文主要介绍Redis中的数据一致性问题以及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、Redis 数据一致性问题的产生1. 单节点环境的一致性问题2. 网络分区和宕机3. 并发写入导致的脏数据4. 持

浅谈Redis Key 命名规范文档

《浅谈RedisKey命名规范文档》本文介绍了Redis键名命名规范,包括命名格式、具体规范、数据类型扩展命名、时间敏感型键名、规范总结以及实际应用示例,感兴趣的可以了解一下... 目录1. 命名格式格式模板:示例:2. 具体规范2.1 小写命名2.2 使用冒号分隔层级2.3 标识符命名3. 数据类型扩展命

Redis实现分布式锁全解析之从原理到实践过程

《Redis实现分布式锁全解析之从原理到实践过程》:本文主要介绍Redis实现分布式锁全解析之从原理到实践过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、背景介绍二、解决方案(一)使用 SETNX 命令(二)设置锁的过期时间(三)解决锁的误删问题(四)Re

一文带你搞懂Redis Stream的6种消息处理模式

《一文带你搞懂RedisStream的6种消息处理模式》Redis5.0版本引入的Stream数据类型,为Redis生态带来了强大而灵活的消息队列功能,本文将为大家详细介绍RedisStream的6... 目录1. 简单消费模式(Simple Consumption)基本概念核心命令实现示例使用场景优缺点2