redis学习笔记

什么是redis?

redis是一个Nosql数据库,与传统数据库相比,redis的数据是存在于内存之中,也因此有较高的读写性能,被广泛地应用于缓存当中。
另外,redis除了做缓存之外,也常常用于分布式锁和消息队列。
redis提供了5种基本数据类型和三种特殊的数据类型用于支持不同的业务场景。redis还支持事务、持久化、Lua脚本和多种的集群方案。

redis 5+3数据类型

string:

介绍

string 是redis最简单的数据类型,是简单的key-value形式,value可以是string也可以是数字。

虽然redis用c语言编写,但并没有沿用c的字符串表示,而是自己构建了一种简单动态字符串(SDS),相比原生的c字符串,SDS不光可以保存文本数据还可以保存二进制数据,并且能在O(1)时间获取字符串长度。
使用场景
  • 常规技术
  • 微博数
  • 粉丝数
  • token令牌
基本使用命令

基本

127.0.0.1:6379> set key value #设置 key-value 类型的值
OK
127.0.0.1:6379> get key # 根据 key 获得对应的 value
"value"
127.0.0.1:6379> exists key  # 判断某个 key 是否存在
(integer) 1
127.0.0.1:6379> strlen key # 返回 key 所储存的字符串值的长度。
(integer) 5
127.0.0.1:6379> del key # 删除某个 key 对应的值
(integer) 1
127.0.0.1:6379> get key
(nil)

批量

127.0.0.1:6379> mset key1 value1 key2 value2 # 批量设置 key-value 类型的值
OK
127.0.0.1:6379> mget key1 key2 # 批量获取多个 key 对应的 value
1) "value1"
2) "value2"

计数器

127.0.0.1:6379> set number 1
OK
127.0.0.1:6379> incr number # 将 key 中储存的数字值增一
(integer) 2
127.0.0.1:6379> get number
"2"
127.0.0.1:6379> decr number # 将 key 中储存的数字值减一
(integer) 1
127.0.0.1:6379> get number
"1"

过期

127.0.0.1:6379> expire key  60 # 数据在 60s 后过期
(integer) 1
127.0.0.1:6379> setex key 60 value # 数据在 60s 后过期 (setex:[set] + [ex]pire)
OK
127.0.0.1:6379> ttl key # 查看数据还有多久过期
(integer) 56

List:

介绍

List是一个单key多value 有序集合,它类似于双端队列,可以在左右插入删除元素,如果键不存在,则会创建先的List,如果值全部移除,对应的键也就消失了

使用场景
  • 聊天系统
  • 博客评论
  • 评论分页加载
基本使用命令

实现队列

127.0.0.1:6379> rpush myList value1 # 向 list 的头部(右边)添加元素
(integer) 1
127.0.0.1:6379> rpush myList value2 value3 # 向list的头部(最右边)添加多个元素
(integer) 3
127.0.0.1:6379> lpop myList # 将 list的尾部(最左边)元素取出
"value1"
127.0.0.1:6379> lrange myList 0 1 # 查看对应下标的list列表, 0 为 start,1为 end
1) "value2"
2) "value3"
127.0.0.1:6379> lrange myList 0 -1 # 查看列表中的所有元素,-1表示倒数第一
1) "value2"
2) "value3"

实现栈

127.0.0.1:6379> rpush myList2 value1 value2 value3
(integer) 3
127.0.0.1:6379> rpop myList2 # 将 list的头部(最右边)元素取出
"value3"

查看对应下标list列表、查看list长度

127.0.0.1:6379> rpush myList value1 value2 value3
(integer) 3
127.0.0.1:6379> lrange myList 0 1 # 查看对应下标的list列表, 0 为 start,1为 end
1) "value1"
2) "value2"
127.0.0.1:6379> lrange myList 0 -1 # 查看列表中的所有元素,-1表示倒数第一
1) "value1"
2) "value2"
3) "value3"
127.0.0.1:6379> llen myList
(integer) 3

Set:

介绍

Set是一个单key多value 无序唯一集合 提供了求并、交、差等操作

使用场景
  • 共同关注、共同粉丝、共同圈子
  • 二度好友
  • 扑克牌发牌模拟、随机发牌
基本使用命令
127.0.0.1:6379> sadd mySet value1 value2 # 添加元素进去
(integer) 2
127.0.0.1:6379> sadd mySet value1 # 不允许有重复元素
(integer) 0
127.0.0.1:6379> smembers mySet # 查看 set 中所有的元素
1) "value1"
2) "value2"
127.0.0.1:6379> scard mySet # 查看 set 的长度
(integer) 2
127.0.0.1:6379> sismember mySet value1 # 检查某个元素是否存在set 中,只能接收单个元素
(integer) 1
127.0.0.1:6379> sadd mySet2 value2 value3
(integer) 2
127.0.0.1:6379> sinterstore mySet3 mySet mySet2 # 获取 mySet 和 mySet2 的交集并存放在 mySet3 中
(integer) 1
127.0.0.1:6379> smembers mySet3
1) "value2"

ZSet:

介绍

单key多value 有序集合,这个序来由与value中多了一个score权重系数,集合中的元素能给个按照score进行有序排列

使用场景
  • 排行榜
  • 热搜top10
  • 取topN操作
基本使用命令
127.0.0.1:6379> zadd myZset 3.0 value1 # 添加元素到 sorted set 中 3.0 为权重
(integer) 1
127.0.0.1:6379> zadd myZset 2.0 value2 1.0 value3 # 一次添加多个元素
(integer) 2
127.0.0.1:6379> zcard myZset # 查看 sorted set 中的元素数量
(integer) 3
127.0.0.1:6379> zscore myZset value1 # 查看某个 value 的权重
"3"
127.0.0.1:6379> zrange  myZset 0 -1 # 顺序输出某个范围区间的元素,0 -1 表示输出所有元素
1) "value3"
2) "value2"
3) "value1"
127.0.0.1:6379> zrange  myZset 0 1 # 顺序输出某个范围区间的元素,0 为 start  1 为 stop
1) "value3"
2) "value2"
127.0.0.1:6379> zrevrange  myZset 0 1 # 逆序输出某个范围区间的元素,0 为 start  1 为 stop
1) "value1"
2) "value2"

Hash:

介绍

hash是一个string类型的filed和vlaue的映射表,内部实现是数组+链表,可以仅仅修改这个对象某个字段的值

使用场景
  • 用户信息
  • 商品信息
基本使用命令
127.0.0.1:6379> hset userInfoKey name "guide" description "dev" age "24"
OK
127.0.0.1:6379> hexists userInfoKey name # 查看 key 对应的 value中指定的字段是否存在。
(integer) 1
127.0.0.1:6379> hget userInfoKey name # 获取存储在哈希表中指定字段的值。
"guide"
127.0.0.1:6379> hget userInfoKey age
"24"
127.0.0.1:6379> hgetall userInfoKey # 获取在哈希表中指定 key 的所有字段和值
1) "name"
2) "guide"
3) "description"
4) "dev"
5) "age"
6) "24"
127.0.0.1:6379> hkeys userInfoKey # 获取 key 列表
1) "name"
2) "description"
3) "age"
127.0.0.1:6379> hvals userInfoKey # 获取 value 列表
1) "guide"
2) "dev"
3) "24"
127.0.0.1:6379> hset userInfoKey name "GuideGeGe" # 修改某个字段对应的值
127.0.0.1:6379> hget userInfoKey name
"GuideGeGe"

GEO:

介绍

可以用于将用户给定的地理位置存储起来,并对这些信息进行操作,内部用zset实现

使用场景
  • 附近的人
  • 摇一摇
  • 求两点距离
基本使用命令
127.0.0.1:6379> geoadll china:city 116.40 39.90 beijing
(error) ERR unknown command `geoadll`, with args beginning with: `china:city`, `116.40`, `39.90`, `beijing`, 
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shengzhen
(integer) 2
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou
(integer) 1
127.0.0.1:6379> geoadd china:city 108.96 34.26 xian
(integer) 1
127.0.0.1:6379> geopos china:city beijing
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
127.0.0.1:6379> geodist china:city beijing shanghai
"1067378.7564"
127.0.0.1:6379> geodist china:city beijing shanghai km
"1067.3788"
127.0.0.1:6379> georadius china:citgy 110 30 1000 km
(empty array)
127.0.0.1:6379> georadius china:city 110 30 1000 km
1) "chongqing"
2) "xian"
3) "shengzhen"
4) "hangzhou"
127.0.0.1:6379> georadius china:city 110 30 500 km
1) "chongqing"
2) "xian"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 1
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"


HyperLogLog:

介绍

用于基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的,并且是很小的。在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基
数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。HyperLogLog则是一种算法,它提供了不精确的去重计数方案。

使用场景
  • 假如我要统计网页的UV(浏览用户数量,一天内同一个用户多次访问只能算一次),传统的
    解决方案是使用Set来保存用户id,然后统计Set中的元素数量来获取页面UV。但这种方案只能承载少量用户,一旦用户数量大起来就需要消耗大量的空间来存储用户id。我的目的是统计用户数量而不是保存用户,这简直是个吃力不讨好的方案!而使用Redis的HyperLogLog最多需要12k就可以统计大量的用户数,尽管它大概有0.81%的错误率,但对于统计UV这种不需要很精确的数据是可以忽略不计的。
基本使用命令
redis 127.0.0.1:6379> PFADD runoobkey "redis"

1) (integer) 1

redis 127.0.0.1:6379> PFADD runoobkey "mongodb"

1) (integer) 1

redis 127.0.0.1:6379> PFADD runoobkey "mysql"

1) (integer) 1

redis 127.0.0.1:6379> PFCOUNT runoobkey

(integer) 3

BitMap:

介绍

bitmap是一种位图数据结构,只有0/1,很多场景可以用这一数据结构来省空间

使用场景
  • 已读判断
  • 签到判断
  • 新冠感染人数
基本使用命令
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 1
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 0
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> bitcount sign 
(integer) 4


怎么保证redis和db中的数据一致性(双写一致性)

方案一:延时双删策略

1.在写库前后都进行redis.del(key)操作,并设定合理的超时时间

public void write( String key, Object data )
{
    redis.delKey( key );
    db.updateData( data );
    Thread.sleep( 500 );
    redis.delKey( key );
}

2.具体的步骤:

  1. 先删除缓存
  2. 在写数据库
  3. 休眠500ms
  4. 再次删除缓存

3.设置缓存过期时间
从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。所有的写操作以数据库为准,只要到达缓存过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。

4.该方案的弊端
结合双删策略+缓存超时设置,这样最差的情况就是在超时时间内数据存在不一致,而且又增加了写请求的耗时。
(在极端情况下比如网络波动等问题发生,会导致我们的修改操作的调用出现比较长的耗时操作!!!导致我们的休眠时长不够,而提前删除掉了我们的缓存,但数据还没更新完!!!删除缓存之后,又有线程进行访问导致我们的缓存中出现脏数据)

方案2:将数据串行化

更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到一个 jvm 内部队列中。读取数据的时候,如果发现数据不在缓存中,那么将重新执行“读取数据+更新缓存”的操作,根据唯一标识路由之后,也发送到同一个 jvm 内部队列中。

一个队列对应一个工作线程,每个工作线程串行拿到对应的操作,然后一条一条的执行。这样的话,一个数据变更的操作,先删除缓存,然后再去更新数据库,但是还没完成更新。此时如果一个读请求过来,没有读到缓存,那么可以先将缓存更新的请求发送到队列中,此时会在队列中积压,然后同步等待缓存更新完成。

这里有一个优化点,一个队列中,其实多个更新缓存请求串在一起是没意义的,因此可以做过滤,如果发现队列中已经有一个更新缓存的请求了,那么就不用再放个更新请求操作进去了,直接等待前面的更新操作请求完成即可。

缓存击穿、缓存穿透、缓存雪崩

缓存处理流程

前台发送请求,后台先从缓存中取数据,取到直接返回结果,取不到从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,直接返回空结果。

缓存穿透

描述

缓存击穿是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起id为-1的数据或者id为特别大不存在的数据,这时候的用户很可能是攻击者,攻击会导致数据库压力过大,造成了缓存穿透

解决方案
  • 接口层增加校验,如用户权限校验,id做基础校验,做好拦截器
  • 从缓存取不到的数据,在数据库中也没取到,这时候可以把key-value对写成key-null,缓存有效时间可以设置短一些,如30s,这样可以防止攻击用户反复用同一个id暴力攻击
  • 布隆过滤器
布隆过滤器

布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则

丢弃,从而避免了对底层存储系统的查询压力;

隆过滤器的特点,从容器的角度来说:

如果布隆过滤器判断元素在集合中存在, 不一定存在.
如果布隆过滤器判断不存在, 则一定不存在.
从元素的角度来说:

如果元素实际存在, 布隆过滤器一定判断存在
如果元素实际不存在,布隆过滤器可能判断存在
其他用途

网页爬虫对URL去重,避免爬取相同的 URL 地址;
反垃圾邮件,从数十亿个垃圾邮件列表中判断某邮箱是否垃圾邮箱;
Google Chrome 使用布隆过滤器识别恶意 URL;
Medium 使用布隆过滤器避免推荐给用户已经读过的文章;
Google BigTable,Apache HBbase 和 Apache Cassandra使用布隆过滤器减少对不存在的行和列的查找。

缓存击穿

描述

缓存击穿,是指一个key非常热点,在不停的扛着大并发,对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导使数据库瞬间压力过大。

解决方案
  • 设置热点数据永不过期
从缓存层面来看,没有设置过期时间,所以不会出现热点 key 过期后产生的问题。
  • 加互斥锁
分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布

式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考

验很大。
缓存雪崩
描述

缓存雪崩,是指在某一个时间段,缓存集中过期失效。比如到双十二零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。

解决方案
  • redis高可用(搭建集群)
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。

  • 限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

比如双十一期间对“退款”这一服务降级

  • 数据预热
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

Redis 持久化

两种不同级别的持久化方式

RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。

RDB、AOF两者优缺点

RDB优点

  • 一旦采用该方式、数据库只包含一个文件,有利于做文件备份。
  • 对灾难恢复而言,RDB可以非常轻松将一个单独的文件压缩后再转移到其他存储介质中
  • 性能最大化,它唯一需要做的是fork出子进程,再由子进程完成这些持久化工作,极大避免服务进程执行IO操作
  • 相比AOF,如果数据集很大,RDB的启动效率更高

RDB缺点

  • 系统一旦在定时持久化宕机,此前没来得及写入磁盘的数据都将丢失
  • RDB是通过fork子进程协助数据持久化工作,因此,当数据集较大是,可能会导致服务器停止几百毫秒,甚至一秒

AOF优点

  • 具有更高的安全性
  • 由于日志文件写入是append模式,写入过程出现宕机不会对已写入数据造成影响
  • 日志过大时,redis会采用rewrite机制,即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。
  • AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。

AOF的缺点

  • AOF文件大于RDB文件,在恢复大数据集时RDB速度比AOF快
  • 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效
选择标准

二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb)。

常用配置
RDB持久化配置

Redis会将数据集的快照dump到dump.rdb文件中。此外,我们也可以通过配置文件来修改Redis服务器dump快照的频率,在打开6379.conf文件之后,我们搜索save,可以看到下面的配置信息:

save 900 1              #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。
save 300 10            #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。
save 60 10000        #在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。
AOF持久化配置

在Redis的配置文件中存在三种同步方式,它们分别是:

appendfsync always     #每次有数据修改发生时都会写入AOF文件。
appendfsync everysec  #每秒钟同步一次,该策略为AOF的缺省策略。
appendfsync no          #从不同步。高效但是数据不会被持久化。

Redis主从复制、哨兵模式

为什么需要主从复制

一般来说,要将 Redis用于工程项目中,只使用一台 Redist是万万不能的,原因如下:

1、从结构上,单个 Redist服务器会发生单点故障,井且一台服务器需要处理所有的请求负載,压力较大
2、从容量上,单个 Redis服务器内存容量有限就算一台 Redis服务器内存容量为266,也不能将所有内存用作 Redis?存储内存一般来说,单台 Redist最大使用内存不应该超过206

环境配置

只配置从库,不配置主库!

命令slaveof 127.0.0.1 6310

主机可以写,从机不可以写只能读!主机中的所有数据都会被从机保存

复制原理

Slave启动成功连接到 master 后会发送一个sync命令

Master接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后, master将传送整个数据文件到 slave,井完成一次完全同步

全量复制:而 slave服务在接收到数据库文件数据后,将其存盘井加载到内存中

增量复制:Master继续将新的所有收集到的修改命令依次传给 slave,完成同步

但是只要是重新连接 master,一次完全同步(全量复制)将被自动执行

主机宕机

如果主机宕机了,从机想要变成主机 我们可以通过命令 slaveof no one 让自己变成主机!其他节点就可以手动连接,这个时候主机连接了 还想要 这个主机当老大 那么我们需要手动配置

哨兵模式

介绍

主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式,哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
在这里插入图片描述

哨兵的作用
  • 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
  • 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
    然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。
多哨兵模式在这里插入图片描述

每隔1秒每个哨兵会向主节点、从节点、其他哨兵发送ping命令,做心跳检测

主观下线与客观下线

主观下线:根据定时任务3对没有有效回复的节点做主观下线处理。
客观下线:若主观下线的是主节点,会联系其他哨兵对此主节点进行判断,一定数量(一半以上吧)的哨兵达成一致意见才认为一个master客观上已经宕机掉,各个哨兵之间通过命令SENTINELis_master_down_by_addr来获得其它哨兵对master的检测结果。

leader哨兵选举

1、每个做主观下线的sentinel节点像其他sentinel节点发送命令,要求将自己设置为领导者
2、接收到的sentinel可以同意或者拒绝
3、如果该sentinel节点发现自己的票数已经超过半数并且超过了quorum
4、如果此过程选举出了多个领导者,那么将等待一段时重新进行选举

主节点选举
  • 选择健康状态从节点(排除主观下线、断线),排除5秒钟没有心跳的、排除主节点失联超过10*down-after-millisecends。
  • 选择最高优先级中复制偏移量最大的从机。
  • 如果还没有选出来,则按照ID排序,获取运行ID最小的从节点。

Redis 事务

简介

Redis的 单条命令保证原子性,但是 Redis 事务不能保证原子性。Redis 事务的本质是一组命令的集合。事务中每条命令都会被序列化,执行过程中按顺序执行,不允许其他命令进行干扰。也就是 Redis 事务具有一次性,顺序性,排他性,但 Redis 事务没有隔离级别的概念。

redis事务不会回滚!

代码测试

事务正常执行(watch、multi、exec)

127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money  # watch监视money对象
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec  # 事务正常执行
1) (integer) 80
2) (integer) 20

执行期间更新

客户端1:
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED:
客户端2:
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000
OK
事务执行失败
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec
(nil)
重新监视
127.0.0.1:6379> unwatch
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec
1) (integer) 990
2) (integer) 30

Redis为什么快、为什么单线程还快

首先,采用了多路复用io阻塞机制
然后,数据结构简单,操作节省时间
最后,运行在内存中,自然速度快

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值