狂神redis笔记总结-上

狂神完整Redis笔记:https://blog.csdn.net/lijie0213/article/details/108129934

9、Linux下安装Redis详解

2020年9月26日 最新稳定版本是redis 6.0.6 http://www.redis.cn/

linux安装redis-6.0.1单机和集群

数据库事务正确执行的四个基本元素的缩写:ACID

原子性(Atomicity),一致性(Consistency),隔离性(Isolation),持久性(Durability).一个支持事务(Transaction)的数据库,必须要具有四种特性,否则在失误过程当中无法保证数据的正确性,交易过程及可能达不到交易方的要求.

原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)
事务前后数据的完整性必须保持一致。
隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响.(数据永久性)

开发的原因,需要对吞吐量(TPS)、QPS、并发数、响应时间(RT)几个概念做下了解,查自百度百科,记录如下:
1. 响应时间(RT)
  响应时间是指系统对请求作出响应的时间。直观上看,这个指标与人对软件性能的主观感受是非常一致的,因为它完整地记录了整个计算机系统处理请求的时间。由于一个系统通常会提供许多功能,而不同功能的处理逻辑也千差万别,因而不同功能的响应时间也不尽相同,甚至同一功能在不同输入数据的情况下响应时间也不相同。所以,在讨论一个系统的响应时间时,人们通常是指该系统所有功能的平均时间或者所有功能的最大响应时间。当然,往往也需要对每个或每组功能讨论其平均响应时间和最大响应时间。
  对于单机的没有并发操作的应用系统而言,人们普遍认为响应时间是一个合理且准确的性能指标。需要指出的是,响应时间的绝对值并不能直接反映软件的性能的高低,软件性能的高低实际上取决于用户对该响应时间的接受程度。对于一个游戏软件来说,响应时间小于100毫秒应该是不错的,响应时间在1秒左右可能属于勉强可以接受,如果响应时间达到3秒就完全难以接受了。而对于编译系统来说,完整编译一个较大规模软件的源代码可能需要几十分钟甚至更长时间,但这些响应时间对于用户来说都是可以接受的。

2. 吞吐量(Throughput)
吞吐量是指系统在单位时间内处理请求的数量。对于无并发的应用系统而言,吞吐量与响应时间成严格的反比关系,实际上此时吞吐量就是响应时间的倒数。前面已经说过,对于单用户的系统,响应时间(或者系统响应时间和应用延迟时间)可以很好地度量系统的性能,但对于并发系统,通常需要用吞吐量作为性能指标。
  对于一个多用户的系统,如果只有一个用户使用时系统的平均响应时间是t,当有你n个用户使用时,每个用户看到的响应时间通常并不是n×t,而往往比n×t小很多(当然,在某些特殊情况下也可能比n×t大,甚至大很多)。这是因为处理每个请求需要用到很多资源,由于每个请求的处理过程中有许多不走难以并发执行,这导致在具体的一个时间点,所占资源往往并不多。也就是说在处理单个请求时,在每个时间点都可能有许多资源被闲置,当处理多个请求时,如果资源配置合理,每个用户看到的平均响应时间并不随用户数的增加而线性增加。实际上,不同系统的平均响应时间随用户数增加而增长的速度也不大相同,这也是采用吞吐量来度量并发系统的性能的主要原因。一般而言,吞吐量是一个比较通用的指标,两个具有不同用户数和用户使用模式的系统,如果其最大吞吐量基本一致,则可以判断两个系统的处理能力基本一致。

3. 并发用户数
  并发用户数是指系统可以同时承载的正常使用系统功能的用户的数量。与吞吐量相比,并发用户数是一个更直观但也更笼统的性能指标。实际上,并发用户数是一个非常不准确的指标,因为用户不同的使用模式会导致不同用户在单位时间发出不同数量的请求。一网站系统为例,假设用户只有注册后才能使用,但注册用户并不是每时每刻都在使用该网站,因此具体一个时刻只有部分注册用户同时在线,在线用户就在浏览网站时会花很多时间阅读网站上的信息,因而具体一个时刻只有部分在线用户同时向系统发出请求。这样,对于网站系统我们会有三个关于用户数的统计数字:注册用户数、在线用户数和同时发请求用户数。由于注册用户可能长时间不登陆网站,使用注册用户数作为性能指标会造成很大的误差。而在线用户数和同事发请求用户数都可以作为性能指标。相比而言,以在线用户作为性能指标更直观些,而以同时发请求用户数作为性能指标更准确些。
4. QPS每秒查询率(Query Per Second)
  每秒查询率QPS是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准,在因特网上,作为域名系统服务器的机器的性能经常用每秒查询率来衡量。对应fetches/sec,即每秒的响应请求数,也即是最大吞吐能力。 (看来是类似于TPS,只是应用于特定场景的吞吐量)

单点登录英文全称Single Sign On,简称就是SSO。它的解释是:在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。

redis 6.0后版本为多线程.

13、String字符串类型详解

ping ok #检查redis是否安装成功

set key1 v1 #设置值

get key1 #获取值

keys * #获得所有的key

EXISTS key1 #判断某一个key是否存在

APPEND key1 hello #追加字符串,如果当前可以不存在就相当于setkey

STRLEN key1 #获取字符串长度

INCR views #自增1

DECR views #自减1

incrby views 11 #设置步长,指定增量

flushdb #清空数据库内容

flushall #清空所有数据库内容

getrange key1 0 3 #截取字符串[0,3]

getrange key1 0 -1 #获得全部的字符串

setrange key2 1 xxx #替换指定位置开始的字符串

#setex(set with expire)  #设置过期时间
#setnx(set if not exist) #不存在再设置(分布式锁中会常使用)

setex key3 30 hello #设置key3过期时间30s

ttl key3 #查看剩余的存活时间

setnx mykey "redis" #如果my可以不存在set mykey,否则原数据不变,创建失败

mset k1 v1 k2 v2 k3 v3 #同时设置多个值

mget k1 k2 k3 #同时获取多个值

msetnx mk1 mv1 mk2 mv2 #msetnx是一个原子性的操作,要么一起成功,要么一起失败.

set user:1 {name:ling,age:3} #设置一个user:1对象 值为json字符来保存一个对象.

#这里的可以是一个巧妙的设置: user:{id}:{filed},如此设计在redis中是完全OK的.

mset user:1:name ling user:1:age 2

127.0.0.1:6379> mget user:1:name user:1:age
1) "ling"
2) "2"

getset #先get然后在set

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 mongedb #如果存在值,获取原来的值,并设置新的值(用于更新操作)
"redis"
127.0.0.1:6379> get db
"mongedb"

B站获取关注粉丝数,用uid incr.

14、List列表类型详解

############################################################
127.0.0.1:6379> lpush list 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	#显示列表所有的值(先进去的在最后)
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1		#redis不区分大小写
1) "three"
2) "two"
127.0.0.1:6379> rpush list right #将一个值或多个值,插入到列表尾部(左)
(integer) 6
127.0.0.1:6379> lrange list 0 -1
1) "five"
2) "four"
3) "three"
4) "two"
5) "one"
6) "right"
############################################################
lpop
rpop
127.0.0.1:6379> lpop list	#移除list第一个值
"four"
127.0.0.1:6379> rpop list	#移除list最后一个值
"one"
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
############################################################
lidex
127.0.0.1:6379> lrange list 0 -1	#显示list全部值
1) "three"
2) "two"
127.0.0.1:6379> lindex list 1		#显示list下标为1的值
"two"
127.0.0.1:6379> lindex list 0
"three"
127.0.0.1:6379> lindex list -1
"two"
############################################################
llen
127.0.0.1:6379> llen list		#显示list的长度
(integer) 2
############################################################
lrem:移除指定的值
127.0.0.1:6379> lrange list 0 -1
1) "six"
2) "two"
3) "one"
4) "one"
5) "three"
6) "two"
127.0.0.1:6379> lrem list 1 six		#移除list集合中指定个数的value,精确匹配
(integer) 1
127.0.0.1:6379> lrem list 2 one
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "three"
3) "two"
############################################################
ltrim
127.0.0.1:6379> lrange list 0 -1
1) "six"
2) "five"
3) "one"
4) "three"
5) "two"
127.0.0.1:6379> ltrim list 1 2		#通过下标截取指定长度,这个list已经被改变了,截断了只剩下截断的元素.
OK
127.0.0.1:6379> lrange list 0 -1
1) "five"
2) "one"
############################################################
rpoplpush:#移除列表的最后一个元素,将他移动到新的列表中.
127.0.0.1:6379> lrange list 0 -1
1) "five"
2) "one"
127.0.0.1:6379> rpoplpush list mylist
"one"
127.0.0.1:6379> lrange list 0 -1
1) "five"
127.0.0.1:6379> lrange mylist 0 -1
1) "one"
############################################################
lset:#将列表中指定下标的指替换成另外一个值(更新操作)
127.0.0.1:6379> exists list		#判断这个列表是否存在
(integer) 1
127.0.0.1:6379> lset list 1 item		#如果不存在列表我们去更新就会报错
(error) ERR index out of range
127.0.0.1:6379> 
127.0.0.1:6379> lset list 0 item		#如果存在更新下标的指
OK
127.0.0.1:6379> lrange list 0 0
1) "item"
############################################################
linsert #将某个具体的值插入到指定元素的前面或后面
127.0.0.1:6379> linsert list before 1 2222
(integer) 5
127.0.0.1:6379> linsert list after 1 33333
(integer) 6
127.0.0.1:6379> lrange list 0 -1
1) "2222"
2) "1"
3) "33333"
4) "two"
5) "one"
6) "item"
127.0.0.1:6379> 

image-20200913212926589

15、Set集合类型详解

set中是不能重复读的!

############################################################
127.0.0.1:6379> sadd myset hello ling china		#set集合中添加匀速
(integer) 3
127.0.0.1:6379> SMEMBERS myset			#查看指定set的所有值
1) "ling"
2) "hello"
3) "china"
127.0.0.1:6379> sismember myset hello	#判断一个值是不是在set集合中
(integer) 1
127.0.0.1:6379> sismember myset hello1
(integer) 0
127.0.0.1:6379> SMEMBERS myset
1) "ling"
2) "hello"
3) "china"
############################################################
127.0.0.1:6379> scard myset		#获取set集合中的内容元素个数
(integer) 3	
############################################################
127.0.0.1:6379> SMEMBERS myset		#查看指定集合set的所有值
1) "ling"
2) "hello"
3) "china"
127.0.0.1:6379> srem myset hello	#移除set集合中的指定元素
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "ling"
2) "china"
############################################################
set 无序不重复集合,抽随机

127.0.0.1:6379> SRANDMEMBER myset  #随机抽出一个元素
"ling"
127.0.0.1:6379> SRANDMEMBER myset 2 #随机抽出指定个数的元素
"china"

############################################################
删除指定的key,随机删除key

127.0.0.1:6379> SMEMBERS myset
1) "shanghai"
2) "helll1"
3) "ling"
4) "china"
5) "java"
127.0.0.1:6379> spop myset		#随机删除一个set集合中的元素
"ling"
127.0.0.1:6379> SMEMBERS myset
1) "java"
2) "helll1"
3) "shanghai"
4) "china"
############################################################

127.0.0.1:6379> smembers myset
1) "ling"
2) "hello"
3) "world"
4) "goodbye"
5) "seeyou"
127.0.0.1:6379> smove myset myset2 seeyou #将一个指定的值,移动到另一个set集合
(integer) 1
127.0.0.1:6379> smembers myset2
1) "seeyou"
############################################################
拼多多,抖音,qq,共同关注,共同好友
数字集合类:
-差集
-交集
-并集

127.0.0.1:6379> sadd user1 a b c d
(integer) 4
127.0.0.1:6379> sadd user2 b e f j
(integer) 4
127.0.0.1:6379> sdiff user1 user2		#差集
1) "a"
2) "c"
3) "d"
127.0.0.1:6379> sinter user1 user2		#交集,共同好友
1) "b"
127.0.0.1:6379> sunion user1 user2		#并集
1) "d"
2) "c"
3) "a"
4) "f"
5) "b"
6) "j"
7) "e"
微博,A用户所有关注的人放在一个set集合中,将它的粉丝也放在一个集合中.
共同关注,共同爱好,二度好友(你可能认识),推荐好友(六度分隔理论)

16、Hash哈希类型详解

Map集合,key-may,这个值是map集合,本质和string类型没有太大区别,还是一个key-value

set myhash field ling

############################################################
127.0.0.1:6379> HSET myhash field1 ling	#set一个具体key-value
(integer) 1
127.0.0.1:6379> hget myhash field1		#获取一个字段值
"ling"
127.0.0.1:6379> hmset myhash field1 hello field2 redis		#set多个key-value
OK
127.0.0.1:6379> hmget myhash field1 field2		#获得多个字段值
1) "hello"
2) "redis"
127.0.0.1:6379> hgetall myhash		#获得全部数据
1) "field1"
2) "hello"
3) "field2"
4) "redis"
127.0.0.1:6379> hdel myhash field1		#删除hash指定key字段,对应的value值也就消失了 
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "redis"
############################################################
hlen

127.0.0.1:6379> HGETALL myhash
1) "field2"
2) "hi"
3) "field1"
4) "hello"
127.0.0.1:6379> keys *
1) "myhash"
127.0.0.1:6379> hlen myhash		#获取hash表的字段数量
(integer) 2
############################################################
127.0.0.1:6379> hexists myhash field1		#判断hash中指定字段是否存在
(integer) 1
127.0.0.1:6379> hexists myhash fiel
(integer) 0
############################################################
127.0.0.1:6379> hkeys myhash		#只获得所有field
1) "field2"
2) "field1"
127.0.0.1:6379> hvals myhash		#只获得所有value
1) "hi"
2) "hello"
############################################################
incr	decr
127.0.0.1:6379> hset myhash field3 5
(integer) 1
127.0.0.1:6379> hincrby myhash field3 1		#指定增量
(integer) 6
127.0.0.1:6379> hincrby myhash field3 -1	#指定减量
(integer) 5
127.0.0.1:6379> hsetnx myhash field4 hello	#如果不存在则可以设置
(integer) 1
127.0.0.1:6379> hsetnx myhash field5 wor
(integer) 1
127.0.0.1:6379> hsetnx myhash field4 adaw	#如果存在则不能设置

hash

hash变更的数据user name age,尤其是用户信息之类,经常变动的信息.

hash更适合存储对象,string更适字符串存储.

17、Zset有序集合详解

在set的基础上,增加了一个值,set k1 v1 zset k1 score1 v1

############################################################
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"
############################################################
127.0.0.1:6379> zadd salary 5000 zhangsan	#添加三个值
(integer) 1
127.0.0.1:6379> zadd salary 3000 ling
(integer) 1
127.0.0.1:6379> zadd salary 10000 dalao
(integer) 1
#zrangebyscore key min max
127.0.0.1:6379> zrangebyscore salary -inf +inf	#显示全部用户,从小到大
1) "ling"
2) "zhangsan"
3) "dalao"
127.0.0.1:6379> zrevrange salary 0 -1	#显示全部用户,从大到小排序
1) "dalao"
2) "zhangsan"
3) "ling"
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores	#显示全部用户并附带成绩
1) "ling"
2) "3000"
3) "zhangsan"
4) "5000"
5) "dalao"
6) "10000"
127.0.0.1:6379> zrangebyscore salary -inf 3000 withscores	#显示工资小于3000员工的升序排序
1) "ling"
2) "3000"
############################################################
#移除rem中的元素

127.0.0.1:6379> zrange salary 0 -1
1) "ling"
2) "zhangsan"
3) "dalao"
127.0.0.1:6379> zrem salary zhangsan	#移除有序集合中的指定元素
(integer) 1
127.0.0.1:6379> zcard salary		#获得有序集合中的个数
(integer) 2
############################################################
127.0.0.1:6379> zadd myset 1 hello 2 hi 3 ling
(integer) 3
127.0.0.1:6379> zcount myset 1 3		#获取指定区间的成员数量
(integer) 3
127.0.0.1:6379> zcount myset 2 3
(integer) 2

按理思路:set 排序,储存班级成绩表,工资表排序.

普通消息,1,重要消息,2,带权重进行判断.

排行榜应用实现,取Top N测试.

三种特殊的数据类型

18、Geospatial地理位置详解

geospatial地理位置,朋友的定位,附近的人,打车距离计算

redis的Geo在redis3.2版本就推出了!这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的人!

只有六个命令

GEOADD
GEODIST
GEOHASH
GEOPOS 
GOERADIUS
GEORADIUSBYMEMBER

geoadd

image-20200922175123854

#geoadd 添加地理位置
#规则:两极无法直接添加,一般会下载城市数据,通过Java程序一次性导入!
#参数key 值()
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijin
(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 shengzheng 120.16 30.24 hangzhou 102.96 34.26 xian
(integer) 3

geopos

127.0.0.1:6379> geopos china:city beijin	#获取指定的城市的精度和纬度
1) 1) "61.399999558925629"
   2) "39.900000091670925"
127.0.0.1:6379> geopos china:city beijin shanghai
1) 1) "61.399999558925629"
   2) "39.900000091670925"
2) 1) "121.47000163793564"
   2) "31.229999039757836"

geodist

两个人之间的距离!

单位:

  • m表示单位为米.
  • km表示单位为千米.
  • mi表示单位为英里
  • ft表示单位为英尺.
127.0.0.1:6379> geodist china:city beijin shanghai
"5417753.9752"
127.0.0.1:6379> geodist china:city beijin shanghai km	#查看北京到上海的直线距离
"5417.7540"
127.0.0.1:6379> geodist china:city beijin xian km
"3706.9058"
127.0.0.1:6379> geodist china:city beijin chongqing km
"4233.6142"

georadius 以给定的经纬度为中心,找出某一半径内的元素

我附近的人(获得所有附近人的地址,定位)通过半径来查询!

获得指定数量的人,200

所有数据都应该录入:china:city ,才会让结果更加真实.

127.0.0.1:6379> georadius china:city 110 30 1000 km	#以110,30这个经纬度为中心,寻找房源1000公里内的城市
1) "chongqing"
2) "xian"
3) "shengzheng"
4) "hangzhou"
127.0.0.1:6379> georadius china:city 110 30 700 km
1) "chongqing"
127.0.0.1:6379> georadius china:city 110 30 1000 km withdist	#显示到中间距离的位置
1) 1) "chongqing"
   2) "341.9374"
2) 1) "xian"
   2) "814.6289"
3) 1) "shengzheng"
   2) "924.6408"
4) 1) "hangzhou"
   2) "977.5143"
127.0.0.1:6379> georadius china:city 110 30 1000 km withcoord	#显示他人的定位信息
1) 1) "chongqing"
   2) 1) "106.49999767541885"
      2) "29.529999579006592"
2) 1) "xian"
   2) 1) "102.96000212430954"
      2) "34.2599996441893"
3) 1) "shengzheng"
   2) 1) "114.04999762773514"
      2) "22.520000087950386"
4) 1) "hangzhou"
   2) 1) "120.16000002622604"
      2) "30.240000322949022"
127.0.0.1:6379> georadius china:city 110 30 1000 km withcoord withdist count 1	#筛选出指定的结果
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885"
      2) "29.529999579006592"
127.0.0.1:6379> georadius china:city 110 30 1000 km withcoord withdist count 2
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885"
      2) "29.529999579006592"
2) 1) "xian"
   2) "814.6289"
   3) 1) "102.96000212430954"
      2) "34.2599996441893"

georadiusbymember

#找出位于指定元素周围的其他元素
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijin 1000 km
1) "beijin"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 500 km
1) "hangzhou"
2) "shanghai"

geohash命令 返回一个或者多个位置元素的Geohash表示

该命令将返回11个字符的geohash字符串

#将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么则距离越近.
127.0.0.1:6379> geohash china:city beijin shanghai
1) "tr5dvpw47e0"
2) "wtw3sj5zbj0"

GEO 底层的实现原理其实就是Zset,我们可以使用Zset命令来操作geo

127.0.0.1:6379> zrange china:city 0 -1	#查看地图中的全部元素
1) "beijin"
2) "chongqing"
3) "xian"
4) "shengzheng"
5) "hangzhou"
6) "shanghai"
127.0.0.1:6379> zrem china:city beijin	#移除指定元素
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "xian"
3) "shengzheng"
4) "hangzhou"
5) "shanghai"

19、Hyperloglog基数统计

什么是基数?

image-20200922194032537

简介

redis 2.8.9 版本就更新了Hyperloglog数据结构.

redis Hyperloglog 基数统计的算法.

优点:占用的内存时固定,2^64(long)不同的元素的技术,只需要12KB内存,如果要从内存角度Hyperloglog首选.

网页的UV(一个人访问一个网站多次,但还是算作一个个)

传统的方式,set保存用户的id,然后就可以统计set中的元素数量作为标准判断.

这个方式如果保存大量的用户id,就会比较麻烦,我们的目的是为了计数,而不是保存用户id.

0.81%错误率,统计UV任务,可以忽略不计.

127.0.0.1:6379> pfadd mykey a b c d e f	#创建一组元素 mykey
(integer) 1
127.0.0.1:6379> pfcount mykey	#统计 mykey 元素的基数数量
(integer) 6
127.0.0.1:6379> pfadd mykey2 b c a j d w	#创建二组元素 mykey
(integer) 1
127.0.0.1:6379> pfcount mykey2
(integer) 6
127.0.0.1:6379> pfmerge mykey3 mykey mykey2	#合并两组 mykey mykey2 =>mykey3 并集
OK
127.0.0.1:6379> pfcount mykey3	#看并集的数量
(integer) 8

如果允许容错,那么一定可以使用Hyperloglog.

如果不允许容错,就是用set或者自己的数据类型即可.

20、Bitmaps位图场景详解

位存储

统计用户信息,活跃,不活跃!登录,未登录!打卡,未打卡!两个的状态,都可以使用bitmaps

Bitmaps位图,数据结构!都是操作二进制位来进行记录,就只有0和1两个状态.

365天 = 365bit 1字节 = 8bit 46个字节左右!

周打卡

127.0.0.1:6379> setbit sign 6 0 #设置0~6一周,打卡为1,没打卡为0
(integer) 0
127.0.0.1:6379> getbit sign 0	#获取指定日期打卡状态
(integer) 1
127.0.0.1:6379> bitcount sign	#统计这周的打卡记录,就可以看到是否有全勤
(integer) 6

21、Redis基本的事务操作

Redis事务本质:一组命令的集合.一个事务中的所有命令都会被序列化, 在事务执行过程中,会按照顺序执行.

一次性,顺序性,排他性!执行一系列命令.

------队列 set set set get 执行-------

Redis事务没有隔离级别的概念.

所有的命令在事务中,并没有直接被执行.只有发起执行命令的时候才会执行.exec

Redis单条命令保存原子性,但事务不保证原子性.

redis事务:

  • 开启事务(multi)
  • 命令入队(写命令...)
  • 执行事务(exec)

所以事务中的命令在加入时都没有被执行,直到提交时才会开始执行(Exec)一次性完成。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> keys *
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) "v1"
4) OK
5) 1) "k4"
   2) "k1"
   3) "k2"
   4) "k3"

错误:一次只能添加一个值到事物中,ERR syntax error

127.0.0.1:6379> set k1 v1 k2 v2
QUEUED
127.0.0.1:6379> keys *
QUEUED
127.0.0.1:6379> exec
1) (error) ERR syntax error
2) (empty list or set)

取消事务(discard)

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> discard	#放弃事务
OK
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI	#当前未开启事务
127.0.0.1:6379> get k1	# 被放弃事务中命令并未执行
(nil)

事务错误

代码语法错误(编译时异常),所有的命令都不执行

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> error k1	#这是一条语法错误命令
(error) ERR unknown command 'error'	#会报错但是不影响后续命令入队
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.	#执行报错
127.0.0.1:6379> get k1
(nil)	#其他命令都没有被执行

代码逻辑错误(运行时异常) 其他命令可以正常执行 >>>所以不保证事务原子性

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> incr k1		#这条命令逻辑错误(对字符串进行增量)
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range	#报错,不是int类型
4) "v2"

虽然中间有一条命令报错了,但是后面的指令依旧正常执行成功了.

所有说Redis单条指令保证原子性,但是Redis事务不能保证原子性.

监控 watch

悲观锁:

  • 很悲观,认为什么时候都会出现问题,无论做什么都会加锁 (synchronize 影响性能效率)

乐观锁:

  • 很乐观,认为什么时候都不会出现问题,所有不会上锁.更新数据的时候去判断一下,在此期间是否有人修改过这个数据.
  • 获取version
  • 更新的时候比较version

使用watch key监控指定数据,相当于乐观锁加锁.

正常执行

127.0.0.1:6379> set money 100	#设置余额:100
OK
127.0.0.1:6379> set use 0	#支出使用:0
OK
127.0.0.1:6379> watch money	#监控money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby use 20
QUEUED
127.0.0.1:6379> exec		#监视值没有被中途修改
1) (integer) 80
2) (integer) 20

测试多线程修改值,使用watch可以当做redis的乐观锁操作(相当于getversion)

我们启动另一个客户端模拟插队线程.

线程1:

127.0.0.1:6379> watch money	#money上锁
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby use 20
QUEUED
127.0.0.1:6379>		#此时事务并没有执行

模拟线程插队,线程2:

127.0.0.1:6379> incrby money 500	#修改了线程1中监视的money
(integer) 580

回到线程1,执行事务:

127.0.0.1:6379> exec	#执行之前,另一个线程修改了我们的值,这个时候就会导致事务执行失败
(nil)		#没有结果,说明事务执行失败
127.0.0.1:6379> get money	#线程2 修改生效
"580"
127.0.0.1:6379> get use		#线程1事务执行失败,数值没有被修改
"20"

22、jedis连接阿里云服务器

原文地址:https://www.cnblogs.com/gdf456/p/13836983.html