NoSQL Redis

一、NoSQL入门简介

1.1 Memcacheed + mysql +垂直拆分(读写分离)​​​​​​​​​​​​

在这里插入图片描述

1.2 分库分表 + 水平拆分 + mysql集群

​​​​在这里插入图片描述
数据库本质 读写
早些年mySAM:表锁
转战Innodb:行锁
逐渐使用分库分表i来解决写的压力(早些年代mysql推出表分区 但使用率不高 后来推出集群 但并不是解决所有问题)

1.3 用户访问大致过程

在这里插入图片描述

1.4 为什么用noSQL

用户的个人信息 社交网络 地理位置 用户自己产生的数据 用户日志,nosql可以很好的处理以上情况

1.5 什么是NoSQL

NoSQL = Not Only SQL 泛指非关系型数据库
传统关系型数据库很难对付web2.0时代 尤其是超大规模的高并发的社区 暴露出多难克服的问题
NoSQL在当今大数据环境下发展十分迅速,redis是发展最快的
很多数据类型 用户的跟人信息 社交网络 地理位置,这些数据类型的存储不需要一个固定的格式!不需要多余的操作就可以横向拓展!Map<String, Object> 使用键值对来控制!

1.6 NoSQL特点

解耦
1、方便拓展(数据之间没有关系,很好拓展)
2、大数据量性能高(NoSQL的缓存记录级别,是一种细粒度的缓存,性能会比较高)
3、数据类型是多样型的!(不需要事先设计数据库!随去随用!如果数据库的量是十分大的表,很多人无法设计)
4、传统RDBMS和NoSQL

传统RDBMS
--结构化组织
--SQL
--数据和关系都存在单独的表中
--数据操作语言、数据定义语言
--严格的一致性
--基础的事务
NoSQL
--不仅仅是数据
--没有固定的查询语言
--键值对存储 列存储 文档存储 图形数据库(社交关系)
--最终一致性
--CAP定理和BASE (异地多活)
--高性能 高可用 高扩展
--...

1.6.1 了解:3V+3高

大数据时代的3V:主要是描述问题的
1、海量 Volume
2、多样 Variety
3、实时 Velocity

大数据时代的3高:主要是解决问题的
1、高并发
2、高可扩
3、高性能

真正在公司种的时间:NoSQL + RDBMS (阿里巴巴架构演进)技术没有高低之分,就看你如何使用(提升内功,形成思维)

二、阿里巴巴演进

技术急不得,越是慢慢学,才能越扎实!
敏捷开发、极限编程
开源才是技术的王道

2.1 没有什么是加一层解决不了的

# 1、商品的基本信息
名称 价格 商家信息 关系型数据库就可以解决!mysql/oracle 淘宝早年就去IOE了! 王坚 推荐文章 阿里云的这群疯子
淘宝用的mysql不是大家用的mysql

# 2、商品的描述
评论(文字比较多) 放入文档型数据库 MongoDB

# 3、图片
分布式文件系统 FastDFS
--淘宝自己的 TFS
--Gooale的 	GFS
--Hadoop 	HDFS
--阿里云的	OSS

# 4、商品的关键字(搜索)
--搜索引擎 solr ealsticsearch
--ISearch 多隆(多了解一些技术大佬!)
(所有牛逼的人都有一段苦逼的岁月!但是你只要SB一样的去坚持,终将牛逼!)

# 5、商品热门的波段信息
--内存数据库
--Redis Tair Memache...

# 6、商品的交易 外部的支付接口
--三方接口

大型互联网应用的问题:

 数据类型多
 数据源多,经常重构
 数据要改造,大面积改造

解决问题(统一数据服务平台 UDSL):

模型字段的数据路 Mapping DSL
统一查询更新API
性能优化
热点缓存(UDSL的二级缓存)

以上为NoSQ入门概述

三、NoSQL四大分类

3.1 KV键值对

  • 新浪:Redis
  • 美团:redis + tair
  • 阿里、百度:redis + memecache

3.2 文档型数据库(bson与json)

  • MongoDB
基于分布式文件存储的数据库 C++编写 主要用来处理大量的文档
介于关系型数据库和非关系型数据之间的产品,非关系数据库中最像关系型数据的
  • ConchDB

3.3 列存储数据库

  • HBase
  • 分布式文件系统

3.4 图关系型数据库

他不是存图形的,放的是关系 朋友圈 广告推荐
  • Neo4j
  • InfoGrid

3.5 四者对比

四、Redis 入门

4.1 概述

4.1.1 Redis 是什么

Remote Dictionary Server 远程字典服务,是一个开源的使.用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。也被人们称之为结构化数据库!
在这里插入图片描述

4.1.2 Redis 能干嘛

1、内存存储、持久化,内存中是断电即失的,所以说持久化很重要 (rdb aof)
2、效率很重要,可以用于高速缓存
3、发布订阅系统
4、地图信息分析
5、计时器、计数器(浏览量)
6、…

4.1.3 Redis的特性

1、多样的数据类型
2、持久化
3、集群
3、事务

4.1.4 学习中要用到的东西

1、https://redis.io/
2、http://www.redis.cn/topics/introduction
3、哔哩哔哩狂神说java
注意:Windows版本在GitHub上下载 Redis推荐在LLinux上搭建

4.2 Windows安装

4.3 Linux安装

4.4 测试工具 redis-benchmark

redis-benchmark是一个压力测试工具
在这里插入图片描述

# 测试:100个并发连接 1000000请求
redis-benchmark -h localhost -p 6379 -c 100 -n 10000

# 切换数据库 select index 0开始
select 0

# 清空当前数据库 flushdb

#清空所有数据库 flushall

4.4 思考:Redis是单线程为什还这么块?

Redis是基于内存操作,CPU不是Redis的瓶颈,Redis瓶颈是机器内存以及带宽
Redis是C语言写的 10000+ QPS,完全不比Memecache差,Redis 是单线程的
1、误区1:高性能的服务器一定是多线程的?
2、误区2:多线程一定比单线程效率高?
核心源原因:redis将所有的数据放在内存中,所以单线程操作效率是最高的。对于内存来说,美哟上下文切换,效率就是最高的。多次读写都是在一个CPU上,在内存情况下,这个就是最佳方案!

五、五大数据类型

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

5.1 Redis-Key

redis-cli -h host -p port #连接
ping #ping pong
flushall #刷新数据库
select index #切换数据库 select 1
set key value #设置键值 set name tst
get key #查看键值 get name
key * #查看所有的key key *
exists key #判断key是否存储 exists name
move key db #移除key move name 0
expire key seconds #设置key的过期时间 expire name 10
ttl key #查看key的过期时间 ttl name
type key #查看key对应的value的类型 typr name

5.2 String

append key value #向key追加value,如果key不存在则相当于set key value append name "hello"
strlen key #查看字符串长度 strlen name 
incr key #自增 1
decr key #自减 1
incrby key increment #步长
decrby key decrement
getset

5.3 List

#######################################################
127.0.0.1:6379> lpop list
"three"
127.0.0.1:6379> rpop list
"last"
127.0.0.1:6379> lindex list 0
"two"
127.0.0.1:6379> lindex list 0
"two"
127.0.0.1:6379> lrem list 0 one
(integer) 1
127.0.0.1:6379> llen list
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "two"
#######################################################
ltrim 移除指定下标的元素
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> rpush a hello
(integer) 1
127.0.0.1:6379> rpush a world
(integer) 2
127.0.0.1:6379> rpush a !
(integer) 3
127.0.0.1:6379> rpush a my name is xiaoai
(integer) 7
127.0.0.1:6379> ltrim a 1 3 #移除指定区间内的元素
OK
127.0.0.1:6379> lrange a 0 -1
1) "world"
2) "!"
3) "my"
#######################################################
rpoplpush 从a中pop出复制到b
127.0.0.1:6379> rpush a 0 1 2 3 4
(integer) 5
127.0.0.1:6379> rpoplpush a b
"4"
127.0.0.1:6379> lrange a 0 -1
1) "0"
2) "1"
3) "2"
4) "3"
127.0.0.1:6379> lrange b 0 -1
1) "4"
#######################################################
lset 将指定位置的值替换问另一个值,更新操作
127.0.0.1:6379> exists list
(integer) 0
127.0.0.1:6379> lset lsit 0 item
(error) ERR no such key
127.0.0.1:6379> lset a 0 100
OK
127.0.0.1:6379> lrange a 0 -1
1) "100"
2) "1"
3) "2"
4) "3"
#######################################################
linsert 将值插入列表指定位置前面或后面
127.0.0.1:6379> lpush a 0 1 2 3 4
(integer) 5
127.0.0.1:6379> linsert a before 0 -1
(integer) 6
127.0.0.1:6379> lrange a 0 -1
1) "4"
2) "3"
3) "2"
4) "1"
5) "-1"
6) "0"
127.0.0.1:6379> linsert a after 0 -2
(integer) 7
127.0.0.1:6379> lrange a 0 -1
1) "4"
2) "3"
3) "2"
4) "1"
5) "-1"
6) "0"
7) "-2"
#######################################################

5.3.1 小结

  • 实际上是一个链表, before node after,left,right
  • 如果key不存在则创建新的链表
  • 如果key存在则新增内容
  • 如果移除了所有值,空链表,也代表不存在
  • 在两边插入或改动值效率最高,中间元素效率会偏低

消息排队 消息队列 lpush rpop 栈 lpush lpop

5.4 Set

set 中的值是不能重复的

#######################################################
127.0.0.1:6379> sadd s 0 1 0 0 2 2 #添加元素
(integer) 3
127.0.0.1:6379> smembers s #查看元素
1) "0"
2) "1"
3) "2"
127.0.0.1:6379> sismember s 3 #判断值是不是set中的元素
(integer) 0
127.0.0.1:6379> sismember s 1
(integer) 1
#######################################################
127.0.0.1:6379> scard s #获取集合中元素的个数
(integer) 3
127.0.0.1:6379> sadd s 4
(integer) 1
127.0.0.1:6379> scard s
(integer) 4
#######################################################
127.0.0.1:6379> srem s 4 #移除指定的值
(integer) 1
127.0.0.1:6379> smembers s
1) "0"
2) "1"
3) "2"
#######################################################
127.0.0.1:6379> srandmember s 1 #随机抽取出指定个数的元素
1) "1"
127.0.0.1:6379> srandmember s 1
1) "2"
#######################################################
127.0.0.1:6379> smembers s
1) "0"
2) "1"
3) "2"
127.0.0.1:6379> spop s #随机pop出一个集合中的值
"2"
127.0.0.1:6379> spop s
"0"
127.0.0.1:6379> smembers s
1) "1"
#######################################################
127.0.0.1:6379> sadd s  0 1 2 3 4 5
(integer) 6
127.0.0.1:6379> sadd b 6 7 8
(integer) 3
127.0.0.1:6379> smove s b 0 #将指定的值,移动到另一个set集合中
(integer) 1
127.0.0.1:6379> smembers b
1) "0"
2) "6"
3) "7"
4) "8"
127.0.0.1:6379> smembers s
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> sadd b 1 2 3
(integer) 3
127.0.0.1:6379> scard b
(integer) 3
#######################################################
微博 B站 共同关注(并集)
127.0.0.1:6379> sadd a 1 2 3 4 5 6 7
(integer) 7
127.0.0.1:6379> sadd b 4 5 6 7 8 9 10
(integer) 7
127.0.0.1:6379> sdiff a b #差集
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> sinter a b #交集
1) "4"
2) "5"
3) "6"
4) "7"
127.0.0.1:6379> sunion a b #并集
 1) "1"
 2) "2"
 3) "3"
 4) "4"
 5) "5"
 6) "6"
 7) "7"
 8) "8"
 9) "9"
10) "10"

5.4.1 应用场景

微博,A用户将所有关注的人放在一个set集合中,将他的偶分是也放在一个集合中
共同关注 共同爱好 二度好友 推荐好友(六度分割理论)

5.5 Hash

Map集合,key-map 是一个map集合

#######################################################
127.0.0.1:6379> hset a name tangshengtao #设置 值
(integer) 1
127.0.0.1:6379> hget a name # 获得值
"tangshengtao"
127.0.0.1:6379> hmset a age 18 adress guiyang #设置多值
OK
127.0.0.1:6379> hmget a name age adress #获得多值
1) "tangshengtao"
2) "18"
3) "guiyang"
127.0.0.1:6379> hgetall a #获取hash所有内容
1) "name"
2) "tangshengtao"
3) "age"
4) "18"
5) "adress"
6) "guiyang"
127.0.0.1:6379> hdel a name # 删除指定的field
(integer) 1
127.0.0.1:6379> hgetall a 
1) "age"
2) "18"
3) "adress"
4) "guiyang"
#######################################################
127.0.0.1:6379> hlen a #获取长度
(integer) 2
127.0.0.1:6379> hexists a name #判断指定字段是否存在
(integer) 0
127.0.0.1:6379> hexists a age
(integer) 1
127.0.0.1:6379> hkeys a #获取所有键
1) "age"
2) "adress"
127.0.0.1:6379> hvals a #获取所有值
1) "18"
2) "guiyang"
#######################################################
127.0.0.1:6379> hset a age 18
(integer) 1
127.0.0.1:6379> hincrby a age 1
(integer) 19
127.0.0.1:6379> hincrby a age -1
(integer) 18
127.0.0.1:6379> hsetnx a age 8 #存在则不是设置
(integer) 0
127.0.0.1:6379> hsetnx a email 1643506641@qq.com
(integer) 1

5.5.1 应用场景

变更的数据 user name age 尤其是用户信息之类的 经常变动的信息 hash更适合对象的存储 String更加适合字符串存储

5.6 Zset

有序集合,在set的基础上增加了一个值

#######################################################
127.0.0.1:6379> zadd a 1 one
(integer) 1
127.0.0.1:6379> zadd a 2 two 3 three
(integer) 2
127.0.0.1:6379> zrange a 0 -1
1) "one"
2) "two"
3) "three"
#######################################################
127.0.0.1:6379> zadd salary 2500 xaiohong 5000 zhangsan 500 tst
(integer) 3
127.0.0.1:6379> zrangebyscore salary -inf +inf
1) "tst"
2) "xaiohong"
3) "zhangsan"
127.0.0.1:6379> zrangebyscore salary -inf 2500 withscores
1) "tst"
2) "xaiohong"
#######################################################
127.0.0.1:6379> zrange a 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> zrem a one
(integer) 1
127.0.0.1:6379> zrange a 0 -1
1) "two"
2) "three"
#######################################################
127.0.0.1:6379> zcard a #获取集合中的个数
(integer) 2
#######################################################
127.0.0.1:6379> zrange a 0 -1
1) "two"
2) "three"
127.0.0.1:6379> zrevrange a 0 -1 #反转
1) "three"
2) "two"
#######################################################
127.0.0.1:6379> zcount a 1 3 #获取指定区间的成员数量
(integer) 2
127.0.0.1:6379> zcount a 1 2
(integer) 1

5.6.1 应用场景

案例: set 排序 存储班级成绩表 工资排序表
普通消息 重要消息 带权重判断
排行榜 取Top N实现

七、三种特殊数据类型

7.1 geospatial

朋友地位 附近的人 打车距离计算

7.1.1 geospatial学习文档

https://www.redis.net.cn/order/3685.html

7.1.2 geospatial命令

GEOADD GEODIST GEOHASH GEOPOS GEORADIUS GEORADIUSBYMEMBER

#######################################################
#添加地理位置
#geoadd 纬度 精度 名称
#有效的经度从-180度到180度
#有效的纬度从-85.05112878度到85.05112878度
127.0.0.1:6379> geoadd china:city 116.413384 39.910925 beijin
(integer) 1
127.0.0.1:6379> geoadd china:city 121.463615 31.195908 shanghai 114.064552 22.548457 shengzheng
(integer) 2
127.0.0.1:6379> geoadd china:city 106.806219 26.550098 guiyang 106.263584 26.411186 pingba
(integer) 2
#######################################################
#获取指定的城市的经纬度
127.0.0.1:6379> geopos china:city beijin guiyang 
1) 1) "116.41338318586349"
   2) "39.910924739867674"
2) 1) "106.80622011423111"
   2) "26.550097738289459"
#######################################################
#获取两个城市之间的直线距离
127.0.0.1:6379> geodist china:city guiyang pingba m 
"56189.3681"
#######################################################
# 附近的人 获得所有人的地址定位 通过半径查询
127.0.0.1:6379> georadius china:city 106 26 1000 km
1) "pingba"
2) "guiyang"
3) "shengzheng"
127.0.0.1:6379> georadius china:city 106 26 1000 km withdist
1) 1) "pingba"
   2) "52.7593"
2) 1) "guiyang"
   2) "101.0394"
3) 1) "shengzheng"
   2) "903.0384"
127.0.0.1:6379> georadius china:city 106 26 1000 km withcoord
1) 1) "pingba"
   2) 1) "106.26358240842819"
      2) "26.411184879873055"
2) 1) "guiyang"
   2) 1) "106.80622011423111"
      2) "26.550097738289459"
3) 1) "shengzheng"
   2) 1) "114.06455129384995"
      2) "22.548457402406278"
127.0.0.1:6379> georadius china:city 106 26 100 km withdist withcoord count 1
1) 1) "pingba"
   2) "52.7593"
   3) 1) "106.26358240842819"
      2) "26.411184879873055"
#######################################################
#获得以指定城市为中心,在指定范围内的城市
127.0.0.1:6379> georadiusbymember china:city guiyang 1000 km 
1) "pingba"
2) "guiyang"
3) "shengzheng"
#######################################################
#将二维坐标转为一维字符串,如果两者越相近,则距离越近
127.0.0.1:6379> geohash china:city beijin guiyang pingba
1) "wx4g1199ur0"
2) "wkezn3mct20"
3) "wkew604b2c0"
#######################################################
127.0.0.1:6379> zrange china:city 0 -1
1) "pingba"
2) "guiyang"
3) "shengzheng"
4) "shanghai"
5) "beijin"
127.0.0.1:6379> zrem china:city beijin
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "pingba"
2) "guiyang"
3) "shengzheng"
4) "shanghai"

7.2 hyperloglog

7.2.1 什么是基数

A {1,3,5,7,8,9}
B {1,3,5,7,8}
基数指的是不重复的元素 基数=9 可以接受误差

7.2.2 简介

Redis 2.8.9 更新了Hyperloglog数据结构
Redis Hyperloglog 基数统计算法
优点:占用内存是固定,2^64 不同的元素的技术,只需要12KB的内存!
网页的UV(一个人访问一个网站多次,但还是算作一个人)
传统方式 set 保存用户的id,然后统计set的元素数据作为标准
这种方式如果保存大量的用户id,就会比较麻烦,我们的目的是为了计数,而不是保存用户id
0.81%的错误率 可忽略不计

127.0.0.1:6379> pfadd m a b c d e f g
(integer) 1
127.0.0.1:6379> pfcount m
(integer) 7
127.0.0.1:6379> pfadd s b c d e f h k
(integer) 1
127.0.0.1:6379> pfcount s
(integer) 7
127.0.0.1:6379> pfcount m s
(integer) 9
127.0.0.1:6379> pfmerge v m s
OK
127.0.0.1:6379> pfcount v
(integer) 9

7.3 bitmaps

7.3.1 位存储

疫情
正常:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
感染:0 1 0 1 1 1 1 1 1 0 0 1 1 1 0 0
统计活跃与不活跃、登录与未登录、打卡未打卡!两个状态的,都可以使用 bitmaps
Bitmaps 都是操作二进制来进行记录的,只有0 和 1两个状态

#######################################################
#创建打卡记录
127.0.0.1:6379> setbit sign 0 0
(integer) 0
127.0.0.1:6379> setbit sign 1 1
(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 1
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
#######################################################
#查看某天是否打卡
127.0.0.1:6379> getbit sign 4
(integer) 1
127.0.0.1:6379> getbit sign 6
(integer) 0
#######################################################
#统计一个星期打卡次数 判断是否全勤
127.0.0.1:6379> bitcount sign
(integer) 4

八、事务

Redis 事务本质:一组命令的集合
Redis 单条命令保存原子性,但是事务不保证原子性,但保留一次性、顺序性、排他性
Redis 事务没有隔离级别的概念,所有命令在事务中,被没有直接被执行!只有发起执行命令的时候才执行!exec
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
1) OK
2) OK
3) "v2"
4) OK
#######################################################
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 k4 v4
QUEUED
#撤销事务
127.0.0.1:6379> discard
OK
127.0.0.1:6379> get k4
(nil)

8.1 事务异常

8.1.1 编译型错误

(代码有问题!命令有错),事务所有命令不执行

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 k3
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5
(nil)

8.1.2 运行时错误(1/0)

事务队列中存在语法性错误,那么执行命令的时候,其他命令正常执行,错误命令抛出异常
这也反应Redis 事务没有原子性

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
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"

8.2 Redis 监控

8.2.2 监控 watch

悲观锁
无论做什么都加锁
乐观锁
不一定每次都会加锁,更新数据的时候判断一下,在此期间是否有人修改过这个数据
获取version
更新的时候比较version

8.2.3 测试

#正常执行
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
1) (integer) 80
2) (integer) 20
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> get out
"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

#线程1 未提交 exec 执行前,线程2进入
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000
OK

#线程1执行
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

九、Jedis

9.1 什么是jedis

官方推荐的 java操作redis的中间件

9.2 测试

9.2.1 导入依赖

<dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.6.0</version>
        </dependency>

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

9.2.2 Ping

package com.tst;

import redis.clients.jedis.Jedis;

public class TestPing {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        System.out.println(jedis.ping());
        
    }
}

在这里插入图片描述

9.2.3 Keys

package com.tst;

import redis.clients.jedis.Jedis;

import java.util.Set;

public class TestKey {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        System.out.println("清空数据:"+jedis.flushDB());
        System.out.println("判断某个键是否存在:"+jedis.exists("username"));
        System.out.println("新增<'username','kuangshen'>的键值对:"+jedis.set("username", "kuangshen"));
        System.out.println("新增<'password','password'>的键值对:"+jedis.set("password", "password"));
        System.out.print("系统中所有的键如下:");
        Set<String> keys = jedis.keys("*");
        System.out.println(keys);
        System.out.println("删除键password:"+jedis.del("password"));
        System.out.println("判断键password是否存在:"+jedis.exists("password"));
        System.out.println("查看键username所存储的值的类型:"+jedis.type("username"));
        System.out.println("随机返回key空间的一个:"+jedis.randomKey());
        System.out.println("重命名key:"+jedis.rename("username","name"));
        System.out.println("取出改后的name:"+jedis.get("name"));
        System.out.println("按索引查询:"+jedis.select(0));
        System.out.println("删除当前选择数据库中的所有key:"+jedis.flushDB());
        System.out.println("返回当前数据库中key的数目:"+jedis.dbSize());
        System.out.println("删除所有数据库中的所有key:"+jedis.flushAll());
    }
}

9.2.4 String

package com.tst;

import redis.clients.jedis.Jedis;

import java.util.concurrent.TimeUnit;

public class TestString {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        jedis.flushDB();
        System.out.println("===========增加数据===========");
        System.out.println(jedis.set("key1","value1"));
        System.out.println(jedis.set("key2","value2"));
        System.out.println(jedis.set("key3", "value3"));
        System.out.println("删除键key2:"+jedis.del("key2"));
        System.out.println("获取键key2:"+jedis.get("key2"));
        System.out.println("修改key1:"+jedis.set("key1", "value1Changed"));
        System.out.println("获取key1的值:"+jedis.get("key1"));
        System.out.println("在key3后面加入值:"+jedis.append("key3", "End"));
        System.out.println("key3的值:"+jedis.get("key3"));
        System.out.println("增加多个键值对:"+jedis.mset("key01","value01","key02","value02","key03","value03"));
        System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03"));
        System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03","key04"));
        System.out.println("删除多个键值对:"+jedis.del("key01","key02"));
        System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03"));

        jedis.flushDB();
        System.out.println("===========新增键值对防止覆盖原先值==============");
        System.out.println(jedis.setnx("key1", "value1"));
        System.out.println(jedis.setnx("key2", "value2"));
        System.out.println(jedis.setnx("key2", "value2-new"));
        System.out.println(jedis.get("key1"));
        System.out.println(jedis.get("key2"));

        System.out.println("===========新增键值对并设置有效时间=============");
        System.out.println(jedis.setex("key3", 2, "value3"));
        System.out.println(jedis.get("key3"));
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(jedis.get("key3"));

        System.out.println("===========获取原值,更新为新值==========");
        System.out.println(jedis.getSet("key2", "key2GetSet"));
        System.out.println(jedis.get("key2"));

        System.out.println("获得key2的值的字串:"+jedis.getrange("key2", 2, 4));
    }
}

9.2.5 List

package com.tst;

import redis.clients.jedis.Jedis;

public class TestList {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        System.out.println("===========添加一个list===========");
        jedis.lpush("collections", "ArrayList", "Vector", "Stack", "HashMap", "WeakHashMap", "LinkedHashMap");
        jedis.lpush("collections", "HashSet");
        jedis.lpush("collections", "TreeSet");
        jedis.lpush("collections", "TreeMap");
        System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));//-1代表倒数第一个元素,-2代表倒数第二个元素,end为-1表示查询全部
        System.out.println("collections区间0-3的元素:"+jedis.lrange("collections",0,3));
        System.out.println("===============================");
        // 删除列表指定的值 ,第二个参数为删除的个数(有重复时),后add进去的值先被删,类似于出栈
        System.out.println("删除指定元素个数:"+jedis.lrem("collections", 2, "HashMap"));
        System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
        System.out.println("删除下表0-3区间之外的元素:"+jedis.ltrim("collections", 0, 3));
        System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
        System.out.println("collections列表出栈(左端):"+jedis.lpop("collections"));
        System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
        System.out.println("collections添加元素,从列表右端,与lpush相对应:"+jedis.rpush("collections", "EnumMap"));
        System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
        System.out.println("collections列表出栈(右端):"+jedis.rpop("collections"));
        System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
        System.out.println("修改collections指定下标1的内容:"+jedis.lset("collections", 1, "LinkedArrayList"));
        System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
        System.out.println("===============================");
        System.out.println("collections的长度:"+jedis.llen("collections"));
        System.out.println("获取collections下标为2的元素:"+jedis.lindex("collections", 2));
        System.out.println("===============================");
        jedis.lpush("sortedList", "3","6","2","0","7","4");
        System.out.println("sortedList排序前:"+jedis.lrange("sortedList", 0, -1));
        System.out.println(jedis.sort("sortedList"));
        System.out.println("sortedList排序后:"+jedis.lrange("sortedList", 0, -1));
    }
}

9.2.6 Set

package com.tst;

import redis.clients.jedis.Jedis;

public class TestSet {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        System.out.println("============向集合中添加元素(不重复)============");
        System.out.println(jedis.sadd("eleSet", "e1","e2","e4","e3","e0","e8","e7","e5"));
        System.out.println(jedis.sadd("eleSet", "e6"));
        System.out.println(jedis.sadd("eleSet", "e6"));
        System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
        System.out.println("删除一个元素e0:"+jedis.srem("eleSet", "e0"));
        System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
        System.out.println("删除两个元素e7和e6:"+jedis.srem("eleSet", "e7","e6"));
        System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
        System.out.println("随机的移除集合中的一个元素:"+jedis.spop("eleSet"));
        System.out.println("随机的移除集合中的一个元素:"+jedis.spop("eleSet"));
        System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
        System.out.println("eleSet中包含元素的个数:"+jedis.scard("eleSet"));
        System.out.println("e3是否在eleSet中:"+jedis.sismember("eleSet", "e3"));
        System.out.println("e1是否在eleSet中:"+jedis.sismember("eleSet", "e1"));
        System.out.println("e1是否在eleSet中:"+jedis.sismember("eleSet", "e5"));
        System.out.println("=================================");
        System.out.println(jedis.sadd("eleSet1", "e1","e2","e4","e3","e0","e8","e7","e5"));
        System.out.println(jedis.sadd("eleSet2", "e1","e2","e4","e3","e0","e8"));
        System.out.println("将eleSet1中删除e1并存入eleSet3中:"+jedis.smove("eleSet1", "eleSet3", "e1"));//移到集合元素
        System.out.println("将eleSet1中删除e2并存入eleSet3中:"+jedis.smove("eleSet1", "eleSet3", "e2"));
        System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1"));
        System.out.println("eleSet3中的元素:"+jedis.smembers("eleSet3"));
        System.out.println("============集合运算=================");
        System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1"));
        System.out.println("eleSet2中的元素:"+jedis.smembers("eleSet2"));
        System.out.println("eleSet1和eleSet2的交集:"+jedis.sinter("eleSet1","eleSet2"));
        System.out.println("eleSet1和eleSet2的并集:"+jedis.sunion("eleSet1","eleSet2"));
        System.out.println("eleSet1和eleSet2的差集:"+jedis.sdiff("eleSet1","eleSet2"));//eleSet1中有,eleSet2中没有
        jedis.sinterstore("eleSet4","eleSet1","eleSet2");//求交集并将交集保存到dstkey的集合
        System.out.println("eleSet4中的元素:"+jedis.smembers("eleSet4"));
    }
}

9.2.7 Hash

package com.tst;

import redis.clients.jedis.Jedis;

import java.util.HashMap;
import java.util.Map;

public class TestHash {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        Map<String,String> map = new HashMap<String,String>();
        map.put("key1","value1");
        map.put("key2","value2");
        map.put("key3","value3");
        map.put("key4","value4");
        //添加名称为hash(key)的hash元素
        jedis.hmset("hash",map);
        //向名称为hash的hash中添加key为key5,value为value5元素
        jedis.hset("hash", "key5", "value5");
        System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));//return Map<String,String>
        System.out.println("散列hash的所有键为:"+jedis.hkeys("hash"));//return Set<String>
        System.out.println("散列hash的所有值为:"+jedis.hvals("hash"));//return List<String>
        System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6:"+jedis.hincrBy("hash", "key6", 6));
        System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));
        System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6:"+jedis.hincrBy("hash", "key6", 3));
        System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));
        System.out.println("删除一个或者多个键值对:"+jedis.hdel("hash", "key2"));
        System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));
        System.out.println("散列hash中键值对的个数:"+jedis.hlen("hash"));
        System.out.println("判断hash中是否存在key2:"+jedis.hexists("hash","key2"));
        System.out.println("判断hash中是否存在key3:"+jedis.hexists("hash","key3"));
        System.out.println("获取hash中的值:"+jedis.hmget("hash","key3"));
        System.out.println("获取hash中的值:"+jedis.hmget("hash","key3","key4"));
    }
}

9.2.8 Password

package com.tst;

import redis.clients.jedis.Jedis;

public class TestPassword {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        //验证密码,如果没有设置密码这段代码省略
//        jedis.auth("password");

        jedis.connect(); //连接
        jedis.disconnect(); //断开连接

        jedis.flushAll(); //清空所有的key
    }
}

9.2.9 事务

9.2.9.1 正常执行
package com.tst;

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

public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello", "world");
        jsonObject.put("name", "tst");
        jedis.flushDB();
        String result = jsonObject.toJSONString();
        Transaction multi = jedis.multi();
        try {
            multi.set("user1",result);
            multi.set("user2",result);
           
            multi.exec();
        } catch (Exception e){
            multi.discard();
            e.printStackTrace();
        }finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close();
        }
    }
}
9.2.9.2 运行时错误
package com.tst;

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

public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello", "world");
        jsonObject.put("name", "tst");
        String result = jsonObject.toJSONString();
        jedis.flushDB();
        Transaction multi = jedis.multi();
        try {
            multi.set("user1",result);
            multi.set("user2",result);
            int i = 1/0;
            multi.exec();
        } catch (Exception e){
            multi.discard();
            e.printStackTrace();
        }finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值