前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >高并发系统设计-redis技术梳理

高并发系统设计-redis技术梳理

作者头像
35岁程序员那些事
发布2020-02-24 13:01:55
1K0
发布2020-02-24 13:01:55
举报

架构师就是梳理技术,整理文档,落地技术方案,首先架构师需要梳理下redis能为我们解决什么问题,以及redis的技术门槛,redis的优势和缺点。

这里就再举一个电商交易最常见的业务场景,商品支付如何扣减库存,是否需要锁库存,高并发的业务场景下如何解决商品超卖的问题?既然问题抛出来的,架构师就应该要出可以快速落地的解决方案,这里就结合redis分布式缓存设计一套可以快速落地的解决方案。

  1. 调研redis的技术门槛

redis是分布式缓存,那么肯定要比基于java堆的本地缓存要复杂,这个是毋庸置疑的。

学习一门技术必须要通读技术框架的官网redis官网https://redis.io/ ,如果你觉得你的英文水平很菜,也可以参考中文网站,比如:http://doc.redisfans.com/,全程教你如何利用redis客户端操作缓存,当然作为一个java开发,也许只要知道如何规范的使用redis客户端操作缓存,实现高可用和高并发,redis的集群部署的高可用可能就是需要运维关注了,但是作为开发还是要知道redis集群的高可用管理的原理,不然你也不会很好的利用redis为业务解决更多诡异的问题。

redis命里梳理

键值命令梳理

DEL KEY:删除一个key或者多个key,时间复杂度O(N),N为key值的数量

DUMP KEY:序列化给定的key,并返回序列化的值,使用RESTORE命令可以反序列化为redis的键。 序列化命令的时间复杂度很高

EXISTS KEY: 检查给定 key 是否存在,时间复杂度O(1),若 key 存在,返回 1 ,否则返回 0

EXPIRE key seconds:这个命令可以说是经常用。如果一个命令只是修改(alter)一个带生存时间的 key 的值而不是用一个新的 key 值来代替(replace)它的话,那么生存时间不会被改变,如下命令可以更改key的过期时间:DEL、SET和GETSET。如下命令是不可以更改key的过期时间:INCR、LPUSH、HSET、RENAME(更改key的名称)

KEYS:查找指定匹配模式的key,KEYS命令很耗时,建议生产环境不要使用,如果真的需要查找key,就使用集合结构(set)来代替。

SCAN cursor:SCAN命令用于迭代当前数据库中的数据库键,SCAN命令是一个基于游标的迭代器(cursor based iterator)SCAN命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN命令的游标参数, 以此来延续之前的迭代过程。当SCAN 命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。其实这种迭代思想和mysql数据库的游标分页一样,都是为了减少数据库的查询次数,充分利用索引的优势。

SCAN是线程安全的,意味着多个客户端可以并发的对同一个数据集进行迭代,客户端每次执行都要传入一个游标,并在执行之后获得一个新的游标,游标包含了所有的迭代状态,服务器无需记录迭代记录的任何状态。scan 176 MATCH *11* COUNT 1000,传入游标值176 模糊匹配key值为11 ,设置count为1000,返回元素最多为1000个。SCAN可以用于线上

SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC | DESC] [ALPHA] [STORE destination]:

sort命令,是业务使用比较多的一个命令,这里要重点的梳理下,因为如果使用不当,直接影响性能。最简单的排序,sort key 升序和sort key DESC降序

使用ALPHA修饰符对字符串进行排序,sort默认排序对象为数字,sort key ALPHA对字符串排序;使用limit,控制排序之后返回的元素个数 sort key ALPHA limit 0 5(offset,count),offset指定要跳过的元素数量,count指定跳过offset个指定的元素之后,要返回的元素个数。 sort默认是按照键值进行排序,有些业务场景,例如分页场景,可能会需要按照键进行排序,再获取指定规则的元素,那么sort也是支持的。

SORT uid BY name*,name* 是一个占位符, 它先取出 uid 中的值,覆盖*,然后再用新的key值进行排序,比如取值为1,2,3,4,覆盖之后为name1、name2、name3、name4,那么就按照这些值进行排序。

SORT uid GET name*,可以根据排序结果去除键值为name*的值。可以使用组合命令,保存排序的结果,比如SORT numbers STORE sorted-numbers

可以通过将 sort命令的执行结果保存,并用 expire为结果设置生存时间,以此来产生一个sort操作的结果缓存。

这样就可以避免对sort操作的频繁调用:只有当结果集过期时,才需要再调用一次sort操作。

另外,为了正确实现这一用法,你可能需要加锁以避免多个客户端同时进行缓存重建(也就是多个客户端,同一时间进行sort操作,并保存为结果集),具体参见SETNX命令 SET(集合命令梳理)

SADD:将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。假如 key 不存在,则创建一个只包含 member 元素作成员的集合。当 key 不是集合类型时,返回一个错误。

SCARD KEY:返回集合元素的个数

SDIFF:返回集合元素之间的差集,例如:SDIFF A B,对比集合A和B,返回A中有B中没有的元素

SDIFFSTORE:返回差集并存储,SDIFFSTORE C A B,对比集合A和B,返回A中有B中没有的元素,并存储到集合c中。

SINTER:返回集合的交集,SINTER A B,返回集合A和B相同的元素

SINTERSTORE:返回集合的交集并存储,SINTERSTORE C A B 返回集合A和B相同的元素,并存储到C。

SISMEMBER key member:判断 member 元素是否集合 key 的成员。如果 member 元素是集合的成员,返回 1 。如果 member 元素不是集合的成员,或 key 不存在,返回 0 。

SMEMBERS key:返回集合 key 中的所有成员。不存在的 key 被视为空集合。

SUNION key [key ...]:返回集合并集,SUNION A B,返回A和B集合的并集

SSCAN key cursor [MATCH pattern] [COUNT count]:命令用于迭代集合键中的元素,支持增量式迭代, 它们每次执行都只会返回少量元素,可以用于生产环境

SCAN命令用于迭代当前数据库中的数据库键。SSCAN 命令用于迭代集合键中的元素。

HSCAN命令用于迭代哈希键中的键值对。ZSCAN命令用于迭代有序集合中的元素(包括元素成员和元素分值)。

SSCAN命令、 HSCAN命令和 ZSCAN命令的第一个参数总是一个数据库键。

而 SCAN命令则不需要在第一个参数提供任何数据库键 —— 因为它迭代的是当前数据库中的所有数据库键。

Hash(哈希表)

HDEL key field [field ...]:HDEL user a b 删除key user中的元素a和b,删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。在Redis2.4以下的版本里, HDEL每次只能删除单个域,如果你需要在一个原子时间内删除多个域,请将命令包含在MULTI/ EXEC块内。

HEXISTS key field:HEXISTS user a,查看哈希表 key 中,给定域 field 是否存在。时间复杂度:O(1),返回值:如果哈希表含有给定域,返回 1 。如果哈希表不含有给定域,或 key 不存在,返回 0 。

HSETNX key field value:将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在。若域 field 已经存在,该操作无效。如果 key 不存在,一个新哈希表被创建并执行HSETNX命令。

HGET key field:返回哈希表 key 中给定域 field 的值。 时间复杂度:O(1),返回值:给定域的值。当给定域不存在或是给定 key 不存在时,返回 nil 。

HGETALL key:返回哈希表 key 中,所有的域和值。在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。

HKEYS key:返回哈希表 key 中的所有域。

HLEN key:返回哈希表 key 中域的数量。

HMGET key field [field ...]:返回哈希表 key 中,一个或多个给定域的值。如果给定的域不存在于哈希表,那么返回一个 nil 值。因为不存在的 key 被当作一个空哈希表来处理,所以对一个不存在的 key 进行HMGET操作将返回一个只带有 nil 值的表。

HMSET key field value [field value ...]:

HMSET user id 1 name 2,同时将多个 field-value (域-值)对设置到哈希表 key 中。此命令会覆盖哈希表中已存在的域。如果 key 不存在,一个空哈希表被创建并执行HMSET操作。

HSETNX key field value:将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在。若域 field 已经存在,该操作无效。如果 key 不存在,一个新哈希表被创建并执行 HSETNX命令。

HVALS key:返回哈希表 key 中所有域的值。

List(列表)

LPUSH key value [value ...]:LPUSH user 1 2 3,如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表头,如果 key 不存在,一个空列表会被创建并执行 lpush操作。当 key 存在但不是列表类型时,返回一个错误。

LPUSHX key value:简单的理解就是从列表的左边插入,将值 value 插入到列表 key 的表头,当且仅当 key 存在并且是一个列表。和 LPUSH命令相反,当 key 不存在时,这个场景下,LPUSHX命令什么也不做。

LSET key index value:将列表 key 下标为 index 的元素的值设置为 value 。当 index 参数超出范围,或对一个空列表( key 不存在)进行LSET时,返回一个错误。

LTRIM key start stop:对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。执行命令 LTRIM list 0 2 ,表示只保留列表 list 的前三个元素,其余元素全部删除。业务场景下LTRIM通常与LPUSH或者RPUSH组合使用

LRANGE key start stop:返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定

RPOP key:移除并返回列表 key 的尾元素。

RPUSH key value [value ...]:就是从列表的右边插入,将一个或多个值 value 插入到列表 key 的表尾(最右边)。

如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表尾:比如对一个空列表 mylist 执行 RPUSH mylist a b c ,得出的结果列表为 a b c ,等同于执行命令 RPUSH mylist a 、 RPUSH mylist b 、 RPUSH mylist c 。如果 key 不存在,一个空列表会被创建并执行RPUSH操作。当 key 存在但不是列表类型时,返回一个错误。

LINDEX key index:返回列表 key 中,下标为 index 的元素。

LINSERT key BEFORE|AFTER pivot value:将值 value 插入到列表 key 当中,位于值 pivot 之前或之后。 当 pivot 不存在于列表 key 时,不执行任何操作。当 key 不存在时, key 被视为空列表,不执行任何操作。如果 key 不是列表类型,返回一个错误。

LLEN key:返回列表 key 的长度。如果 key 不存在,则 key 被解释为一个空列表,返回 0。如果 key 不是列表类型,返回一个错误。

LPOP key:移除并返回列表 key 的头元素。

SortedSet(有序集合)

score是有序集合排序的一个关键元素,也是有序集合的特性

ZADD key score member [[score member] [score member] ...]:将一个或多个 member 元素及其 score 值加入到有序集 key 当中。

如果某个 member 已经是有序集的成员,那么更新这个 member 的 score 值,并通过重新插入这个 member 元素,来保证该 member 在正确的位置上。

score 值可以是整数值或双精度浮点数。如果 key 不存在,则创建一个空的有序集并执行 ZADD操作。当 key 存在但不是有序集类型时,返回一个错误。

ZCARD key:返回有序集 key 的基数。

ZCOUNT key min max:返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量。

ZRANGE key start stop [WITHSCORES]:ZRANGE user 1 100 WITHSCORES,返回有序集 key 中,指定区间内的成员。其中成员的位置按 score 值递增(从小到大)来排序。具有相同 score 值的成员按字典序(lexicographical order )来排列。命令加上WITHSCORES就是为了返回score

ZREVRANGE key start stop [WITHSCORES]:返回有序集 key 中,指定区间内的成员。其中成员的位置按 score 值递减(从大到小)来排列。具有相同 score 值的成员按字典序的逆序(reverse lexicographical order)排列。

Pub/Sub(发布订阅)

PSUBSCRIBE pattern [pattern ...]:订阅一个或多个符合给定模式的频道。返回值:接收到的信息

PUBLISH channel message:将信息 message 发送到指定的频道 channel 。

PUBSUB <subcommand> [argument [argument ...]]

PUBSUB CHANNELS[pattern]:活跃频道指的是那些至少有一个订阅者的频道, 订阅模式的客户端不计算在内。pattern 参数是可选的:如果不给出 pattern 参数,那么列出订阅与发布系统中的所有活跃频道。如果给出 pattern 参数,那么只列出和给定模式 pattern 相匹配的那些活跃频道。

PUBSUB NUMSUB[channel-1.....channel-n]:返回给定频道的订阅者数量, 订阅模式的客户端不计算在内。复杂度 O(N) , N 为给定频道的数量。返回值 一个多条批量回复(Multi-bulk reply),回复中包含给定的频道,以及频道的订阅者数量。 格式为:频道 channel-1 , channel-1 的订 阅者数量,频道 channel-2 , channel-2 的订阅者数量,诸如此类。 回复中频道的排列顺序和执行命令时给定频道的排列顺序一致。 不给定任何频道而直接调用这个命令也是可以的, 在这种情况下, 命令只返回一个空列表。

PUBSUB NUMPAT:复杂度 O(1),返回订阅模式的数量。注意, 这个命令返回的不是订阅模式的客户端的数量, 而是客户端订阅的所有模式的数量总和。

PUNSUBSCRIBE [pattern....]:指示客户端退订所有给定模式。如果没有模式被指定,也即是,一个无参数的 PUNSUBSCRIBE 调用被执行,那么客户端使用 PUNSUBSCRIBE 命令订阅的所有模式都会被退订。在这种情况 下,命令会返回一个信息,告知客户端所有被退订的模式。

SUBSCRIBE channel [channel ...]:订阅给定的一个或多个频道的信息。

UNSUBSCRIBE [channel [channel ...]]指示客户端退订给定的频道。如果没有频道被指定,也即是,一个无参数的 UNSUBSCRIBE 调用被执行,那么客户端使用SUBSCRIBE 命令订阅的所有频道都会被退订。在这种情况下,命令会返回一个信息,告知客户端所有被退订的频道。

Transaction(事务)

DISCARD:取消事务,放弃执行事务块内的所有命令。如果正在使用 Watch 命令监视某个(或某些) key,那么取消所有监视,等同于执行命令UNWATCH。

EXEC:执行所有事务块内的命令。假如某个(或某些) key 正处于WATCH命令的监视之下,且事务块中有和这个(或这些) key 相关的命令,那么EXEC命令只在这个(或这些) key 没有被其 他命令所改动的情况下执行并生效,否则该事务被打断(abort)。时间复杂度:事务块内所有命令的时间复杂度的总和。返回值:事务块内所有命令的返回值,按命令执行的先后顺序排列。当操作被打断时,返回空值 nil 。

MULTI:标记一个事务块的开始。事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由命令原子性(atomic)地执行。

UNWATCH:取消 WATCH命令对所有 key 的监视。如果在执行WATCH命令之后,EXEC命令或 DISCARD命令先被执行了的话,那么就不需要再执行 UNWATCH了。

因为EXEC命令会执行事务,因此 WATCH 命令的效果已经产生了;而DISCARD命令在取消事务的同时也会取消所有对 key 的监视,因此这两个命令执行之后,就没有必要执行UNWATCH了。

WATCH key [key ...]:监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

2. 调研redis的结论

redis可以供业务使用的功能:

1)数据存储:你可以把redis的存储功能理解为是java容器集合,只不过人家比较高大尚,是分布式容器集合,是所有接入集群的客户端应用都能拿到这些集合数据,而java容器集合自身不能跨jvm。

2)分布式锁

3)内存分页

4)消息队列:替代mq的一种分布式解决方案

3.redis给业务带来的高并发的解决方案

由于redis强大的客户端api,以及redis的天生的线程安全特性(底层通信是单线程的),完全可以用于高并发交易系统中的交易降级处理技术。

设计一套能够抗大流量的高并发系统,必须要充分利用好redis,所谓缓存为王,真的很重要。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-07-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 架构随笔录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档