Redis介绍及常用命令

数据库介绍

数据库发展至今已有3代:

  1. SQL,传统关系型数据库(RDBMS),例如:MySQL、Oracle
  2. NoSQL,非关系型数据库,例如:Redis、MongoDB
  3. NewSQL,一种新的关系型数据库管理系统,是对各种新的可扩展/高性能数据库的简称,不仅具有NoSQL对海量数据的存储管理能力,还保留了SQL作为查询语言,保证了ACID事务特性,例如:VoltDB、TiDB

传统SQL是以行和列的表格形式来存储的,互联网发展至今,数据量越来越大,传统SQL出现了瓶颈;而NoSQL放弃了传统SQL的强事务保证和关系模型,重点放在数据库的高可用性和可扩展性。NoSQL指Not Only SQL,表示不仅仅是SQL,NoSQL在当今大数据环境下发展十分迅速,而Redis是发展最快的,所以是当下必须要掌握的技术

RDBMS与NoSQL的特点
RDBMS

高度组织化结构化数据
结构化的SQL查询语言
数据和关系都存储在单独的表中
数据操作语言(DML)和数据定义语言(DDL)
严格的一致性
基础事务(ACID):ACID 代表原子性、一致性、隔离性和持久性

NoSQL

没有SQL查询语言
没有预定的模式,不需要事先设计数据库
键-值对存储,列存储,文档存储,图形数据库
最终一致性,而非ACID属性
非结构化和不可预知的数据
高性能,高可用性,高可扩性
CAP定理:一致性,可用性,分隔容错性
BASE定理:基本可用、软状态、最终一致性

大数据时代的3V和3高
3V:主要是描述现存的问题
  1. 海量(Volume):代表数据总量大,如:可能有上亿人同时购买同一商品
  2. 多样(Variety):代表数据类型多,如:个人信息、位置信息、文字信息、图片信息等
  3. 实时(Velocity):数据处理速度快,如:实时直播,未来会越来越快,延迟越来越低
3高:主要是指对程序的要求
  1. 高并发:由于用户量大,必须要支持高并发,如:1秒钟内可能就有上亿人同时购买同一商品
  2. 高可扩:可伸缩性要强,根据用户量和产品变更,可以随时水平拆分,增减服务器
  3. 高性能:保证产品的性能,提高用户体验

在实际工作中,大部分企业通常都是RDBMS和NoSQL一起使用的,NewSQL虽然更强大,但是目前使用的企业较少

Redis介绍

Redis(Remote Dictionary Server) 是一个使用C语言编写的,开源的高性能非关系型(NoSQL)的键值对数据库。与传统数据库不同,Redis的数据是存在内存中的,所以读写速度非常快,因此Redis被广泛应用于缓存方向,每秒可以处理超过10万次读写操作,是已知性能最快的Key-Value DB。Redis也经常用来做分布式锁

Redis支持多种类型的数据结构,如:字符串(string), 散列(hash), 列表(list), 集合(set), 有序集合(zset)与范围查询,bitmaps,hyperloglogs和地理位置(geospatial)索引半径查询。Redis还内置复制、事务 、LUA脚本、LRU驱动事件和不同级别的磁盘持久化,并通过Redis哨兵和自动分区提供高可用性

MongoDB是文档型数据库,使用bson格式(和json格式相似),是一个基于分布式文件存储的数据库,是用C++编写的,主要用来处理大量文档。MongoDB是一个介于关系型数据库与非关系型数据库中间的产品,它是非关系型数据库中功能最丰富的,也是最像关系型数据库的

Redis官方网站

Redis官方中文网站,中文网站信息更新比较慢,若想了解更多最新版本信息请访问英文官方网站

Redis安装
  1. 由于Redis是使用C语言写的,所以安装Redis前必须先安装gcc编译器,安装方法请参考此文章,安装Redis 6以上,gcc版本不能太低,此处安装使用的是gcc8.3版本,是可以正常安装Redis的,若未安装gcc,Redis编译时会报如下错误

    make[3]:cc:命令未找到
    make[3]: *** [Makefile:223:alloc.o] 错误 127
    make[3]: 离开目录“/root/redis-6.2.1/deps/hiredis”
    make[2]: *** [Makefile:51:hiredis] 错误 2
    make[2]: 离开目录“/root/redis-6.2.1/deps”
    make[1]: [Makefile:313:persist-settings] 错误 2 (已忽略)
        CC adlist.o
    /bin/sh: cc: 未找到命令
    make[1]: *** [Makefile:364:adlist.o] 错误 127
    make[1]: 离开目录“/root/redis-6.2.1/src”
    make: *** [Makefile:6:all] 错误 2
    
  2. 下载redis,若下载失败可以通过华为、阿里、清华等国内镜像站下载

    wget https://download.redis.io/releases/redis-6.2.1.tar.gz
    

    请添加图片描述

  3. 解压并进入目录,进行编译、安装操作

    tar -zxf redis-6.2.1.tar.gz
    cd redis-6.2.1
    # 编译Redis
    make
    # 安装Redis
    make install
    

    请添加图片描述

  4. 执行完 make install命令后,redis-6.2.1的src目录下会出现编译后的redis服务程序redis-server和用于测试的客户端程序redis-cli

    cd src
    # 启动Redis
    ./redis-server ../redis.conf
    

    请添加图片描述

    但是redis默认不是后台启动的,要想使用命令添加数据,还需要复制一个选项卡,为了方便可以将其配置为后台运行模式,修改redis的配置文件redis.conf

Redis配置文件解释

介绍几个常用的配置,编辑vim redis.conf文件,若担心修改错误,可以复制文件后进行修改,如:cp redis.conf src/my_redis.conf,然后直接修改my_redis.conf,启动redis时选择修改后的文件./redis-server my_redis.conf

  1. 网络配置

    ################################## NETWORK #####################################
    bind 127.0.0.1 -::1 # 绑定的IP,默认只能本地访问,可以新增IP,或者直接改为*,允许所有可以访问
    bind 192.166.66.22  # 添加IP
    protected-mode yes  # 保护模式,默认开启,不建议关闭
    port 6379  # 访问端口配置
    timeout 0  # 配置客户端连接时的超时时间
    

    配置网络并重启Redis服务后,可视化管理工具才可以成功连接到Redis服务

    请添加图片描述

  2. 通用配置

    ################################# GENERAL #####################################
    daemonize yes  # 以后台方式运行,上文已经提到,要想后台运行就是将no改为yes
    pidfile /var/run/redis_6379.pid # 后台运行时需要指定一个pid文件,若有多个Redis服务需要指定不同的pid文件和端口
    loglevel notice  # 日志等级,有4种:debug、verbose、notice、warning
    logfile ""   # 日志输出位置,为空则输出到默认位置/dev/null
    databases 16 # 数据库的数量,默认有16个数据库
    

    配置后台运行后,再次启动Redis

    请添加图片描述

    默认的16个数据库

    请添加图片描述

  3. 快照模式,RDB配置,Redis是内存数据库,若没有持久化,数据就会断电即失。默认采用rdb的持久化方式,rdb方式采用异步的方式保存数据,只是在进程异常退出时,可能会造成小部分数据丢失,主要看配置保存的时间点,也可使用aof方式,后续介绍

    ################################ SNAPSHOTTING  ################################
    save 3600 1   # 表示3600秒内,若有1条数据有变更,就会保存一次,也就是持久化操作
    save 300 100  # 表示300秒内,若有10条数据有变更,就会进行持久化操作
    save 60 10000 # 表示60秒内,若有10000条数据有变更,就会进行持久化操作,这种通常为高并发情况
    stop-writes-on-bgsave-error yes # 若持久化出错,Redis是否还要继续工作
    rdbcompression yes # 是否压缩rdb文件,会消耗部分CPU资源
    rdbchecksum yes # 是否对rdb文件进行错误检查,有三个选项no、yes、client,client指仅用户连接时检查,但是它影响集群
    dbfilename dump.rdb # 设置把内存数据库写入本地的文件名称,此文件是进行压缩后的二进制文件
    dir ./  # 压缩后的二进制rdb文件保存的目录
    
  4. 安全配置

    ################################## SECURITY ###################################
    requirepass foobared # 配置Redis登录密码,默认无密码
    requirepass 123456   # 配置Redis密码为123456,但是通常都是使用命令设置密码
    
    # *************************************************************************** #
    [root@localhost src]# redis-cli -p 6379  # 使用客户端程序连接本地Redis
    127.0.0.1:6379> ping  # 测试连接是否正常,返回PONG表示正常
    PONG
    127.0.0.1:6379> exit  # 断开连接,使用exit或quit命令都可以
    [root@localhost src]# redis-cli -h 192.166.66.22 -p 6379  # 使用客户端程序连接指定主机的Redis
    192.166.66.22:6379> ping  # 测试连接是否正常
    PONG
    192.166.66.22:6379> CONFIG GET requirepass # 获取Redis密码,可以看到默认返回空密码
    1) "requirepass"
    2) ""
    192.166.66.22:6379> CONFIG SET requirepass "123456"  # 设置Redis密码为123456
    OK
    192.166.66.22:6379> quit # 断开Redis连接后重连,否则密码不生效
    [root@localhost src]# redis-cli -h 192.166.66.22 -p 6379
    192.166.66.22:6379> ping # 测试连接是否正常,结果报错,接下来的操作都会提示未进行身份验证的报错
    (error) NOAUTH Authentication required.
    192.166.66.22:6379> CONFIG GET requirepass
    (error) NOAUTH Authentication required.
    192.166.66.22:6379> auth 123456 # 输入Redis密码
    OK
    192.166.66.22:6379> ping # 再次测试连接是否正常,因为密码验证通过,所以结果返回正常
    PONG
    192.166.66.22:6379> CONFIG GET requirepass # 再次获取Redis密码,返回刚刚设置的密码
    1) "requirepass"
    2) "123456"
    

    请添加图片描述

  5. 内存管理

    ############################## MEMORY MANAGEMENT ################################
    maxmemory <bytes>  # 配置Redis最大内存容量
    MAXMEMORY POLICY:  # 内存达到上限后的缓存淘汰策略
    	1.volatile-lru    # 从即将过期且使用lru算法的key中随机删除
    	2.allkeys-lru     # 从所有key中删除lru算法的key,lru指很久没有使用的,最近最少使用的
    	3.volatile-lfu    # 从即将过期且使用lfu算法的key中随机删除
    	4.allkeys-lfu     # 从所有key中删除lfu算法的key,lfu指最不经常使用的,使用频率最低的
    	5.volatile-random # 从即将过期的key中随机删除
    	6.allkeys-random  # 从所有key中随机删除
    	7.volatile-ttl    # 删除即将过期的key
    	8.noeviction      # 永不过期,直接报错,返回错误
    
  6. 附加模式,AOF配置,aof默认不开启,上文已说到,默认使用rdb方式做持久化,大部分情况下rdb已经够用,aof与rdb模式是可以同时启用,并不冲突,若aof可用,则Redis启动时将自动加载aof,aof文件能够提供更好的持久化保障

    ############################## APPEND ONLY MODE ###############################
    appendonly no # 默认不开启aof
    appendfilename "appendonly.aof" # 保存数据的aof文件名称
    appendfsync everysec # fsync()函数是用来同步内存中所有已修改的文件数据
    1.no # 不会执行fsync()函数,也就不会及时同步数据,操作系统会在适当的时候自己同步数据,此模式速度快
    2.always # 每次数据变更都会执行fsync()函数,及时同步数据,但是消耗性能
    3.everysec # 每秒执行一次fsync()函数,进行同步,是一种折中方案
    no-appendfsync-on-rewrite no # 重新机制,默认不开启
    aof-use-rdb-preamble yes # 开启混合模式
    

每次文件变更都会保存一条数据会造成数据冗余,进而导致文件越来越大,所以aof模式有个rewrite机制,设置一个阈值,当文件大小达到阈值要求时就会触发rewrite机制,会把aof文件中过程化的数据删除,只保留最新的记录,进而使aof文件达到瘦身效果,但是rewrite机制开启后断电等异常情况也可能会造成部分数据丢失
Redis 4.0之后持久化支持混合模式,开启混合模式后,子进程会把内存中的数据以rdb的方式写入aof文件中,把重写缓冲区中的增量命令以aof方式也写入到aof文件,将含有rdb格式和aof格式的数据文件覆盖旧的aof文件,新的aof文件中,一部分数据来自rdb文件,一部分来自Redis运行过程中的增量数据,所以现网环境更推荐混合模式,既能满足性能又能保证用户数据不丢失

Redis常用命令
基础命令
./redis-server ../redis.conf # 启动Redis
redis-cli -h 192.166.66.22 -p 6379 # 启动Redis客户端
192.166.66.22:6379> auth 123456 # 验证Redis密码
OK
192.166.66.22:6379> ping # 测试连接是否正常,若未带有数据则返回PONG,否则会返回后面带的数据
PONG
192.166.66.22:6379> select 2 # 选择要使用的数据库,默认使用0号数据库
OK
192.166.66.22:6379[2]> keys * # 查询当前数据库的所有key
(empty array)
192.166.66.22:6379[2]> set name duanyd # 设置一个key的值
OK
192.166.66.22:6379[2]> get name # 获取一个key的值
"duanyd"
192.166.66.22:6379[2]> EXPIRE name 10 # 设置key的过期时间,单位是秒
(integer) 1
192.166.66.22:6379[2]> ttl name # 查看当前key的剩余时间
(integer) 7
192.166.66.22:6379> EXISTS information #检查key是否存在,存在返回1,不存在返回0
(integer) 1
192.166.66.22:6379> dbsize # 查看key总数
(integer) 0
192.166.66.22:6379> type information  # 查看key的数据类型,若key不存在则返回none
string
192.166.66.22:6379> rename information data # key重命名
OK
192.166.66.22:6379> move name 1 # 移动key到指定数据库,数据库编号为0~15
(integer) 1
192.166.66.22:6379> FLUSHDB # 清空当前数据库
OK
192.166.66.22:6379> FLUSHALL # 清空所有数据库
OK
192.166.66.22:6379> exit # 退出客户端,或者使用quit命令,又或者使用Ctrl+C键
ps -ef | grep redis # 查看Redis进程
kill 1688 # 使用杀掉进程的方式结束Redis服务

也可以使用可视化管理工具Redis Desktop Manager管理Redis数据,使用命令添加数据时最好不要出现中文,由于格式问题会被转译的

请添加图片描述

Redis常用数据类型及其常用命令

更多更详细的命令介绍,请查看官方文档命令介绍:官方文档官方中文文档,根据需要在页面搜索相关命令

1. String(字符串)

String类型是最常用的数据类型,键值对形式(key-value)

192.166.66.22:6379[3]> set sex boy # 设置一个key的值
OK
192.166.66.22:6379[3]> APPEND sex "girl" # 追加字符串,加不加引号都可以的,若key不存在,就相当于set key操作
(integer) 7
192.166.66.22:6379[3]> STRLEN sex # 获取字符串长度
(integer) 7
192.166.66.22:6379[3]> get sex # 获取一个key的值
"boygirl"
192.166.66.22:6379[3]> set value 0 # 设置一个key的值,key为value
OK
192.166.66.22:6379[3]> INCR value # 自增1
(integer) 1
192.166.66.22:6379[3]> INCR value
(integer) 2
192.166.66.22:6379[3]> get value # 获取一个value的值
"2"
192.166.66.22:6379[3]> DECR value # 自减1
(integer) 1
192.166.66.22:6379[3]> DECR value
(integer) 0
192.166.66.22:6379[3]> get value # 获取一个value的值
"0"
192.166.66.22:6379[3]> INCRBY value 8 # 自增,指定步长
(integer) 8
192.166.66.22:6379[3]> INCRBY value 10
(integer) 18
192.166.66.22:6379[3]> DECRBY value 2 # 自减,指定步长
(integer) 16
192.166.66.22:6379[3]> set word "To find a happy excuse,over my spring"
OK
192.166.66.22:6379[3]> GETRANGE word 8 21 # 截取字符串,[8,21]
"a happy excuse"
192.166.66.22:6379[3]> GETRANGE word 28 -1 # 截取字符串,[28,-1]
"my spring"
192.166.66.22:6379[3]> GETRANGE word 0 -1 # 获取全部字符串,相当于get key操作
"To find a happy excuse,over my spring"
192.166.66.22:6379[3]> SETRANGE word 3 "AAAAAA" # 替换指定位置开始的字符串
(integer) 37
192.166.66.22:6379[3]> get word # 获取一个word的值
"To AAAAAA happy excuse,over my spring"
192.166.66.22:6379[3]> mset name dyd age 18 # 同时设置多个key的值
"name"
"age"
192.166.66.22:6379[3]> mget name age # 同时获取多个key的值
1) "dyd"
2) "18"
192.166.66.22:6379[3]> getset db Redis # 先获取再设置,若key不存在,则返回nil,已成功设置db值
(nil)
192.166.66.22:6379[3]> get db #  获取key的值,可以看到上一步操作已设置成功
"Redis"
192.166.66.22:6379[3]> getset db MongoDB # 若key存在,返回原来的值,并替换之前的值
"Redis"
192.166.66.22:6379[3]> get db # 再次获取key的值,可以看到db的值已被替换
"MongoDB"
192.166.66.22:6379[3]> SETEX teacher 10 lili # 设置一个key的值为lili,并设置过期时间10秒
OK
192.166.66.22:6379[3]> ttl teacher # 查看当前key的剩余时间
(integer) 8
192.166.66.22:6379[3]> get teacher # 有效期内获取key的值
"lili"
192.166.66.22:6379[3]> get teacher # 过期后获取key的值,过期后就获取不到啦
(nil)
192.166.66.22:6379[3]> SETNX course English # 若key不存在,则创建key
(integer) 1
192.166.66.22:6379[3]> get course # 获取到key的值
"English"
192.166.66.22:6379[3]> SETNX age 20 # 若key已存在,则创建失败
(integer) 0
192.166.66.22:6379[3]> get age # 由于key已存在,所以获取到之前的值
"18"
192.166.66.22:6379[3]> MSETNX age 18 date April # msetnx是一个原子性的操作,只能同时成功或同时失败
(integer) 0
192.166.66.22:6379[3]> mget age date # 上一步因为age已存在,所以date创建失败,获取不到date的值
1) "18"
2) (nil)
192.166.66.22:6379[3]> MSETNX birthday July date April # 满足原子性,两个key同时创建成功
(integer) 1
192.166.66.22:6379[3]> mget birthday date # 同时获取两个key的值
1) "July"
2) "April"
192.166.66.22:6379[3]> set user:2 {name:Amy,sex:girl,age:20} # 设置一个user:2对象,值为json字符
OK
192.166.66.22:6379[3]> get user:2 # 获取对象user:2的值
"{name:Amy,sex:girl,age:20}"
192.166.66.22:6379[3]> MSET user:3 {name:Darcy,sex:boy,age:21} user:6 {name:Enid,sex:girl} # 设置多个对象
OK
192.166.66.22:6379[3]> mget user:3 user:6 # 获取对象user:3、user:6的值
1) "{name:Darcy,sex:boy,age:21}"
2) "{name:Enid,sex:girl}"
192.166.66.22:6379[3]> MSET user:1:name dyd user:6:age 18 # 设置多个对象
OK
192.166.66.22:6379[3]> mget user:1:name user:6:age # 获取多个对象的值
1) "dyd"
2) "18"
2. Hash(哈希)

hash也可以理解为一个key-value,只是此处的value也可以视为key-value,key-(key-value),本质上hash和string没有太大区别

192.166.66.22:6379[7]> HSET myhash dyd Jun # 设置一个具体的域和值
(integer) 1
192.166.66.22:6379[7]> HGET myhash dyd # 获取key的值
"Jun"
192.166.66.22:6379[7]> HMSET myhash dyd1 Jul dyd2 Aug # 同时设置多个key-value
OK
192.166.66.22:6379[7]> HMGET myhash dyd1 dyd2 # 同时获取多个域的值
1) "Jul"
2) "Aug"
192.166.66.22:6379[7]> HGETALL myhash # 获取myhash全部的域和值
1) "dyd"
2) "Jun"
3) "dyd1"
4) "Jul"
5) "dyd2"
6) "Aug"
192.166.66.22:6379[7]> HMSET myhash1 user:1:name duanyd user:1:age 18 # 设置多个对象
OK
192.166.66.22:6379[7]> HGETALL myhash1 # 获取myhash1全部域和值
1) "user:1:name"
2) "duanyd"
3) "user:1:age"
4) "18"
192.166.66.22:6379[7]> HDEL myhash1 user:1:age # 删除hash指定的key,对应的value值也会消失
(integer) 1
192.166.66.22:6379[7]> HGETALL myhash1 # 获取myhash1全部域和值
1) "user:1:name"
2) "duanyd"
192.166.66.22:6379[7]> HLEN myhash # 获取hash表的字段数量
(integer) 3
192.166.66.22:6379[7]> HKEYS myhash # 只获取hash表中的域
1) "dyd"
2) "dyd1"
3) "dyd2"
192.166.66.22:6379[7]> HVALS myhash # # 只获取hash表中的值
1) "Jun"
2) "Jul"
3) "Aug"
192.166.66.22:6379[7]> HSTRLEN myhash dyd # 获取hash表中指定域的值的字符串长度
(integer) 3
192.166.66.22:6379[7]> HEXISTS myhash dyd3 # 判断域是否存在于myhash表中
(integer) 0
192.166.66.22:6379[7]> HSETNX myhash2 dyd3 Sept # 若hash表不存在,则创建一个新hash表并与key关联
(integer) 1
192.166.66.22:6379[7]> HGETALL myhash2 # 获取myhash2全部的域和值
1) "dyd3"
2) "Sept"
192.166.66.22:6379[7]> HSETNX myhash2 dyd3 Sept1 # hash表中key已存在,所以操作无效,返回0
(integer) 0
192.166.66.22:6379[7]> HGETALL myhash2 # 获取myhash2全部的域和值
1) "dyd3"
2) "Sept"
192.166.66.22:6379[7]> HSETNX myhash2 dyd4 Oct # 若hash表中不存在域和值,则创建域和值
(integer) 1
192.166.66.22:6379[7]> HGETALL myhash2 # 获取myhash2全部的域和值
1) "dyd3"
2) "Sept"
3) "dyd4"
4) "Oct"
3. List(列表)

list可以视为一个双向链表,before/after、list/right都可以插入值,若key不存在则会自动创建新链表,若key存在则直接新增value,若所有value都被删除,则为空链表,也可以说key不再存在

192.166.66.22:6379[6]> LPUSH my_list "world" # 将值插入到列表头部,若key不存在,则创建列表并插入数据
(integer) 1
192.166.66.22:6379[6]> LPUSH my_list "Hello"
(integer) 2
192.166.66.22:6379[6]> LRANGE my_list 0 -1 # 因为lpush命令是向左插入,也就是始终在头部,数据是先进后出
1) "Hello"
2) "world"
192.166.66.22:6379[6]> LPUSH my_list "one" "two" "three" # 向列表中同时插入多个值,数据会按需插入列表头部
(integer) 3
192.166.66.22:6379[6]> LRANGE my_list 0 -1 # 先进后出
1) "three"
2) "two"
3) "one"
192.166.66.22:6379[6]> RPUSH mylist1 "world" # 将值插入到列表尾部,同样,若key不存在,则创建列表并插入数据
(integer) 1
192.166.66.22:6379[6]> RPUSH mylist1 "Hello"
(integer) 2
192.166.66.22:6379[6]> LRANGE mylist1 0 -1 # 因为rpush命令是向右插入,也就是始终在尾部,数据是先进后先
1) "world"
2) "Hello"
192.166.66.22:6379[6]> RPUSH mylist1 "one" "two" "three" # 向列表中同时插入多个值,数据会按需插入列表尾部
(integer) 3
192.166.66.22:6379[6]> LRANGE mylist1 0 -1 # 先进先出
1) "one"
2) "two"
3) "three"
192.166.66.22:6379[6]> LRANGE mylist1 1 3 # 获取指定范围内的元素值
1) "Hello"
2) "one"
3) "two"
192.166.66.22:6379[6]> LPUSHX list "Jan" # lpushx命令,只有key存在的时候才会插入数据,数据插入列表头部
(integer) 0
192.166.66.22:6379[6]> LRANGE list 0 -1 # 因为list不存在,所以值插入失败,返回的是空值
(empty array)
192.166.66.22:6379[6]> LPUSH list "a" # 先创建一个list,并插入数据a
(integer) 1
192.166.66.22:6379[6]> LPUSHX list "Jan" # 再执行lpushx命令,数据+1
(integer) 2
192.166.66.22:6379[6]> LRANGE list 0 -1 # 可以看到数据插入成功
1) "Jan"
2) "a"
192.166.66.22:6379[6]> LPUSHX list "Feb" "Mar" # 同时插入多个值
(integer) 4
192.166.66.22:6379[6]> LRANGE list 0 2
1) "Mar"
2) "Feb"
3) "Jan"
192.166.66.22:6379[6]> RPUSHX list1 "April" "May" # rpushx命令,只有key存在的时候才会插入数据,数据插入列表尾部
(integer) 0
192.166.66.22:6379[6]> LRANGE list1 0 -1 # 同样因为list不存在,所以值插入失败,返回的是空值
(empty array)
192.166.66.22:6379[6]> RPUSH list1 "b" # 先创建一个list1,并插入数据b
(integer) 1
192.166.66.22:6379[6]> RPUSHX list1 "April" "May" # 同时插入多个值,数据+2
(integer) 3
192.166.66.22:6379[6]> LRANGE list1 1 2 # 获取指定范围内的元素值,可以看到数据插入成功
1) "April"
2) "May"
192.166.66.22:6379[6]> LRANGE list1 0 -1 # 获取列表的所有信息
1) "b"
2) "April"
3) "May"
192.166.66.22:6379[6]> RPUSH mylist "Jan" "May" "Fen" "Fen" "Mar" "April" "May"
(integer) 7
192.166.66.22:6379[6]> LRANGE mylist 0 -1 # 获取mylist所有元素值
1) "Jan"
2) "May"
3) "Fen"
4) "Fen"
5) "Mar"
6) "April"
7) "May"
192.166.66.22:6379[6]> LREM mylist 3 "Fen" # 从头到尾删除3个Fen元素,当count>0时,从头到尾检索删除
(integer) 2
192.166.66.22:6379[6]> LRANGE mylist 0 -1 # 获取mylist所有元素值
1) "Jan"
2) "May"
3) "Mar"
4) "April"
5) "May"
192.166.66.22:6379[6]> LREM mylist -1 "May" # 从尾到头删除1个May元素,当count<0时,从尾到头检索删除
(integer) 1
192.166.66.22:6379[6]> LRANGE mylist 0 -1 # 获取mylist所有元素值
1) "Jan"
2) "May"
3) "Mar"
4) "April"
192.166.66.22:6379[6]> LREM mylist 0 "Mar" # 删除列表中所有的Mar元素,当count=0时,检索列表所有数据后删除
(integer) 1
192.166.66.22:6379[6]> LRANGE mylist 0 -1 # 获取mylist所有元素值
1) "Jan"
2) "May"
3) "April"
192.166.66.22:6379[6]> LPOP mylist # 删除并返回第一个元素值
"Jan"
192.166.66.22:6379[6]> RPOP mylist # 删除并返回最后一个元素值
"April"
192.166.66.22:6379[6]> LRANGE mylist 0 -1 # 获取mylist所有元素值
1) "May"
192.166.66.22:6379[6]> LPOP mylist 3 # 从头部开始删除指定数量的元素
1) "Jan"
2) "May"
3) "Fen"
192.166.66.22:6379[6]> RPOP mylist 2 # 从尾部开始删除指定数量的元素
1) "May"
2) "April"
192.166.66.22:6379[6]> BLPOP mylist "April" 3 # 阻塞行为的命令,删除指定元素,增加了请求时间,时间内找不到则返回nil
(nil)
(3.06s)
192.166.66.22:6379[6]> BRPOP mylist "May" 5 # 阻塞行为的命令,时间内找不到则返回nil,有效时间内插入了值,会直接删除
(nil)
(5.01s)
192.166.66.22:6379[6]> BLPOP mylist 1 3 # 阻塞行为的命令,根据下标删除,时间内找不到则返回nil
(nil)
(3.01s)
192.166.66.22:6379[6]> RPUSH mylist1 "a" "Jan" # 从头部插入数据
(integer) 2
192.166.66.22:6379[6]> LRANGE mylist1 0 -1 # 获取mylist1所有元素值
1) "a"
2) "Jan"
192.166.66.22:6379[6]> lset mylist1 0 "month" # 修改指定下标的值,更新操作
OK
192.166.66.22:6379[6]> LRANGE mylist1 0 -1 # 获取mylist1所有元素值
1) "month"
2) "Jan"
192.166.66.22:6379[6]> lset mylist8 2 "test" # 若key不存在则会报错
(error) ERR no such key
192.166.66.22:6379[6]> lset mylist1 8 "month1" # 若index超出范围也会报错
(error) ERR index out of range
192.166.66.22:6379[6]> LPUSH mylist2 "Jan" "May" "Fen" # 从头部依次插入数据
(integer) 3
192.166.66.22:6379[6]> LRANGE mylist2 0 -1 # 获取mylist2所有元素值
1) "Fen"
2) "May"
3) "Jan"
192.166.66.22:6379[6]> LTRIM mylist2 1 2 # 修剪已存在的list的元素范围
OK
192.166.66.22:6379[6]> LRANGE mylist2 0 -1 # 修剪后的mylist2的元素值
1) "May"
2) "Jan"
192.166.66.22:6379[6]> LINDEX mylist2 1 # 通过索引获取下标为1的元素值
"Jan"
192.166.66.22:6379[6]> LLEN mylist2 # 获取mylist2的长度,若key不存在则视为空list,返回0
(integer) 2
192.166.66.22:6379[6]> LPUSH mylist "Jan" # 从头部插入数据 
(integer) 1
192.166.66.22:6379[6]> LINSERT mylist before "Jan" "Fen" # 在mylist列表中Jan前面插入Fen
(integer) 2
192.166.66.22:6379[6]> LRANGE mylist 0 -1 # 获取mylist的元素值
1) "Fen"
2) "Jan"
192.166.66.22:6379[6]> LINSERT mylist after "Jan" "Mar" # 在mylist列表中Jan后面插入Mar
(integer) 3
192.166.66.22:6379[6]> LRANGE mylist 0 -1 # 获取插入值后的元素
1) "Fen"
2) "Jan"
3) "Mar"
192.166.66.22:6379[6]> RPOPLPUSH mylist mylist1 #将mylist最后一个元素移动到mylist1中,并返回移动的元素值
"Mar"
192.166.66.22:6379[6]> LRANGE mylist 0 -1 # 获取mylist的元素值
1) "Fen"
2) "Jan"
192.166.66.22:6379[6]> LRANGE mylist1 0 -1 # 获取mylist1的元素值
1) "Mar"
192.166.66.22:6379[6]> BRPOPLPUSH mylist mylist1 6 # 阻塞行为命令,有效时间内找不到则返回nil
"Jan"
4. Set(集合)

Set是无序不重复集合,不可出现相同的元素

192.166.66.22:6379[8]> sadd myset "Nov" # 向myset集合中添加一个元素
(integer) 1
192.166.66.22:6379[8]> sadd myset "Nov" # 再次向集合中添加相同元素失败,返回0
(integer) 0
192.166.66.22:6379[8]> sadd myset "Dec" "month" # 一次向集合中添加多个元素
(integer) 2
192.166.66.22:6379[8]> SMEMBERS myset # 获取指定集合中所有的元素
1) "month"
2) "Dec"
3) "Nov"
192.166.66.22:6379[8]> SISMEMBER myset "Dec" # 判断某元素是否存在集合中
(integer) 1
192.166.66.22:6379[8]> SPOP myset 1 # 随机移除一个或多个元素,数字表示要删除的元素个数
1) "Dec"
192.166.66.22:6379[8]> SMEMBERS myset # 获取指定集合中所有的元素
1) "month"
2) "Dec"
3) "Nov"
192.166.66.22:6379[8]> srem myset "month" "Nov" # 删除一个或多个元素
(integer) 2
192.166.66.22:6379[8]> SMEMBERS myset # 获取指定集合中所有的元素
1) "Dec"
192.166.66.22:6379[8]> SMOVE myset myset1 "Dec" # 移动myset集合中的Dec到myset1集合中
(integer) 1
192.166.66.22:6379[8]> SMEMBERS myset # 获取指定集合中所有的元素
1) "month"
2) "Nov"
192.166.66.22:6379[8]> SMEMBERS myset1 # 获取指定集合中所有的元素
1) "Dec"
192.166.66.22:6379[8]> FLUSHDB # 清空当前数据库
OK
192.166.66.22:6379[8]> sadd myset "month" "Nov" "Dec" # 向集合中添加多个元素
(integer) 3
192.166.66.22:6379[8]> SRANDMEMBER myset # 从集合中随机获取一个元素,不加数值默认随机获取一个
1) "Nov"
192.166.66.22:6379[8]> SRANDMEMBER myset # 从集合中随机获取一个元素
1) "Dec"
192.166.66.22:6379[8]> SRANDMEMBER myset 2 # 从集合中随机获取两个元素
1) "month"
2) "Dec"
192.166.66.22:6379[8]> sadd myset "one" "two" "three" "four" "five" # 向集合中添加多个元素
(integer) 5
192.166.66.22:6379[8]> sadd myset1 "four" "five" "six" "seven" "eight" # 向集合中添加多个元素
(integer) 5
192.166.66.22:6379[8]> sdiff myset myset1 # 差集,指多个集合中一方独有的元素
1) "one"
2) "two"
3) "three"
192.166.66.22:6379[8]> SINTER myset myset1 # 交集,指多个集合中都有的元素,【共同关注】功能会使用此命令
1) "four"
2) "five"
192.166.66.22:6379[8]> SUNION myset myset1 # 并集,指多个集合中的元素合并在一起,去掉重复元素后的结果
1) "two"
2) "one"
3) "six"
4) "five"
5) "eight"
6) "seven"
7) "four"
8) "three"
192.166.66.22:6379[8]> SINTERSTORE myset2 myset # 与sinter类似,获取两个集合的交集,将结果保存在结果集中
(integer) 5
192.166.66.22:6379[8]> SMEMBERS myset2 # 获取指定集合中所有的元素
1) "one"
2) "two"
3) "three"
4) "four"
5) "five"
192.166.66.22:6379[8]> SINTERSTORE myset1 myset # 若目的集合已存在,则直接重写集合中的元素
(integer) 5
192.166.66.22:6379[8]> SMEMBERS myset1 # 获取指定集合中所有的元素
1) "one"
2) "two"
3) "three"
4) "four"
5) "five"
192.166.66.22:6379[8]> SDIFFSTORE My_set myset myset1 # 与sdiff类似,但不直接返回结果,只将结果保存到目的集合中
(integer) 3
192.166.66.22:6379[8]> SMEMBERS My_set # 获取指定集合中所有的元素
1) "one"
2) "two"
3) "three"
192.166.66.22:6379[8]> SINTERSTORE My_set myset myset1 # 与sinter类似,若目的集合已存在,则直接重写集合中的元素
(integer) 2
192.166.66.22:6379[8]> SMEMBERS My_set # 获取指定集合中所有的元素
1) "five"
2) "four"
192.166.66.22:6379[8]> SUNIONSTORE My_Set1 myset myset1 # 与sunion类似,获取两个集合的并集,将结果保存在结果集中
(integer) 8
192.166.66.22:6379[8]> SMEMBERS My_Set1 # 获取指定集合中所有的元素
1) "two"
2) "one"
3) "six"
4) "five"
5) "eight"
6) "seven"
7) "four"
8) "three"
5. Zset(有序集合)

Zset经常用在排名上,如各大网站中的榜单排名,成绩排名,语法上只是在Set的基础上增加一个值,命令改为Z开头

192.166.66.22:6379[9]> ZADD myzset 1 "Rat" # 向集合中添加一个元素
(integer) 1
192.166.66.22:6379[9]> ZADD myzset 3 "Rabbit" 2 "Tiger" # 向集合中添加多个元素
(integer) 2
192.166.66.22:6379[9]> ZRANGE myzset 0 -1 # 根据指定index,返回有序集合的元素列表
1) "Rat"
2) "Tiger"
3) "Rabbit"
192.166.66.22:6379[9]> ZCARD myzset # 获取集合中的元素数量
(integer) 3
192.166.66.22:6379[9]> ZCOUNT myzset 2 10 # 获取数值范围内的元素数量
(integer) 2
192.166.66.22:6379[9]> ZRANK myzset "Tiger" # 获取指定元素的索引值index
(integer) 1
192.166.66.22:6379[9]> ZSCORE myzset "Rabbit" # 获取指定元素的score值
"3"
192.166.66.22:6379[9]> ZINCRBY myzset 5 "Rabbit" # 若元素存在,则增加元素的score值,3+5=8
"8"
192.166.66.22:6379[9]> ZINCRBY myzset 6 "Snake" # 若元素不存在,则直接新增成员
"6"
192.166.66.22:6379[9]> ZRANGE myzset 0 -1 # 获取有序集合中的所有元素
1) "Rat"
2) "Tiger"
3) "Rabbit"
4) "Snake"
192.166.66.22:6379[9]> zrem myzset "Snake" # 删除一个或多个元素
(integer) 1
192.166.66.22:6379[9]> ZRANGEBYSCORE myzset -inf +inf # 将所有元素由小到大排序
1) "Rat"
2) "Tiger"
3) "Rabbit"
192.166.66.22:6379[9]> ZRANGEBYSCORE myzset 1 2 # 根据指定范围内元素的score值大小,由小到大排序
1) "Rat"
2) "Tiger"
192.166.66.22:6379[9]> ZREVRANGEBYSCORE myzset +inf -inf # 将所有元素由大到小排序
1) "Rabbit"
2) "Tiger"
3) "Rat"
192.166.66.22:6379[9]> ZREVRANGEBYSCORE myzset 9 2 # 根据指定范围内元素的score值大小,由大到小排序
1) "Rabbit"
2) "Tiger"
Redis特殊数据类型及常用命令
1. Geospatial(地理位置)

生活中用到的附近的人、附近打车、指定半径内搜索店铺等功能都可以使用redis的geo命令实现,可以访问此网站获取经纬度,或者使用其它网站,实际工作中都是使用程序一次性导入所有数据的

有效经度从-180度到180度,有效纬度从-85.05112878度到85.05112878度,若超出范围,命令会报错

192.166.66.22:6379[10]> GEOADD city 116.512885 39.847469 "beijing" # 添加一条或多条数据
(integer) 1
192.166.66.22:6379[10]> GEOADD city 120.592412 31.303564 "suzhou" 113.631419 34.753439 "zhengzhou"
(integer) 2
192.166.66.22:6379[10]> GEOHASH city "beijing" # 将经纬度转换为字符串
1) "wx4feq406f0"
192.166.66.22:6379[10]> geopos city "beijing" # 获取一个城市的经纬度
1) 1) "116.51288241147994995"
   2) "39.84746799564361908"
192.166.66.22:6379[10]> geopos city "beijing" "zhengzhou" # 获取多个城市的经纬度
1) 1) "116.51288241147994995"
   2) "39.84746799564361908"
2) 1) "113.63141745328903198"
   2) "34.75343888172013607"
192.166.66.22:6379[10]> GEODIST city "beijing" "suzhou" km # 获取两地之间的直线距离,默认单位是m,mi英里、ft英尺
"1019.1303"
192.166.66.22:6379[10]> GEORADIUS city 120 31 1000 km # 以120,31为中心,获取半径为1000km以内的城市
1) "suzhou"
2) "zhengzhou"
192.166.66.22:6379[10]> GEORADIUS city 120 31 1000 km withdist # 显示获取到的城市到中心位置的距离
1) 1) "suzhou"
   2) "65.7255"
2) 1) "zhengzhou"
   2) "726.5045"
192.166.66.22:6379[10]> GEORADIUS city 120 31 1000 km withcoord # 显示获取到的城市经纬度
1) 1) "suzhou"
   2) 1) "120.59240967035293579"
      2) "31.30356425196533365"
2) 1) "zhengzhou"
   2) 1) "113.63141745328903198"
      2) "34.75343888172013607"
192.166.66.22:6379[10]> GEORADIUS city 120 31 1000 km withdist withcoord # 显示到中心距离及经纬度
1) 1) "suzhou"
   2) "65.7255"
   3) 1) "120.59240967035293579"
      2) "31.30356425196533365"
2) 1) "zhengzhou"
   2) "726.5045"
   3) 1) "113.63141745328903198"
      2) "34.75343888172013607"
      192.166.66.22:6379[10]> GEORADIUS city 120 31 1000 km withdist withcoord desc # 数据降序排列
1) 1) "zhengzhou"
   2) "726.5045"
   3) 1) "113.63141745328903198"
      2) "34.75343888172013607"
2) 1) "suzhou"
   2) "65.7255"
   3) 1) "120.59240967035293579"
      2) "31.30356425196533365"
192.166.66.22:6379[10]> GEORADIUS city 120 31 1000 km withdist withcoord count 1 # 只显示一条数据
1) 1) "suzhou"
   2) "65.7255"
   3) 1) "120.59240967035293579"
      2) "31.30356425196533365"
192.166.66.22:6379[10]> GEORADIUSBYMEMBER city "suzhou" 1000 km # 以指定城市为中心,获取半径1000km以内的城市
1) "suzhou"
2) "zhengzhou"
192.166.66.22:6379[10]> GEORADIUSBYMEMBER city "suzhou" 1500 km withdist # 显示获取到的城市到指定城市的距离
1) 1) "suzhou"
   2) "0.0000"
2) 1) "zhengzhou"
   2) "753.7649"
3) 1) "beijing"
   2) "1019.1303"
2. HyperLogLog

hyperloglog是一个在大数据量的情况下做基数统计的算法,优点是占用空间非常小,缺点是存在较小的误差,基数是指集合中去除重复信息后的总数,多个集合可以理解为并集后的数量。像访问量之类,若项目能接受容错可以使用此算法,否则就选择set或其它方式

192.166.66.22:6379[11]> PFADD mykey "redis" "MongoDB" # 将redis和MongoDb放到以mykey为变量名的HyperLogLog结构中
(integer) 1
192.166.66.22:6379[11]> PFADD mykey1  "MongoDB" "MySQL" "Oracle" # HyperLogLog结构中数据成功修改返回1,否则返回0
(integer) 1
192.166.66.22:6379[11]> PFCOUNT mykey # 获取mykey的基数,若不存在则返回0
(integer) 2
192.166.66.22:6379[11]> PFCOUNT mykey1 # 获取mykey1的基数,若不存在则返回0
(integer) 3
192.166.66.22:6379[11]> PFMERGE mykey2 mykey mykey1 # 将两个hyperloglog合并为一个
OK
192.166.66.22:6379[11]> PFCOUNT mykey2 # 查看合并后的基数
(integer) 4
3. Bitmaps

bitmaps也是一种做基数统计的算法,也是能够节省空间,统计用户是否活跃、是否登录、是否打卡等,只要是两个状态,就可以使用bitmaps统计算法,bitmaps都是操作二进制进行位记录的

192.166.66.22:6379[12]> SETBIT mybit 1 0 # 设置key的value在offset处的bit值,语法:setbit key offset value
(integer) 0
192.166.66.22:6379[12]> SETBIT mybit 2 1 # 设置key的value在offset处的bit值,
(integer) 0
192.166.66.22:6379[12]> SETBIT mybit 3 1 # 设置key的value在offset处的bit值
(integer) 0
192.166.66.22:6379[12]> SETBIT mybit 4 0 # 设置key的value在offset处的bit值
(integer) 0
192.166.66.22:6379[12]> SETBIT mybit 5 1 # 设置key的value在offset处的bit值
(integer) 0
192.166.66.22:6379[12]> GETBIT mybit 1 # 获取key对应的value在offset处的bit值
(integer) 0
192.166.66.22:6379[12]> GETBIT mybit 5 # 获取key对应的value在offset处的bit值
(integer) 1
192.166.66.22:6379[12]> BITCOUNT mybit # 统计value被设置为1的bit总数
(integer) 3
192.166.66.22:6379[12]> SETBIT mybit 5 0 # 当key对应的value变小时,bit值设置为1,value变大时,bit值设置为0
(integer) 1
Redis事务

redis事务本质上是一组命令的集合,一个事务中的所有命令都会被序列化,事务执行过程中,命令会按序执行。事务中的命令只有触发执行命令exec后才按序被执行,并不是直接执行的。Redis单条命令具有原子性,但在事务中不保证原子性,而且没有隔离性。事务具有的特点是:一次性、顺序性和排他性

事务顺序:①开启事务(multi)、②命令入队(…要执行的命令…)、③执行事务(exec)

正常的事务

192.166.66.22:6379[15]> MULTI   #开启事务
OK
192.166.66.22:6379[15](TX)> set transaction "Hello" # 设置key的值
QUEUED
192.166.66.22:6379[15](TX)> APPEND transaction " World!" # 追加字符串
QUEUED
192.166.66.22:6379[15](TX)> get transaction # 获取key的值
QUEUED
192.166.66.22:6379[15](TX)> GEOADD china:city 115.035584 35.717889 puyang # 新增一条城市坐标
QUEUED
192.166.66.22:6379[15](TX)> GEOPOS china:city puyang # 获取城市坐标
QUEUED
192.166.66.22:6379[15](TX)> exec # 执行事务,可以看到未执行此命令前上面命令都处于排队等候执行状态
1) OK
2) (integer) 12
3) "Hello World!"
4) (integer) 1
5) 1) 1) "115.03558605909347534"
      2) "35.71789014396396311"

放弃执行事务

192.166.66.22:6379[15]> multi # 开启事务
OK
192.166.66.22:6379[15](TX)> hset hash dyd "Jul" # 设置域和值
QUEUED
192.166.66.22:6379[15](TX)> hget hash dyd # 获取值
QUEUED
192.166.66.22:6379[15](TX)> DISCARD # 取消事务
OK
192.166.66.22:6379[15]> hget hash dyd # 结果为空,事务队列中的命令并没有被执行
(nil)

开启事务后命令编译异常

192.166.66.22:6379[15]> multi # 开启事务
OK
192.166.66.22:6379[15](TX)> lpush list "Mon" "Tue" "Wed" # 从头部向列表中插入数据
QUEUED
192.166.66.22:6379[15](TX)> Rpush list "Thur" # 从尾部向列表中插入数据
QUEUED
192.166.66.22:6379[15](TX)> llrange list # 错误的命令
(error) ERR unknown command `llrange`, with args beginning with: `list`,
192.166.66.22:6379[15](TX)> lrange list 0 -1 # 获取列表中的数据
QUEUED
192.166.66.22:6379[15](TX)> exec # 执行事务报错
(error) EXECABORT Transaction discarded because of previous errors.
192.166.66.22:6379[15]> lrange list 0 -1 # 获取列表数据为空,所以事务队列中的命令并没有被执行
(empty array)

执行事务时异常

192.166.66.22:6379[15](TX)> set key "Hello" # 设置一个key的值
QUEUED
192.166.66.22:6379[15](TX)> INCR key # 让key自增1,命令存在语法性错误,执行事务是报错
QUEUED
192.166.66.22:6379[15](TX)> get key # 获取key的值
QUEUED
192.166.66.22:6379[15](TX)> EXEC # 执行事务
1) OK
2) (error) ERR value is not an integer or out of range # 第二条命令报错,因为值为字符串,不能自增1
3) "Hello"
192.166.66.22:6379[15]> get key # 虽有报错命令,但是其它正常命令已被执行
"Hello"

使用Watch命令实现乐观锁

悲观锁指的是对数据被外界修改持悲观态度,认为任何操作都可能会出现问题,进而采取任何操作都要加锁;

乐观锁是相对悲观锁而言的,认为操作不会出现问题,只是在数据更新的时候判断一下数据是否被修改。

Watch命令可以为Redis事务提供check-and-set(CAS)行为,被Watch的键会被监视,并会发觉这些键是否被改动过了。如果有至少一个被监视的键在EXEC执行之前被修改了,那么整个事务都会被取消,EXEC返回nil来表示事务已经失败

192.166.66.22:6379[15]> set price 10 # 假设一个商品价格10元
OK
192.166.66.22:6379[15]> watch price # 监视价格
OK
192.166.66.22:6379[15]> MULTI # 开启事务
OK
192.166.66.22:6379[15](TX)> DECRBY price 5 # 价格减少5元
QUEUED
192.166.66.22:6379[15](TX)> incrby price 15 # 价格增加15元
QUEUED
192.166.66.22:6379[15](TX)> EXEC # 执行事务,事务开启到执行结束价格未发生变化,所以正常执行成功
1) (integer) 5  # 事务中第一条命令减少5元,现价格为5元
2) (integer) 20 # 事务中第二条命令增加15元,现价格为20元

测试多线程修改价格,这时候watch会监视到价格发生变化,事务执行失败返回nil,这就是实现了乐观锁

192.166.66.22:6379[15]> watch price # 监视价格
OK
192.166.66.22:6379[15]> MULTI # 开启事务
OK
192.166.66.22:6379[15](TX)> incrby price 5 # 接上文,现价格已是20元,在此基础上,价格再增加5元
QUEUED
	# 开启一个新的客户端或者复制选项卡,也连接到redis,将已经被监视的price改为25元
	[root@localhost ~]# redis-cli -h 192.166.66.22 -p 6379
	192.166.66.22:6379> select 15
	OK
	192.166.66.22:6379[15]> set price 25
	OK
	# 然后回到之前的线程,执行exec命令
192.166.66.22:6379[15](TX)> exec # 执行前另一个线程修改了price,导致事务执行失败,返回了nil
(nil)

取消监视,重新获取最新的price才能执行成功

192.166.66.22:6379[15]> UNWATCH # 取消监视
OK
192.166.66.22:6379[15]> watch price # 监视价格
OK
192.166.66.22:6379[15]> multi # 开启事务
OK
192.166.66.22:6379[15](TX)> incrby price 5 # 接上文,现价格已被另一个线程改为25元,在此基础上,价格增加5元
QUEUED
192.166.66.22:6379[15](TX)> exec # 执行事务
1) (integer) 30 # 事务执行成功,25+5=30
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白典

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值