专栏首页架构师玄学之路高并发系统设计-redis技术梳理

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

架构师就是梳理技术,整理文档,落地技术方案,首先架构师需要梳理下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,所谓缓存为王,真的很重要。

本文分享自微信公众号 - 架构师玄学之路(andy_aty),作者:胡弦

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-07-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 读-成功人士的七个习惯-这本书的自我感受

    第一点我自己理解,是要我们在生活中多一点思考,提前准备,等到事情发生的时候,我们可以有更多的选择机会

    用户6969969
  • 如何说服老板实施微服务

    如果你向你老板汇报,你怎么说服老板实施微服务,那么首先你自己应该搞清楚微服务的本质,微服务的商业价值,微服务实施的成本和收益,微服务是否是银弹。

    用户6969969
  • Nacos源码分析系列之Naming模块-如何运行篇

    Naming模块其实Nacos框架实现服务治理功能的核心模块,这个模块具备很多核心功能,本次先从如何运行开始剖析。

    用户6969969
  • Redis命令全集

    总的来说,Redis是一个基于内存的高性能的键值型数据库,也就是常说的NoSQL,可以用来作为数据库或者缓存.并且支持多种数据结构,包括字符串,散列,列表,集合...

    呼延十
  • 网银安全控件问题

    网银的密码输入控件是通过直接读键盘设备IO获取的输入。需要使用驱动级的键盘模拟输入技术才能输进去。

    周小董
  • Redis-各数据类型常用命令(含使用示例)

    Redis的全部命令详情可以在官网查询。 点此Redis系列文章专栏 命令不自己敲一遍,都是白搭,就算忘了也可以翻这篇博客。

    唔仄lo咚锵
  • Redis常用命令详解

    如果客户端处于频道订阅模式下,它将是一个multi-bulk返回,第一次时返回”pong”,之后返回空(empty bulk),除非命令后面更随了参数

    JavaEdge
  • jedis五种数据类型的方法解释

    1)连接操作命令 quit:关闭连接(connection) auth:简单密码认证 help cmd: 查看cmd帮助,例如:hel...

    HUC思梦
  • redis

    (redis.memcache(内存数据库,高速缓存),mongodb(文档数据库))

    Dean0731
  • Redis学习笔记(五)Redis数据存储类型之list

    ​ (2)添加数据 lpush key value1 value2…(从左边压入数据)

    萌萌哒的瓤瓤

扫码关注云+社区

领取腾讯云代金券