Redis入门与扩展

Redis

Nosql概述

大数据一般的数据库无法处理

1.单机Mysql的年代

数据量如果太大,一个机器放不下!

数据的索引,300万(B+Tree),一个机器内存放不下

访问量(读写混合),一个服务器承受不了
在这里插入图片描述
2.Memcached(缓存)+MySQL+垂直拆分
在这里插入图片描述
网站80%的情况都是在读,每次都要去查询数据库的话就十分的麻烦!

为了减轻数据库的压力,使用缓存

发展过程:优化数据结构和索引–>使用文件缓存(IO)–>Memcache

3.分库分表+水平拆分+MySQL集群
在这里插入图片描述
技术和业务在发展的同时,对人的要求也越来越高!

本质:数据库,(读,写)

为什么用NOSQL?

用户的个人信息,社交网络,地理位置。用户自己产生的数据,用户日志等等

就需要用到NoSQL

**NoSQL:**Not Only SQL

泛指非关系型数据库,随着web2.0互联网的诞生!传统的关系型数据库很难!尤其是超大规模的高并发社区!暴露出很多难以克制的问题,NoSQL在当今大数据情况下发展迅速。Redis式发展最快的。

NoSQL特点:

1.方便扩展(数据之间没有关系)

2.大数据量高性能(Redis一秒写8万次,读取11万,NoSQL的缓存记录集,十一黄宗羲力度的缓存,性能会比较高!)

3.数据类型是多样型的!(不需要事先设计数据库!随取随用!)

4.传统的DDBMS和NOSQL

传统的RDBMS

  • 结构化组织
  • SQL
  • 数据和关系都存在在单独的表中
  • 操作,数据定义语言
  • 严格的一致性
  • 基础的事务

NoSQL

  • 不仅仅十数据
  • 没有固定的查询语言
  • 键值对存储,列存储,文档存储,图形数据库
  • 最终在一致性
  • CAP定理和BASE(异地多活)
  • 高性能,高可用,高可扩

真正在公司中的实践:NoSQL+RDBMS

NoSQL的四大分类:

kv键值对:

新浪:Redis

美团:Redis+Tair

阿里,百度:Redis+memecache

文档型数据库:(bson和json一样)

MongDB:是一个基于分布式文件存储的数据库,c++编写,主要用来处理大量的文档。是一个介于关系型数据库和非关系型数据库中间的产品。

列存储数据库:

HNase,分布式文件系统,CounchDB

以列簇式存储,将同一列数据存在一起

图形(Graph)数据库

Neo4j,InfoGrid,Infinite Graph

利用图结构相关算法,比如最短路径寻址,N度关系查找等

Redis入门

**Redis概述:**Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

Redis能干什么?

1.内存存储,持久化,内存中是断电即失,所以持久化很重要(rdb,aof)

2.效率高,可以用与高速缓存

3.发布订阅系统

4.地图信息分析

5.计时器,计数器(浏览量!)

特性:

1.多样的数据类型

2.持久化

3.集群

4.事务

Redis基础知识

Redis有16个数据库,默认使用的是第0个,可以使用select进行切换数据库!

127.0.0.1:6379> select 3 #切换数据库
OK
127.0.0.1:6379[3]>DBSIZE #查看大小
(integer)0
127.0.0.1:6379[3]>Keys * #查看数据库所有的key
"name"

清除当前数据库flushdb

127.0.0.1:6379[3]>flushdb
OK
127.0.0.1:6379[3]>Keys *
(empty list or set)

请除全部数据库FLUSHALL

为什么redis的端口号是6379,mysql的端口号是3306!

https://blog.csdn.net/qq_25504829/article/details/115271672

https://www.php.cn/mysql-tutorials-418118.html

Redis是单线程的!

Redis是很快的,官方表示,Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器内存和网络带宽,既然可以使用单线程来实现,就使用单线程!

Redis为什么单线程还这么快?

Redis是c语言写的,官方提供的数据100000+的QPS,完全不比同样是使用Key-value的Memecache差!

核心:redis是将所有的数据全部放在内存中,所以说使用单线程去操作的效率就是最高的,多线程(CPU上下文切换:耗时的操作!!!),对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个CPU上的,在内存情况下,这个就是最佳的方案!

五大数据类型

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings)散列(hashes)列表(lists)集合(sets)有序集合(sorted sets) 与范围查询, bitmapshyperloglogs地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication)LUA脚本(Lua scripting)LRU驱动事件(LRU eviction)事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

Redis-Key

127.0.0.1:6379> select 3 #切换数据库
OK
127.0.0.1:6379>DBSIZE #查看大小
(integer)0
127.0.0.1:6379>Keys * #查看数据库所有的key
"name"
127.0.0.1:6379>flushdb
OK
127.0.0.1:6379>Keys *
(empty list or set)
127.0.0.1:6379>set name wangjie #set KEY
OK
127.0.0.1:6379>Keys *
"age"
"name"
127.0.0.1:6379>clear #清理
127.0.0.1:6379>get name
"wangjie"
127.0.0.1:6379>EXISTS name 10 #判断当前KEY是否存在
(integer) 1
127.0.0.1.6379>EXPTRE name  #设置KEY的过期时间 ,单位是秒
127.0.0.1:6379>ttl name   #查看KEY的剩余时间,单位是秒
(integer) 4

String(字符串)

127.0.0.1:6379>set key1 v1  #设置key1值为v1
ok
127.0.0.1:6379>get key1  #获得值
"v1"
127.0.0.1:6379>key *
"key1"
127.0.0.1:6379>EXISTS key1
(integer)1
127.0.0.1:6379>APPEND key1 "hello"  #追加key1内容"hello"
(integer)7
127.0.0.1:6379>get key1
"v1hello"
127.0.0.1:6379>STRLEN key1  #获取字符串长度
(integer)7
127.0.0.1:6379>APPEND key1 ",wangjie"
(integer)15
127.0.0.1:6379>STRLEN key1
(integer)15
127.0.0.1:6379>get key1
"v1hello,wangjie"
127.0.0.1:6379>APPEND wangjie "wangjie" #如果当前key不存在,APPEND就是新建set key
##########################################################
i++
127.0.0.1:6379>set views 0 #新建views为0,初始为0
ok
127.0.0.1:6379>get views
"0"
127.0.0.1:6379>incr views #views的值自增1
(integer)1
127.0.0.1:6379>incr views 
(integer)2
127.0.0.1:6379>get views
"2"
127.0.0.1:6379>decr views #views的值自减1
(integer)1
127.0.0.1:6379>INCRBY views 10 #views的值+10,可以设置,指定增量
(integer)11 
127.0.0.1:6379>>DECRBY views 10 #views的值-10
(integer)1
##########################################################
字符串范围
127.0.0.1:6379>keys *
(empty list or set)
127.0.0.1:6379>>set key1 "hello,wangjie"
ok
127.0.0.1:6379>get key1
"hello,wangjie"
127.0.0.1:6379>GETRANGE key1 0 3 #截取从03的字符串
"hell"
127.0.0.1:6379>GETRANGE key1 0 -1 #可以通过这样查看全部的字符串
"hello,wangjie"
##########################################################
替换
127.0.0.1:6379> set key2 abcdefg
ok
127.0.0.1:6379>get key2
"abcdefg"
127.0.0.1:6379>SETRANGE key2 1 xx
(integer) 7
127.0.0.1:6379>get key2
"axxdefg"
##########################################################
#setex (set with expire) #设置过期时间
#setnx (set if not exist)#不存在设置(在分布式锁中会常常使用)
127.0.0.1:6379>setex key3 30 "hello"
ok
127.0.0.1:6379>ttl key3
(integer) 26
127.0.0.1:6379>get key3
"hello"
127.0.0.1:6379>setnx mykey "redis"
(integer) 1
127.0.0.1:6379>keys *
"key2"
"mykey"
"key1"
127.0.0.1:6379>ttl key3
(integer) -2
127.0.0.1:6379>setnx mykey "MongDB"  #如果mykey存在,创建失败
(integer) 0
127.0.0.1:6379>get mykey
"redis"
##########################################################
mset
mget
127.0.0.1:6379>mset k1 v1 k2 v2 k3 v3 #同时设置多个值
ok
127.0.0.1:6379>keys *
"k1"
"k2"
"k3"
127.0.0.1:6379>mget k1 k2 k3 #同时获取多个值
"v1"
"v2"
"v3"
127.0.0.1:6379>msetnx k1 v1 k4 v4 #msetnx是一个原子性的操作,要么一起成功,要么一起失败
##########################################################
#对象
set user:1 {name:zhangsan,age:3} #设置一个user:1的对象 值为json字符来保存一个对象!
#这里的key是一个巧妙的设计: user:{id}:{filed}
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
"zhangsan"
"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 mongdb #如果存在值,获取原来的值,并设置新的值
"redis"
127.0.0.1:6379>get db
"mongdb"

数据结构是相同的

String类似的场景:value除了是我么的字符串还可以是我们的数字!

  • 计数器
  • 统计多单位的数量
  • 对象缓存存储

List

##########################################################
#lpush list插入
127.0.0.1:6379>keys *
(empty list or set)
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
"three"
"two"
"one"
127.0.0.1:6379>LRANGE list 0 1
"three"
"two"
127.0.0.1:6379>Rpush list righr #将一个值或多个值,插入到列表尾部(右)
(integer) 4
127.0.0.1:6379>LRANGE list 0 1 #通过区间具体取值
"three"
"two"
127.0.0.1:6379>LRANGE list 0 -1 #获取list全部的值
"three"
"two"
"one"
"righr"
##########################################################
#lpop rpop 取值
127.0.0.1:6379>LRANGE list 0 -1 #获取list全部的值
"three"
"two"
"one"
"righr"
127.0.0.1:6379>lpop list #移除list左边第一个元素
"three"
127.0.0.1:6379>rpop list #移除list右边第一个元素
"righr"
##########################################################
#lindex
127.0.0.1:6379>LRANGE list 0 -1
"two"
"one"
127.0.0.1:6379>lindex list 1 #通过下标获得list中的某一个值
"one"
127.0.0.1:6379>lindex list 0
"two"
##########################################################
#Llen
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>Llen list   #返回列表的长度
(integer) 3
##########################################################
#移除指定的值 Lrem
127.0.0.1:6379>Lrem list 1 one #移除list集合中指定个数的value,精确匹配
 (integer) 1
127.0.0.1:6379>LRANGE list 0 -1
"three"
"two"
127.0.0.1:6379>Lrem list 1 three
(integer) 1
127.0.0.1:6379>LRANGE list 0 -1
"two"
127.0.0.1:6379>Lpush list three
(integer) 2
127.0.0.1:6379>Lpush list three
(integer) 3
127.0.0.1:6379>lrem list 2 three
(integer) 2
127.0.0.1:6379>LRANGE list 0 -1
"two"
##########################################################
#trim 修剪: list 截断
127.0.0.1:6379>FlushDB
ok
127.0.0.1:6379>keys *
(empty list or set)
127.0.0.1:6379>Rpush mylist "hello"
(integer) 1
127.0.0.1:6379>Rpush mylist "hello1"
(integer) 2
127.0.0.1:6379>Rpush mylist "hello2"
(integer) 3
127.0.0.1:6379>Rpush mylist "hello3"
(integer) 4
127.0.0.1:6379>ltrim mylist 1 2 #通过下标截取指定的长度,这个list已经被改变了,阶段了只剩下截取的元素!
ok
127.0.0.1:6379>LRANGE mylist 0 -1
"hello1"
"hello2"
##########################################################
#rpop rpush 移出列表做后一个元素,将它移动到新的列表中
127.0.0.1:6379>Lpush mylist "hello"
(integer) 3
127.0.0.1:6379>rpoplpush mylist myotherlist
"hello2"
127.0.0.1:6379>lrange mylist 0 -1
"hello"
"hello1"
127.0.0.1:6379>lrange myotherlist 0 -1
"hello2"
##########################################################
#lset 将列表中指定下标的值替换为另外一个值,更新操作
127.0.0.1:6379>lset list 0 item #不存在会报错
(error) ERR no such key
127.0.0.1:6379>lpush list value1
(integer) 1
127.0.0.1:6379>lrange list 0 0
"value1"
127.0.0.1:6379>lset list 0 item
ok
127.0.0.1:6379>lrange list 0 0
"item"
##########################################################
#linsert #将某一个具体的值value插入到指定的值前或后
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 befor "world" "other"
(integer) 3
127.0.0.1:6379>lrange mylist 0 -1
"hello"
"other"
"world"
127.0.0.1:6379>linsert  mylist after "world" "new"
(integer) 4
127.0.0.1:6379>lrange mylist 0 -1
"hello"
"other"
"world"
"new"
  • 它实际上是一个链表,before Node after ,left right都可以插入值
  • 如果key不存在,创建新的链表
  • 如果key存在,新增内容
  • 如果移除了所有的值,空链表,也代表不存在
  • 在两边插入或改动值,效率最高,中间元素,相对来说效率会低一些

消息排队!消息队列(lpush rpop) ,栈(lpush lpop)

在Redis里面,可以把List玩成栈,队列,阻塞队列!

Set

set的值是不能重复的!

##########################################################
#setadd set集合中添加值
127.0.0.1:6379>sadd myset "hello"
(integer) 1
127.0.0.1:6379>sadd myset "wangjie"
(integer) 1
127.0.0.1:6379>sadd myset "learn wangjie"
(integer) 1
127.0.0.1:6379>SMEMBERS myset #获取myset的所有值
"hello"
"learn wangjie"
"wangjie"
127.0.0.1:6379>SISMEMBER myset hello #判断myset中是否有hello
(integer) 1
##########################################################
127.0.0.1:6379>scard myset #获取set集合中的内容元素个数!
(integer) 4
127.0.0.1:6379>srem myset hello #移除set集合中的指定元素
(integer) 1
127.0.0.1:6379>scard myset
(integer) 3
##########################################################
#set 无需不重复集合,抽随机!
127.0.0.1:6379>SRANDMEMBER myset #随机抽出一个元素
"wangjie"
127.0.0.1:6379>SRANDMEMBER myset 2 #随机抽出指定个数的元素
"wangjie"
"learn wangjie"

##########################################################
删除指定的key,随即删除key
127.0.0.1:6379>setadd myset"learnwangjie2"
ok
127.0.0.1:6379>SMEEMBERS myset
"learn wangjie"
"learnwangjie2"
"wangjie"
127.0.0.1:6379>spop myset #随即删除一些set集合中的元素
"learnwangjie2"
127.0.0.1:6379>SMEEMBERS myset
"learn wangjie"
"wangjie"

##########################################################
将一个指定的值,移动到另外一个set集合中!
127.0.0.1:6379>flushdb
127.0.0.1:6379>sadd myset "hello"
(integer) 1
127.0.0.1:6379>sadd myset "world"
(integer) 1
127.0.0.1:6379>sadd myset "wangjie"
(integer) 1
127.0.0.1:6379>sadd mysets "set2"
(integer) 1
127.0.0.1:6379>smove myset myset2 "wangjie" #将一个指定的值,移动到另外一个set集合中!
(integer) 1
127.0.0.1:6379>SMEEMBERS myset
"hello"
"world"
127.0.0.1:6379>SMEEMBERS myset2
"wangjie"
"set2"

##########################################################
127.0.0.1:6379>sadd key1 a
(integer) 1
127.0.0.1:6379>sadd key1 b
(integer) 1
127.0.0.1:6379>sadd key1 c
(integer) 1
127.0.0.1:6379>sadd key2 c
(integer) 1
127.0.0.1:6379>sadd key2 e
(integer) 1
127.0.0.1:6379>sadd key2 d
(integer) 1
127.0.0.1:6379>SDIFF key1 key2 #差集
"b"
"a"
127.0.0.1:6379>INTER key1 key2 #交集
"c"
127.0.0.1:6379>SUNION key1 key2 #并集
"b"
"a"
"c"
"d"
"e"

Hash

map集合,key-map集合!这个时候值是一个map集合.本质和String没有太大区别,还是一个简单的key-value!

set myhash

127.0.0.1:6379>hset myhash fieldl wangjie #set一个具体的key-value
(integer)1
127.0.0.1:6379>hget myhash fieldl
wangjie
127.0.0.1:6379>hmset myhash fieldl hello field2 world #set多个
ok
127.0.0.1:6379>hmget myhash fieldl field2 #获取多个
"hello"
"world"
127.0.0.1:6379>hgetall myhash  #获取全部数据
"fieldl"
"hello"
"field2"
"world"
127.0.0.1:6379>hdel myhash fieldl #删除指定的key,对应的value也没了
(integer) 1
127.0.0.1:6379>hgetall myhash
"field2"
"world"
##########################################################
127.0.0.1:6379>hlen myhash #获取当前key的数量
(integer) 1
127.0.0.1:6379>HEXISTS myhash field2  #判断当前hash的可以是否存在,存在为1,不存在为
(integer) 1
127.0.0.1:6379>HEXISTS myhash field3
(integer) 
##########################################################
#只获得所有field
#只获得所有的value
127.0.0.1:6379>hkeys myhash #只获得hash所有的key
"field2"
127.0.0.1:6379>hvals myhash #只获得所有的value
"world"
##########################################################
#incr decr HINCRBY hset hsetnx hstnx 与String一样

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

hash公示和与对象的存储,String更适合于字符串的存储

Zset(有序集合)

在set的基础上,增加了一个值

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 #遍历
"one"
"two"
"three"
##########################################################
#排序 -inf负无穷 +inf正无穷
127.0.0.1:6379>ZRANGEBYSCORE myset -inf +inf #显示全部的用户,从小到大
"one"
"two"
"three"
127.0.0.1:6379>ZRANGEBYSCORE myset -inf +inf withscores #显示所有用户并附带成绩
"one"
"1"
"two"
"2"
"three"
"3"
127.0.0.1:6379>ZRANGEBYSCORE myset -inf 2 withscores #显示小于2的降序排序
"one"
"1"
"two"
"2"
127.0.0.1:6379>ZREVRANGE myset 0 -1 #从大到小排序
"three"
"two"
"one"
##########################################################
#移除rem中的元素
127.0.0.1:6379>zrem myset three #移除有序集合中的指定元素
(integer) 1
127.0.0.1:6379>zrange myset 0 -1
"one"
"two"
127.0.0.1:6379>zcard mysetc #获取有序集合中的个数
(integer) 2
##########################################################
127.0.0.1:6379>zcount myset 1 2 #获取有序集合指定区间的成员数量
(integer) 2
127.0.0.1:6379>zadd myset 3 "three"
(integer) 1
127.0.0.1:6379>zcount myset 1 3
(integer) 3

其余的API,通过我们的学习,工作中有需要的话,可以去官方文档查看!

http://www.redis.cn/commands.html

三种特殊数据类型

geospatial(地理位置)

距离的计算,Redis的Geo,可以查询一些测试数据

这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的用户!

只有六个命令

  • GEOADD
  • GEODIST
  • GEOHASH
  • GEOPOS
  • GEORADIUS
  • GEORADIUSBYMEMBER

GEOADD

###########################################################geoadd 添加地理位置
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

有效经度为-180-180,纬度为-85-85.

GEOPOS

###########################################################Geopos 获取指定的城市地址
127.0.0.1:6379>GEOPOS china:city beijing
"116.40"
"39.90"
127.0.0.1:6379>GEOPOS china:city shanghai
"121.47"
"31.23"

GEODIST

两人之间的距离!

单位:

  • m表示单位为米
  • km表示单位为千米
  • mi表示单位为英里
  • ft表示单位为英尺
###########################################################GEODIST
127.0.0.1:6379>GEODIST china:city beijing shanghai #查看北京到上海的直线距离
"1037378.7564"
127.0.0.1:6379>GEODIST china:city beijing shanghai km
"1067.3788"

GEORADIUS

一给定的经度纬度为中心,给定的距离为半径,查找范围内的元素

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

###########################################################附近的人 (获得附近所有人的地址:定位!,通过半径查询)
127.0.0.1:6379>GEORADIUS china:city 116 30 1000km
"beijing"

GEORADIUSBYMEMBER

找出位于指定范围内的元素,中心点是由给定的位置元素决定

###########################################################附近的人 (获得附近所有人的地址:城市!,通过半径查询)
127.0.0.1:6379>GEORADIUSBYMEMBER china:city beijing 1000km
"beijing"

GEOHASH

返回一个或多个位置元素的Geohash表示

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

127.0.0.1:6379>GEOHASH china:city beijing chongqin
"wx4fbxxfke0"
"wm5xzrybty0"

Hyperloglog(基数统计的算法)

什么是基数?

基数:不重复的元素

场景:网页UV

优点:占用的内存是固定的,2^64不同元素的技术,只需要费12kb内存!

############################################################
127.0.0.1:6379>PFadd mykey a b c d e f g h i j #创建第一组元素mykey
(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>PFCOUNT mykey2
(integer) 9
127.0.0.1:6379>PFMERGE mykey3 mykey mykey2 #合并两组 mykey mykey2 => mykey3 并集
ok
127.0.0.1:6379>PFCOUNT mykey3 #看并集数量
(integer) 15

如果允许容错

Bitmaps(位存储)

统计用户信息,活跃,不活跃!登录,未登录!打卡

############################################################记录周一到周日的打卡
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 #查看是否打卡,1为是,0为否
(integer) 1
127.0.0.1:6379>getbit sign6
(integer) 0
############################################################ 统计操作  统计打卡的天数
127.0.0.1:6379>bitcount sign #统计这周打卡的天数
(integer) 3

事务

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

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

一次性,顺序性,排他性!

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

在所有的命令食物中,并没有直接被执行!只有发起执行命令的时候才会执行!

redis的事务

  • 开启事务(multi)
  • 命令入队()
  • 执行事务(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 k2
QUEUED
127.0.0.1:6379>set k3 v3
QUEUED
127.0.0.1:6379>exec #执行命令
ok
ok
"v2"
ok

放弃事务

127.0.0.1:6379>multi
127.0.0.1:6379>set k1 v1  #命令入队
QUEUED
127.0.0.1:6379>set k2 v2
QUEUED
127.0.0.1:6379>set k4 v4
QUEUED
127.0.0.1:6379>DISCARD  #放弃事务
ok
127.0.0.1:6379>get k4
(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>set k3 v3
QUEUED
127.0.0.1:6379>getset k2 #语法错误
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379>set k4 v4
QUEUED
127.0.0.1:6379>exec  #执行事务报错
(error) EXECABORT Transaction discarded because of previous errors.

运行时异常,如果事务队列中存在语法性,那么执行命令的时候,其他命令式可以正常执行的

127.0.0.1:6379>set k1 "v1"
ok
127.0.0.1:6379>multi #开启事务
ok
127.0.0.1:6379>incr k1
QUEUED
127.0.0.1:6379>set k2 v2
QUEUED
127.0.0.1:6379>set k3 v3
QUEUED
127.0.0.1:6379>get k3
QUEUED
127.0.0.1:6379>exec
(error) ERR value is not an integer or out of range
ok
ok
"v3"

悲观锁:

  • 无论做什么都会加锁!(影响性能)

乐观锁:

  • 不会上锁!

更新数据的时候去判断一下,在此期间是否有人修改过这个数据

Redis监视测试

正常执行成功!

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 #监视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
(integer) 80
(integer) 20

测试多线程修改值,使用watch可以当做redis的乐观锁操作!

#正在执行的事务1
127.0.0.1:6379>watch money #监视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)
#另一个事务2
127.0.0.1:6379>watch money #监视money 对象
ok
127.0.0.1:6379>multi
ok
127.0.0.1:6379>set money 1000
ok
127.0.0.1:6379>exec

多线程,当事务2在事务1之前修改了money的值,这个时候事务1修改money的值就会失败。

无论redis的事务执行是否成功,都会取消监视

127.0.0.1:6379>unwatch #放弃监视 #如果发现事务执行失败,就先解锁
ok
127.0.0.1:6379>watch money #再次监视 ,获取最新的值
ok
127.0.0.1:6379>DECRBY money 10
(integer) 990
127.0.0.1:6379>INCRBY out 10
(integer) 30
127.0.0.1:6379>exec #比对监视是否发生了变化,如果没有变化,那么可以执行成功,如果变化了就会失败
(integer) 990

如果发现事务执行失败,就先解锁,再次监视 ,获取最新的值,再次执行就好!

Jedis

我们要使用Java来操作Redis

Jedis:是Redis官方推荐的java连接开发工具!使用java操作Redis的中间件!

用IDEA进行测试

1.导入对应的依赖

 <!--导入jedis的包-->
 <dependencies>
        <!--Redis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.2.0</version>
        </dependency>

        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>

2.编码测试

  • 连接数据库
  • 操作命令
  • 断开连接
package com.wj.redis;

import com.alibaba.fastjson.JSONObject;
import netscape.javascript.JSObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class TestRedis {
    public static void main(String[] args) {
        //1.new Jedis 对象即可
        Jedis jedis=new Jedis("127.0.0.1",6379);
        //jedis所有的命令就是我们之前学习所有的指令
        System.out.println( jedis.ping());

        JSONObject jsObject=new JSONObject();
        jsObject.put("hello","world");
        jsObject.put("name","kuangshen");
        //jedis开启事务
        Transaction mutil=jedis.multi();
        String result=jsObject.toJSONString();
      try {
          mutil.set("user1",result);
          mutil.set("user2",result);
          //执行事务
          mutil.exec();
      }catch (Exception  e){
          //放弃事务
          mutil.discard();
          e.printStackTrace();
      }finally {
          jedis.close();
      }
    }
}

下图是redis中一些数据类型的方法调用:
在这里插入图片描述
在这里插入图片描述

在SpringBoot2.x之后,原来使用的Jedis被替换为了lettuce

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值