Redis
链接: 我的博客.欢迎大佬前来指教
一、Nosql概述
1、初识Nosql
为什么会使用Nosql呢?我们现在所处于大数据时代,用户的个人信息,社交网络,地理位置,用户自己产生的数据等,数据量十分的庞大,而一般的数据库无法进行分析处理。当访问量过大后,一个服务器承受不了,当出现这种情况后,于是Nosql便应运而生了,Nosql 的全程是Not only sql 不仅仅是sql,因此,他能做的sql 更多。
2、Nosql的特点
- 方便扩展
- 大数据量的高性能
- 数据类型是多样性的
- 传统RDBMS和Nosql
传统的RDBMS
- 结构化的组织
- SQL
- 数据和关系都存在单独的表中
- 数据定义语言
- 严格的一致性
- 基础的事务
Nosql
- 不仅仅是数据
- 没有固定的查询语言
- 键值对存储,列存储、文档存储、图形数据库
- 最终一致性
- CAP定理和BASE(异地多活)
- 高性能、高可用、高可扩
3、3V+3高
大数据时代的3V:主要是描述问题的
1、海量的Volume
2、多样的Variety
3、实时的Velocity
大数据时代的3高:主要是对程序的要求
1、高并发
2、高可拓
3、高性能
4、Nosql的四大分类
-
KV键值对 例如:redis
-
文档型数据库 例如:MongoDB
-
列存储数据库 例如:Hbase
-
图关系型数据库 例如 :Neo4j
二、初识Redis
1、概述
Redis全称remote dictionary server 远程字典服务,是一个使用C语言编写、支持网络、可基于内存亦可持久化的日志型key-Value数据库,并提供了多种语言的API。Redis 是一个开源的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
这里附上一下官网链接
链接: redis官网 | GitHub上的开源地址
不建议windows ,windows端的github上面已经停更很久了,redis推荐linux
2、安装
Windows端的
下载安装包,解压即可,点server那个exe启动
点击cli 测试,输入ping 输出pong 即是连接成功
Linux端
下载安装包
解压完成
安装运行所需的c环境
安装完成
执行make命令
make install查看是否完成安装
默认路径/usr/local/bin
将配置文件从安装目录拷贝到bin下的Nconfig
修改配置文件
把这里改为yes即可开机启动
这里通过指定的配置文件启动服务,我们刚刚拷贝的那个
测试连接是否成功
关机指令
三、基本指令
1、切换数据库
redis 默认有16个数据库
可以使用select进行切换数据库
127.0.0.1:6379> select 3 # 切换数据库
OK
127.0.0.1:6379[3]> dbsize #查看数据库所占的大小
(integer) 0
127.0.0.1:6379[3]>
2、set和get
因为是key-vakue类型的数据库。所以需要使用set和get来进行操作
127.0.0.1:6379[3]>set name chenning #set key value
OK
127.0.0.1:6379[3]> get name
"chenning"
3、查看所有键值
127.0.0.1:6379[3]>keys *
1) "name"
4、清除
127.0.0.1:6379[3]>flushdb # 清除当前数据库的数据
OK
127.0.0.1:6379[3]> keys *
(empty array)
127.0.0.1:6379> flushall #清空所有数据库的数据
OK
补充说明:
redis是单线程的 redis是基于内存操作的,cpu不是redis的性能瓶颈 redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程 于是就用了单线程
为什么单线程还这么快?
-
误区一 高性能的服务器一定 是多线程的
-
误区二 多线程的一定比单线程的效率高
redis是将所有的数据放在内存中的所有说使用单线程去擦欧总就是最高的 多线程cpu会上下文切换 对于内存系统来说,没有上下文的切换效率就是最高的,在内存情况下,单线程就是效率最高的方案
四、Redis基础数据类型及操作
1、key 相关指令
1.1 查看是否拥有key
127.0.0.1:6379> exists name #查看是否拥有 name这个键
(integer) 1 #拥有则返回1
127.0.0.1:6379> exists name1
(integer) 0 #不拥有则返回0
1.2、移动key
127.0.0.1:6379> move name 1 #move key db 将key移动到对应的数据库
(integer) 1
127.0.0.1:6379> keys *
(empty array)
1.3、查看key值是否存在
127.0.0.1:6379> exists name #查看name这个key在这里存在吗
(integer) 1 #存在
127.0.0.1:6379> exists name1
(integer) 0 #不存在
1.4、设置存活时间
127.0.0.1:6379> set name chenning
OK
127.0.0.1:6379> expire name 10 # 设置存活时间为10秒
(integer) 1
127.0.0.1:6379> ttl name # 查看剩余的时间
(integer) 5 #还剩5秒
127.0.0.1:6379> ttl name
(integer) -2 #已经过期
127.0.0.1:6379> get name
(nil) #值已经无法获取
1.5、查看value的类型
127.0.0.1:6379> set name chenning
OK
127.0.0.1:6379> type name #查看value的类型
string
2、String
2.1、追加
127.0.0.1:6379> get name
"chenning"
127.0.0.1:6379> append name "niubi" #追加 如果没有的话就会新建
(integer) 13 #这里返回的是字符串的长度
127.0.0.1:6379> get name
"chenningniubi"
2.2、查询长度
127.0.0.1:6379> strlen name
(integer) 13
2.3、自增和自减
127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views # 增加 1
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> decr views # 减少 1
(integer) 1
127.0.0.1:6379> decr views
(integer) 0
增加和减少的时候是可以设置步长的
127.0.0.1:6379> incrby views 10 # 设置步长为10 一次增加10
(integer) 12
127.0.0.1:6379> decrby views 4 # 设置步长为4 一次减少4
(integer) 8
2.4、截取字符串
127.0.0.1:6379> getrange name 0 3 #截取 0 到 3 位置的字符串
"chen"
127.0.0.1:6379> getrange name 0 -1 #获取全部的额字符串
"chenningniubi"
2.5、替代字符串
127.0.0.1:6379> SETRANGE name 1 xx #将1后的部分字符串替换为xx
(integer) 13
127.0.0.1:6379> get name
"cxxnningniubi" # 结果
2.6、设置有存活时间的字符串
127.0.0.1:6379> SETEX yy2 30 "hello" #setex key time string
OK
127.0.0.1:6379> ttl yy2
(integer) 23
127.0.0.1:6379> ttl yy2
(integer) 17
127.0.0.1:6379> ttl yy2
(integer) 1
127.0.0.1:6379> ttl yy2
(integer) -2 #已过期
2.7、设置不存在的key
127.0.0.1:6379> SETNX mykey "db"
(integer) 1
127.0.0.1:6379> SETNX mykey "mdu" #如果key已经存在,则不会做出修改,返回0失败
(integer) 0
127.0.0.1:6379>get mykey
"db" #依旧还是原来的值
2.8、批量操作key和value
127.0.0.1:6379> mset k1 v1 k2 v3 k3 v3 #使用 mset 操作,一个key后面跟随一个value值
OK
127.0.0.1:6379> mget k1 k2 k3 #批量获取key的值
1) "v1"
2) "v3"
3) "v3
127.0.0.1:6379> MSETNX k1 v1 k4 v4 #这里的 msetnx 和上面的是一样的操作,需要注意的就是,
#这里是原子性操作,要么一起成功 要么一起失败,例如这里因为设置过k1所以失败了
(integer) 0
2.9、设置对象
set user:1 {name:zhangsan,age:3} #设置一个json字符串来保存对象
或者有更巧妙的设计
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 2
ok
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "2"
2.10、getset
127.0.0.1:6379> getset db redis # 如果不存在,则返回 nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongodb #如果存在值,获取原来的值,并设置新的值
"redis"
127.0.0.1:6379> get db
"mongodb "
3、List
所有的List命令都是L开头的
3.1、push
像队列一样增加
127.0.0.1:6379> LPUSH list one #在左边增加one
(integer) 1
127.0.0.1:6379> LPUSH list two
(integer) 2
127.0.0.1:6379> LPUSH list three
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1 #查看list全部的信息
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> LRANGE list 0 1 #查看list位置 0 到 1 的信息
1) "three"
2) "two"
从右边push
127.0.0.1:6379> RPUSH list four
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "four" #可以看到从右边push后这个four是在最后边的
3.2、 pop
127.0.0.1:6379> RPOP list #从右边pop一个数据出来
"four"
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
3.3、根据下标获取值
127.0.0.1:6379> LINDEX list 1
"two"
127.0.0.1:6379> LINDEX list 0
"three"
3.4、获取长度
127.0.0.1:6379> LLEN list
(integer) 3
3.5、删除指定的元素
127.0.0.1:6379> lrem list 1 three #从list里面删除一个three,
(integer) 1
127.0.0.1:6379> LRANGE list 0 -1 #可以看到list里面仍然还有一个three
1) "three"
2) "two"
3) "one"
3.6、修建枝叶
127.0.0.1:6379> ltrim list 1 2 #只保留list 里面 1 到 2 的元素 list是从0开始的
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one" #已经是修改过的了
3.7、组合命令
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> RPOPLPUSH list otherlist # 使右边的元素弹出并push到otherlist
"one"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
127.0.0.1:6379> LRANGE otherlist 0 -1
1) "one"
3.8、Lset更新操作
127.0.0.1:6379> LSET list 1 item #将list中的 1 号元素改为item
(error) ERR index out of range #不存在则会报错
127.0.0.1:6379> lset list 0 item
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "item" #已经成功的变成了item
#相当于是一个更新操作,如果不存在则会报错
3.9、insert插入操作
127.0.0.1:6379> RPUSH mylist hello
(integer) 1
127.0.0.1:6379> RPUSH mylist world
(integer) 2
127.0.0.1:6379> LINSERT mylist before world niubi #往world前面插入niubi
(integer) 3
127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello"
2) "niubi"
3) "world"
127.0.0.1:6379> LINSERT mylist after world gan #往world后面插入gan
(integer) 4
127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello"
2) "niubi"
3) "world"
4) "gan"
4、Set
set命令都是S开头,存储的元素不可以重复
4.1、添加元素和查看元素
127.0.0.1:6379> sadd myset "hello" # 往set里面添加元素
(integer) 1
127.0.0.1:6379> sadd myset "world"
(integer) 1
127.0.0.1:6379> SMEMBERS myset #查看所有的元素
1) "world"
2) "hello"
127.0.0.1:6379> SISMEMBER myset hello #查看该元素是否在set中
(integer) 1 #含有该元素
4.2、查询set的长度
127.0.0.1:6379> SCARD myset
(integer) 2 #含有两个元素
4.3、移除元素
127.0.0.1:6379> SREM myset hello #remove指定元素
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "world"
4.4、抽随机
127.0.0.1:6379> SRANDMEMBER myset # 从set里面随机抽取一个元素出来
"world"
127.0.0.1:6379> SRANDMEMBER myset
"william"
127.0.0.1:6379> SRANDMEMBER myset
"world"
4.5、随机移除(弹出)一个元素
127.0.0.1:6379> SPOP myset #经典的pop
"world"
127.0.0.1:6379> SPOP myset
"hello"
4.6、将一个元素转移到另外一个set
127.0.0.1:6379> SMOVE myset myset2 hello # 组合命令 将hello元素从myset转移到myset2
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "world"
2) "william"
127.0.0.1:6379> SMEMBERS myset2
1) "hello"
4.7、差,交,并集
127.0.0.1:6379> sadd myset a
(integer) 1
127.0.0.1:6379> sadd myset b
(integer) 1
127.0.0.1:6379> sadd myset1 b
(integer) 1
127.0.0.1:6379> sadd myset1 c
(integer) 1
127.0.0.1:6379> SDIFF myset myset1 #查询 myset之于myset1的差集
1) "a"
127.0.0.1:6379> SINTER myset myset1 #查询 myset和myset1的交集
1) "b"
127.0.0.1:6379> SUNION myset myset1 #查询 myset和myset1的并集
1) "c"
2) "a"
3) "b"
127.0.0.1:6379> SDIFF myset1 myset #查询 myset1之于myset的差集
1) "c"
5、hash
map集合 key-map
5.1、增加和获取元素
127.0.0.1:6379> hset myhash field1 chenning #在myhash中增加 field1 -- chenning 这一对键值对
(integer) 1
127.0.0.1:6379> hget myhash field1 # 在myhash中查询fields所对应的值
"chenning"
127.0.0.1:6379> hmset myhash field1 hello field2 world #批量设置myhash 中的key--value
OK
127.0.0.1:6379> hmget myhash field1 field2 #批量获取 myhash 中的key所对应的value
1) "hello"
2) "world"
127.0.0.1:6379> hgetall myhash #获取所有的key和value
1) "field1"
2) "hello"
3) "field2"
4) "world"
5.2、删除元素
127.0.0.1:6379> hdel myhash field1 #删除 myhash 中该 key 所对应的 value
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"
5.3、查询hash的长度
127.0.0.1:6379> hlen myhash
(integer) 1
5.4、查询是否存在对应的key
127.0.0.1:6379> HEXISTS myhash field1 #经典的 exists 命令
(integer) 0
127.0.0.1:6379> HEXISTS myhash field2
(integer) 1
5.5、查询所有的key和value
127.0.0.1:6379> HKEYS myhash # 查询所有的key值
1) "field2"
2) "field1"
127.0.0.1:6379> hvals myhash # 查询所有的value值
1) "world"
2) "chenning"
5.6、自增和自减
127.0.0.1:6379> hset myhash field3 5
(integer) 1
127.0.0.1:6379> HINCRBY myhash field3 1 #incrby 命令
(integer) 6
127.0.0.1:6379> HINCRBY myhash field3 -1 #增加负值
(integer) 5
5.7、setnx
127.0.0.1:6379> HSETNX myhash field4 hello #和之前一样,不存在才可以设置,若存在则不可以使用该命令更改
(integer) 1
127.0.0.1:6379> HSETNX myhash field4 world
(integer) 0
hash适合变更的数据user、name、age 尤其是用户信息之类的经常变动的信息,hash更适合对象的存储,String更适合字符串的存储
6、Zset 有序集合
相对于set 就是增加了排序的功能,其他功能set有的,他都有
6.1、增加和查看元素
127.0.0.1:6379> ZADD myset 1 one
(integer) 1
127.0.0.1:6379> ZADD myset 2 two 3 three
(integer) 2
127.0.0.1:6379> ZRANGE myset 0 -1
1) "one"
2) "two"
3) "three"
6.2、排序
127.0.0.1:6379> ZADD salary 2500 xiaohong
(integer) 1
127.0.0.1:6379> ZADD salary 5000 zhangsan
(integer) 1
127.0.0.1:6379> ZADD salary 500 chenning
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf #按照从小到大的顺序排序 -inf 负无穷 +inf 正无穷
1) "chenning"
2) "xiaohong"
3) "zhangsan"
排序的其他操作
127.0.0.1:6379> ZADD salary 2500 xiaohong
(integer) 1
127.0.0.1:6379> ZADD salary 5000 zhangsan
(integer) 1
127.0.0.1:6379> ZADD salary 500 chenning
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf
1) "chenning"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> ZRANGEBYSCORE salary 0 -1
(empty array)
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 2500 withscores #显示从负无穷到2500的元素,并把数据显示出来
1) "chenning"
2) "500"
3) "xiaohong"
4) "2500"
127.0.0.1:6379> ZREVRANGE salary 0 -1
1) "zhangsan"
2) "chenning"
127.0.0.1:6379> ZREVRANGE salary 0 -1 withscores #使用 revrange 进行倒序排序
1) "zhangsan"
2) "5000"
3) "chenning"
4) "500"
6.3、移除元素
127.0.0.1:6379> ZRANGE salary 0 -1
1) "chenning"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> zrem salary xiaohong # remove 这个元素
(integer) 1
127.0.0.1:6379> ZRANGE salary 0 -1
1) "chenning"
2) "zhangsan"
6.4、获取元素个数
127.0.0.1:6379> zcard salary
(integer) 2 获取个数
五、三大特殊数据类型
1、geospatial
1.1、 getadd
添加国家城市的经纬度信息
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
(integer) 1
127.0.0.1:6379> GEOADD china:city 114.05 22.52 shenzheng
(integer) 1
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
注意事项
- 两级无法直接添加,一般可以可以通过Java一键导入所有城市的信息
- 有效的经度从-180度到180度
- 有效的维度信息从-85.05112878到85.05112878度
- 当坐标位置超出上述的指定的范围的时候,该命令回返回一个错误
1.2、getpos
获取城市的位置信息
127.0.0.1:6379> GEOPOS china:city beijing chongqing
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
1.3、geodist
获取两点之间的距离信息
127.0.0.1:6379> GEODIST china:city beijing shanghai km # 这后面的km是可以换的, 比如 m 等都可以
"1067.3788"
127.0.0.1:6379> GEODIST china:city beijing chongqing km
"1464.0708"
1.4、georadius
查询指定范围内的城市信息
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km #查询以经纬度110 30为圆心周围1000km的城市位置信息
1) "chongqing"
2) "xian"
3) "shenzheng"
4) "hangzhou"
其他的附带参数
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord #跟上面的一样,但是带经纬度的参数
1) 1) "chongqing"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) 1) "108.96000176668167114"
2) "34.25999964418929977"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist #带距离指定位置的距离参数
1) 1) "chongqing"
2) "341.9374"
2) 1) "xian"
2) "483.8340"
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"
通过指定元素来查找元素
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km #距离北京1000km的所有元素
1) "beijing"
2) "xian"
1.5、将经纬度转化
127.0.0.1:6379> GEOHASH china:city beijing chongqing #二维的经纬度转换为一位的字符串 字符串长得越想就越接近
1) "wx4fbxxfke0"
2) "wm5xzrybty0"
1.6、像set一样操作
因为其实本质上就是zset所有下面这些操作是都可以进行的
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqing"
2) "xian"
3) "shenzheng"
4) "hangzhou"
5) "shanghai"
6) "beijing"
127.0.0.1:6379> ZREM china:city beijing
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqing"
2) "xian"
3) "shenzheng"
4) "hangzhou"
5) "shanghai"
2、hyperloglog
hyperloglog主要就是关于基数的操作,而基数就是不重复的元素
优点就是占用的内存固定,2^64不同的元素的技术,只需要12kb的内存,例如在统计网站的访问的次数的时候,一个人访问多次,就可以记为一次。虽然有0.81%的错误率,但是可以忽略
127.0.0.1:6379> PFADD mykey a b c d e f g h i j # 添加元素
(integer) 1
127.0.0.1:6379> PFCOUNT mykey # 计数元素的个数
(integer) 10
127.0.0.1:6379> PFADD mykey2 i j z x c v b n m
(integer) 1
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 #将mykey mykey2中的元素合并到mykey3中
OK
127.0.0.1:6379> PFCOUNT mykey3 #合并后只有不重复的元素,所有这里只有15个元素
(integer) 15
3、bitmaps
位存储
统计用户信息,是否活跃,是否登录,两个状态的,都可以使用Bitmaps,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 0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(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> getbit sign 5
(integer) 0
获取sign为1的数量
127.0.0.1:6379> BITCOUNT sign
(integer) 3
六、事务
- Redis事务的本质就是一组命令的组合,一个事务中所有的命令都会被序列化,在事务执行的过程中,会按照顺序执行,一次性,顺序性,排他性的执行一些列的命令
- Redis的事务没有隔离级别的概念,所有的命令在事务中,只有发起执行命令exec 才会执行
- Redis单条命令式保留原子性,但是事务并不保留原子性,
redis的事务
- 开启事务(multi)
- 命令入队
- 执行事务(exec)
1、正常的执行事务
127.0.0.1:6379> multi #开启事务命令
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> exec #执行事务命令
1) OK
2) OK
3) "v2"
4) OK
2、放弃事务
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> DISCARD #放弃事务命令
OK
127.0.0.1:6379> get k4
(nil)
3、执行事务时的异常
事务的异常分为两种,编译时的异常和执行时的异常,下面主要写一下两种异常的不同点
3.1、编译时异常
编译时的异常即代表你的语法可能就有问题,一旦一句有问题,整个事务都不能正常运行
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> getset k3
(error) ERR wrong number of arguments for 'getset' command #这里就开始提示有err
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors. # 导致整个事务都不能正常运行
127.0.0.1:6379> get k5
(nil)
3.2、运行时错误
运行时错误表示语法没有啥问题,是其他地方除了问题,仅仅只影响那一句话,其他的依旧可以正常的执行
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> incr k1 #这里的k1对应的value是一个字符串,是不能增加 1 的
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> get k3
QUEUED #前面的语句语法都没有任何的问题
127.0.0.1:6379(TX)> exec
1) (error) ERR value is not an integer or out of range #事务的第一句不能正常执行
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3" #事务的其他语句是可以正常运行的
七、Redis实现乐观锁
悲观锁
认为什么时候都会出错,无论做什么都会枷锁,效率低下
乐观锁
-认为什么时候都不会出问题,所以不会上锁,更新数据的时候去判断一下,在此期间是否有人去修改过这个数据
- 获取version
- 更新的时候比较version
单线程的时候
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 # 加锁
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 20
QUEUED
127.0.0.1:6379(TX)> INCRBY out 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
2) (integer) 20
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 10
QUEUED
127.0.0.1:6379(TX)> INCRBY out 10
QUEUED
127.0.0.1:6379(TX)> exec # 在执行这个事务前,有其他的线程改变了这个值,则会导致执行失败
(nil)
若执行失败,则正确操作为: