当前位置:   article > 正文

狂神说Redis学习笔记基础篇(上)

狂神说redis

一、NoSQL概述

2020年之后,大数据时代
一般的数据库无法进行分析管理了!

1、什么是NoSQL

2.单机MySQL的年代

在这里插入图片描述
90年代,一个基本的网站访问量一般不会太大,单个数据库完全足够!
使用静态网页Html,服务器没有太大的压力。
整个网站的瓶颈
1、数据量太大、一个机器放不了
2、数据的索引(B + Tree),一个机器内存也放不下
3、访问量(读写混合),一个服务器承受不了

3.Memcached(缓存)+ MySQL + 垂直拆分(多写分离)

网站80%的情况都是在读、每次都有去查询数据库的话就十分的麻烦,所以希望减轻数据库的压力,可以使用缓存来保证效率
发展过程:优化数据结构和索引 - - - > 文件缓存(IO) - - - > Memcached(当时最热门的技术! )
在这里插入图片描述

4.分库分表 + 水平拆分 + MySQL集群

本质:数据(读、写)
早些年MyISAM:表锁,十分影响效率,高并发下就会出现严重的锁问题
早些年Innodb:行锁
慢慢的就开始分库分表来解决写的压力,MySQL推出了表分区,这个并没有多少公司使用MySQL的集群,满足了所有的需求
在这里插入图片描述

5.如今最近的年代

MySQL等关系数据库就不够用了,数据量多,变化很快!
MySQL有的使用它来存储比较大的文件,博客,图片!数据库表很大,效率就低了!如果有一种数据库来专门处理这种数据,MySQL的压力就会变得十分小(研究如何处理这些问题!)大数据的IO压力下,表几乎没办法更改。

6.目前一个基本的互联网架构

在这里插入图片描述

7.为什么要用NoSQL

用户的个人信息,社交网络,地理位置,用户自己产生的数据,用户日志等等就会爆发式增长!
这时候就要用NoSQL数据库了,NoSQL可以很多好的处理以上的情况!

2、什么是NoSQL

1.NoSQL

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

2.NoSQL特点

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

  2. 大数据量高性能(Redis一秒写8万次,可以读取11万次,NoSQL的缓存记录级,是一种细粒度的缓存,性能比较高)

  3. 数据类型是多样型的!(不需要事先设计数据库!随取随用!如果是数据库量十分大的表,很多人就无法设计了!)

  4. 传统的RDBMS和NoSQL

     传统的RDBMS
     - 结构化组织
     - SQL
     - 数据和关系都存在单独的表中 row col
     - 操作操作,数据定义语言
     - 严格的一致性
     - 基础的事务
     - ......
    
     NoSQL
     - 不仅仅是数据
     - 没有固定的查询语言
     - 键值对存储,列存储,文档存储,图形数据库(社交关系)
     - 最终一致性,
     - CAP定理和BASE(异地多活!)	  
     - 高性能,高可用,高可扩展
     - ......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

了解:3v + 3高

大数据时代的3V:主要是描述问题的

  1. 海量Volume
  2. 多样Variety
  3. 实时Velocity

大数据时代的3高:主要是对程序的要求

  1. 高并发
  2. 高可扩
  3. 高性能

真正在公司中的实践:NoSQL + RDBMS 一起使用才是最强的。

3.NoSQL的四大分类

KV键值对:

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

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

  • MongoDB(必须掌握)
    • MongoDB是一个基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档!
    • MongoDB是一个介于关系型数据库中和非关系型数据库中间的产品!MongoDB是非关系型数据库中功能最丰富,最像关系型数据库的!
  • ConthDB

列存储的数据库:

  • HBase
  • 分布式文件系统

图关系数据库:

  • 他不是存图形的,放的是关系,比如:朋友圈社交网络,广告推荐!
  • Neo4j,infoGrid;

四者对比:

分类Examples举例典型应用场景数据模型优点缺点
键值(key-value)Tokyo Cabinet/Tyrant,Redis,Voldemort,Oracle BDB内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等key指向Value的键值对,通常采用Hash table来实现查找速度快数据无结构化,通常只能被当作字符和二级制数据
列存储数据库Cassandra,HBase,Riak分布式的文件系统以列簇式存储,将同一列数据存在一起查找速度快,可扩展性强,更容易进行分布式扩展功能相对局限
文档型数据库CouchDB,MongoDBWeb应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解value的内容)key-value对应的键值对,value为结构化数据数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构查询性能不高,而且缺乏统一的查询语法
图形(Graph)数据库Neo4j,InfoGrid,Infinite gRAPH社交网络,推荐系统等,专注于构建关系图谱图结构利用图结构相关算法。比如最短路径寻址,N度关系查找等很多时候需要对整个图做计算才能d得出需要的信息,而且这种结构不太好做分布式集群方案

二、Redis入门

1、概述

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

在这里插入图片描述

redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

免费和开源,是最热门的NoSQL技术之一,也被人们称之为结构化数据库!

2、Redis能干嘛

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

3、Windows下运行Redis

  • 开启Redis,运行redis-server.exe服务即可
    默认端:6379
    在这里插入图片描述
  • 使用redis,redis-cli.exe客户端来连接redis
    在这里插入图片描述
    Windows下确实简单,但是Redis推荐我们使用Linux去开发使用!

4、Linux下运行Redis

1、下载安装包 redis-6.2.6.tar.gz
2、解压Redis的安装包!程序/opt tar -zxvf redis-6.2.6.tar.gz
在这里插入图片描述
3、进入解压后的文件 cd redis-6.2.6.tar.gz 可以看到Redis的配置文件
在这里插入图片描述
4、基本的环境安装

yum install -y gcc-c++

make

make install
  • 1
  • 2
  • 3
  • 4
  • 5

m
在这里插入图片描述
5、redis的默认安装路径 usr/local/bin
在这里插入图片描述

6、将redis配置文件复制到当前目录下
在这里插入图片描述
7、默认不是后台启动的,修改配置文件

vim redis.conf
i进行编辑 修改no为yes
按Esc退出编辑
按:输入wq保存并退出
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述
8、启动Redis服务

进入安装目录

redis-server kconfig/redis.conf
  • 1

在这里插入图片描述

9、使用 redis-cli进行连接测试

redis-cli -p 6379
  • 1

在这里插入图片描述
10、查看redis的进程是否开启
在这里插入图片描述
11、关闭redis服务 shutdown exit
在这里插入图片描述
12、再次查看进程是否存在
在这里插入图片描述

5、测试性能

redis-benchmark是一个压力测试工具!
官方自带的性能测试工具!
redis-benchmark命令参数

此表来自菜鸟教程

在这里插入图片描述

简单测试:

# 测试: 100个并发连接	100000请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
  • 1
  • 2

在这里插入图片描述

如何查看这些分析:

在这里插入图片描述

6、基础知识

redis默认有16个数据库
在这里插入图片描述
默认使用的是第0个
可以使用select 进行切换
在这里插入图片描述

127.0.0.1:6379[3]>  keys *	# 查看数据库所有的key
1) "name"
  • 1
  • 2

清除当前数据库: flushdb

清除全部数据库内容: flushdb

127.0.0.1:6379[3]>  flushdb
OK
127.0.0.1:6379[3]> keys *
(empty array) 
  • 1
  • 2
  • 3
  • 4

Redis是单线程的!
Redis是很快的,官方表示Redis是基于内存操作,CPU不是Reids的性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,即然可以使用单线程来实现,就使用单线程!所有就使用了单线程!
Redis是C语言写的,官方提供的数据位 10万 + QPS,这个不比同样使用key-value的Memcache差!

为什么Redis单线程还这么快
1、误区1:高性能的服务器一定是多线程的
2、误区2:多线程一定比单线程效率高
CPU、内存、硬盘的速度要有所了解
核心:redis是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文切换:耗时的操作),对于内存系统来说,如果没有上下文切换效率是最高的!多次读写都是在一个CPU上的,在内存情况下,这个就是最佳方案!

三、五大数据类型

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

1、Redis-Key

127.0.0.1:6379> set age 1	# set key
OK
127.0.0.1:6379> keys *		# 查看所有的key
1) "age"
2) "name"
127.0.0.1:6379> exists name	# 判断当前的key是否存在
(integer) 1
127.0.0.1:6379> move name 1	# 移除当前的key
(integer) 1
127.0.0.1:6379> expire name 10	# 设置key的过期时间单位是秒
(integer) 1
127.0.0.1:6379> ttl name		# 查看当前key的剩余时间
(integer) 4
127.0.0.1:6379> get name
(nil)	
127.0.0.1:6379> type age		# 查看当前key的一个类型
string


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

2、Strng(字符串)

127.0.0.1:6379> set key1 v1			# 设置值	
OK
127.0.0.1:6379> get key1			# 获得值
"v1"
127.0.0.1:6379> keys *				# 获得所有的key
1) "key1"
127.0.0.1:6379> EXISTS key1			# 判断某一个key是否存在
(integer) 1
127.0.0.1:6379> append key1 "hello"	# 追加字符串,如果当前key不存在就相当于set key
(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 ",kuang"	
(integer) 13
127.0.0.1:6379> get key1
"v1hello,kuang"
127.0.0.1:6379> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
127.0.0.1:6379> set views 0		# 初始浏览量为0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views		# 自增1 浏览量为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		# 自减1	浏览量-1
(integer) 1
127.0.0.1:6379> decr views
(integer) 0
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> INCRBY views 10	# 设置步长 指定增量
(integer) 10
127.0.0.1:6379> DECRBY views 5	# 
(integer) 5

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
# 字符串范围 range
127.0.0.1:6379> set key1 "hello,kuangshen"		# 设置key1的值
OK
127.0.0.1:6379> get key1
"hello,kuangshen"
127.0.0.1:6379> GETRANGE key1 0 3				# 截取字符串 [0,3]
"hell"
127.0.0.1:6379> GETRANGE key1 0 -1				# 获得全部的字符串和get key是一样的
"hello,kuangshen"
127.0.0.1:6379> 

# 替换
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"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
# setex (set with expire)		# 设置过期时间
# setnx (set if not  expire)	# 不存在设置(在分布式锁中会常常使用)
127.0.0.1:6379> setex key3 30 "hello" 	# 设置 key3的值为hello,30秒后过期
OK
127.0.0.1:6379> get key3
"hello"
127.0.0.1:6379> ttl key3
(integer) 5
127.0.0.1:6379> setnx mykey "redis"		# 如果mykey不存在,创建mykey
(integer) 1
127.0.0.1:6379> keys *
1) "key2"
2) "key1"
3) "mykey"
127.0.0.1:6379> ttl key3
(integer) -2
127.0.0.1:6379> setnx mykey :"mongodeb"	# 如果mykey存在,创建失败  
(integer) 0
127.0.0.1:6379> get mykey
"redis"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
# mset
# mget
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3	# 同时设置多个值
OK
127.0.0.1:6379> keys *
1) "k2"
2) "k1"
3) "k3"
127.0.0.1:6379> mget k1 k2 k3			# 同时获取多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4 		# msetnx是一个原子性的操作,要么一起成功要么一起失败
(integer) 0
127.0.0.1:6379> get key4
(nil)
127.0.0.1:6379> 

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
# 对象
set user:1	{name:zhangsan,age:3} 	# 设置一个user:1 对象值 为json字符串来保存一个对象!
# 这里的key是一个巧妙的设计:user:{id}:{filed},如此设计在Redis中是完全可以的
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"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
# 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 mongodb		# 如果存在值,获取原来值,并设置新的值
"redis"
127.0.0.1:6379> get db
"mongodb"


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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

  • 计数器
  • 统计多单位的数量 uid:9595959559:follow 0 incr
  • 粉丝数
  • 对象缓存存储

3、List(列表)

基本的数据类型,列表
在这里插入图片描述
在redis立马,可以把list当成,栈,队列,阻塞队列
所有的list命令都是用 l 开头的,Redis不区分大小写命令

# 插入 LPUSH  RPUSH
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	# 获取list中的值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379>  LRANGE list 0 1	# 通过区间获取具体的值
1) "three"
2) "two"
127.0.0.1:6379> Rpush list right 	# 将一个值或多个值,插入到列表的尾部(右)
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
# 删除 LPOP RPOP
127.0.0.1:6379> Lrange list 0 -1	
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> LPOP list 			# 移除列表的第一个元素
"three"
127.0.0.1:6379> RPOP list			# 移除列表的最后一个元素
"right"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
# Lindex
127.0.0.1:6379> Lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> lindex list 1		# 通过下标获得list中的某一个值
"one"
127.0.0.1:6379> lindex list 0
"two"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
# 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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
# 移除指定的值  Lrem
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> lrem list 1 one			# 移除list集合中指定个数的value,精确匹配
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "two"
127.0.0.1:6379> lrem list 1 three
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
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
1) "two"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
# trim 修剪	
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
1) "hello1"
2) "hello2"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
rpoplpush	# 移除列表最后一个元素,将它移动到新的列表中
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> rpoplpush mylist myotherlist	# 移除列表的最后一个元素,将他移动到新的列表中
"hello2"
127.0.0.1:6379> lrange mylist 0 -1		# 查看原来的列表
1) "hello"
2) "hello1"
127.0.0.1:6379> lrange myotherlist 0 -1		# 查看目标列表中确实存在该值
1) "hello2"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
lset 将列表指定下标的值 替换为另外一个值 ,更新操作
127.0.0.1:6379> EXISTS list			# 判断这个列表是否存在
(integer) 0
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
1) "value1"
127.0.0.1:6379> lset list 0 item	# 如果存在列表,更新当前下标的值
OK
127.0.0.1:6379> lrange list 0 0
1) "item"
127.0.0.1:6379> lset list 1 other	# 如果不存在,则会报错
(error) ERR index out of range

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
linsert		# 将某一个具体的值插入到列表中某个元素的前面或者后面
127.0.0.1:6379> Rpush mylist "heelo"
(integer) 1
127.0.0.1:6379> Rpush mylist "world"
(integer) 2
127.0.0.1:6379> linsert mylist before "world" "other"
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "heelo"
2) "other"
3) "world"
127.0.0.1:6379> linsert mylist after "world" new 
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "heelo"
2) "other"
3) "world"
4) "new"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

小结

  • 实际上是一个链表,before Node after ,left right都可以插入值
  • 如果key不存在,创建新的链表
  • 如果key存在,新增内容
  • 如果移除了所有值,空链表,也代表不存在!
  • 在两边插入或者改动值,效率最高!中间元素,相对来说效率低一点
    消息排队!消息队列(Lpush Rpop),栈(Lpush Lpop)

4、set(集合)

set中的值是不能重复的

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> sadd myset java
(integer) 1
127.0.0.1:6379> smembers myset			# 查看指定set中的所有值
1) "hello"
2) "world"
3) "java"
127.0.0.1:6379> sismember myset hello	# 判断某一个值是不是在set集合中
(integer) 1
127.0.0.1:6379> sismember myset ku
(integer) 0

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
127.0.0.1:6379> scard myset		# 获取set集合中的内容元素个数
(integer) 4
  • 1
  • 2
127.0.0.1:6379> srem myset hello	# 移除set集合中的指定元素
(integer) 1
127.0.0.1:6379> scard myset
(integer) 3
127.0.0.1:6379> smembers myset
1) "hello2"
2) "world"
3) "java"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
set 无序不重复集合。抽随机
127.0.0.1:6379> smembers myset
1) "hello2"
2) "world"
3) "java"
127.0.0.1:6379> srandmember myset 	# 随机抽选出一个元素
"java"
127.0.0.1:6379> srandmember myset
"world"
127.0.0.1:6379> srandmember myset
"hello2"
127.0.0.1:6379> srandmember myset 2	# 随机抽选出指定个数的元素
1) "hello2"
2) "java"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
删除指定的key,随机删除key
127.0.0.1:6379> smembers myset 
1) "hello2"
2) "world"
3) "java"
127.0.0.1:6379>  spop myset 		# 随机删除一些set集合中的元素
"world"
127.0.0.1:6379> spop myset
"java"
127.0.0.1:6379> smembers myset
1) "hello2"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
# 将一个指定的值移动到另外一个set集合中 
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 java
(integer) 1
127.0.0.1:6379> sadd myset2 set2
(integer) 1
127.0.0.1:6379> smove myset myset2 java	# 将一个指定的值移动到另外一个set集合中 
(integer) 1
127.0.0.1:6379> smembers myset2
1) "set2"
2) "java"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
共同关注(并集)
数字集合类:
 - 差集	sdiff
 - 交集	sinter
 - 并集 sunion
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 key1 d
(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> sdiff key1 key2			# 差集
1) "d"
2) "a"
3) "b"
127.0.0.1:6379> sinter key1 key2		# 交集 共同好友就这样实现的
1) "c"
127.0.0.1:6379> sunion key1 key2		# 并集
1) "a"
2) "b"
3) "c"
4) "e"
5) "d"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

微博,A用户将所有的关注的人放在一个set集合中,将它的粉丝也放到一个集合中!
共同关注,共同爱好,二度好友

5、Hash(哈希)

Map集合,key- Map (< key-value >) 本质和string类型没有太大区别,还是一个简单的key-value

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
hlen

127.0.0.1:6379> hmset myhash filed1 hello filed2 world 
OK
127.0.0.1:6379> hgetall myhash	
1) "filed2"
2) "world"
3) "filed1"
4) "hello"
127.0.0.1:6379> hlen myhash			# 获取hash表的字段数量
(integer) 2

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
127.0.0.1:6379> hexists myhash filed1		# 判断hash中指定字段是否存在
(integer) 1
127.0.0.1:6379> hexists myhash filed3
(integer) 0
  • 1
  • 2
  • 3
  • 4
127.0.0.1:6379>  hkeys myhash		# 只获取所有filed
1) "filed2"
2) "filed1"
127.0.0.1:6379> hvals myhash		# 只获取所有value
1) "world"
2) "hello"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
incr decr
127.0.0.1:6379> hset myhash filed3 5		# 指定增量
(integer) 1
127.0.0.1:6379> hincrby myhash filed3 1
(integer) 6
127.0.0.1:6379> hincrby myhash filed3 -1
(integer) 5
127.0.0.1:6379> hsetnx myhash filed4 hello	# 如果不存在则可以设置
(integer) 1
127.0.0.1:6379> hsetnx myhash filed4 world # 如果存在则不可以设置
(integer) 0

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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

6、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
1) "one"
2) "two"
3) "three"


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
排序怎么实现

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 qigai
(integer) 1
# zrangebyscore key min max 
127.0.0.1:6379> zrangebyscore salary -inf +inf 	# 显示全部的用户 从小到大排序
1) "qigai"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> zrevrange salary 0 -1		# 从大到小进行排序
1) "zhangsan"
2) "xiaohong"
3) "qigai"
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores 	#显示全部的用户并且附带成绩
1) "qigai"
2) "500"
3) "xiaohong"
4) "2500"
5) "zhangsan"
6) "5000"
127.0.0.1:6379> zrangebyscore salary -inf 2500 withscores	#显示工资小于2500员工的升序排列
1) "qigai"
2) "500"
3) "xiaohong"
4) "2500"


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
zrem  移除元素

127.0.0.1:6379> zrange salary 0 -1
1) "qigai"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> zrem salary xiaohong	# 移除有序集合中的指定元素
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "qigai"
2) "zhangsan"
127.0.0.1:6379> zcard salary 			# 获取有序集合中的个数
(integer) 2

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
127.0.0.1:6379> zadd myset 1 hello
(integer) 1
127.0.0.1:6379> zadd myset 2 world
(integer) 1
127.0.0.1:6379> zadd myset 3 java
(integer) 1
127.0.0.1:6379> zcount myset 1 3 		# 获取指定区间的成员数量
(integer) 3
127.0.0.1:63
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

四、三种特殊数据类型

1、geospatial地理位置

朋友的定位,附近的人,打车,距离计算
Redis 的Geo可以推算地理位置的信息,两地之间的距离,方圆几里的人
官方文档

  1. getadd
# getadd 添加地理位置
# 规则:两极无法直接添加,我们一般会下载城市数据,直接通过java程序来一次性导入
#有效的经度从-180度到180度
#有效的纬度从-85.05112878度到85.05112878度。
#当坐标位置超出上述指定范围时,该命令将会返回一个错误。
#127.0.0.1:6379> geoadd china:city 39.90 116.40 beijing
(error) ERR invalid longitude,latitude pair 39.900000,116.400000

# 参数 key 值(纬度、经度、名称)
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.5 29.6 chongqing
(integer) 1
127.0.0.1:6379> geoadd china:city 114.05 22.52 shenzhen
(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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  1. geopos
    获得当前定位:一定是一个坐标值
127.0.0.1:6379> geopos china:city beijing	# 获取指定程序的经度和纬度
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
127.0.0.1:6379> geopos china:city beijing shanghai chongqing
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
2) 1) "121.47000163793563843"
   2) "31.22999903975783553"
3) 1) "106.49999767541885376"
   2) "29.60000097326405211"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  1. Geodist
    两人之间的距离!
    单位如下:
  • m 表示单位为米。
  • km表示单位为千米。
  • mi表示单位为英里。
  • ft表示单位为英尺。
127.0.0.1:6379> geodist china:city beijing shanghai km	# 查看上海到北京的直线距离
"1067.3788"
127.0.0.1:6379> geodist china:city beijing chongqing km	# 查看北京到重庆的直线距离
"1457.7307"

  • 1
  • 2
  • 3
  • 4
  • 5
  1. Georadius以给定的纬度为中心,找出某一半径内的元素
    获得所有附近的人的地址,定位,通过半径来查询
    所有数据应该都录入:china:city,才会让结果更加请求
127.0.0.1:6379> georadius china:city 110 30 1000 km		# 以 110 	30这个 经纬度为中心,寻找方圆1000km内的城市
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
127.0.0.1:6379> georadius china:city 110 30 500 km
1) "chongqing"
2) "xian"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist		# 显示到中心距离的位置
1) 1) "chongqing"
   2) "340.7173"
2) 1) "xian"
   2) "483.8340"
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord	# 显示他人的定位信息
1) 1) "chongqing"
   2) 1) "106.49999767541885376"
      2) "29.60000097326405211"
2) 1) "xian"
   2) 1) "108.96000176668167114"
      2) "34.25999964418929977"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 1		# 筛选出指定的结果
1) 1) "chongqing"
   2) "340.7173"
   3) 1) "106.49999767541885376"
      2) "29.60000097326405211"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 2
1) 1) "chongqing"
   2) "340.7173"
   3) 1) "106.49999767541885376"
      2) "29.60000097326405211"
2) 1) "xian"
   2) "483.8340"
   3) 1) "108.96000176668167114"
      2) "34.25999964418929977"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 3
1) 1) "chongqing"
   2) "340.7173"
   3) 1) "106.49999767541885376"
      2) "29.60000097326405211"
2) 1) "xian"
   2) "483.8340"
   3) 1) "108.96000176668167114"
      2) "34.25999964418929977"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  1. GEORADIUSBYMEMBER
# 找出位于指定元素周围的其他元素
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km 
1) "beijing"
2) "xian"
127.0.0.1:6379> georadiusbymember china:city shanghai 400 km
1) "hangzhou"
2) "shanghai"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  1. GEOHASH命令 - 返回一个或多个位置元素的Geohash表示
    该命令将返回11个字符的GeoHash字符串
# 将二维的经纬度转换为一纬的字符串,如果两个字符串越接近,那么距离越近
127.0.0.1:6379> geohash china:city beijing chongqing
1) "wx4fbxxfke0"
2) "wm78rkwbtb0"

  • 1
  • 2
  • 3
  • 4
  • 5
  1. GEO底层的实现原理其实就是Zset,我们可以使用Zset命令来操作geo
127.0.0.1:6379> zrange china:city 0 -1	# 查看地图中全部的元素
1) "chongqing"	
2) "xian"
3) "shenzhen"
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) "shenzhen"
4) "hangzhou"
5) "shanghai"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2、Hyperloglog

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

  2. 简介
    Redis2.8.9版本就更新了Hyperloglog数据结构
    Reids Hyperloglog基数统计的算法
    优点:占用的内存是固定的,2^64不同的元素的基数,只需要费12kb内存,如果要从内存角度来比较的话Hyoerloglog首选
    网页的UV(一个人访问一个网站多次但是还是算作一个人)
    传统的方式,set保存用户的id,然后就可以统计set中的元素数量作为标准判断
    这个方式如果保存大量的用户id就会比较麻烦,我们的目的是为了计数,而不是为了保存用户id
    0.81%错误率!统计UV任务,可以忽略不计的

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  		# 统计 mykey元素的基数数量
(integer) 10
127.0.0.1:6379> pfadd mykey2 i j z c v b n m # 创建第二组元素
(integer) 1
127.0.0.1:6379> pfcount mykey2 
(integer) 8
127.0.0.1:6379> pfmerge mykey3 mykey mykey2		# 合并两组mykey mykey2 => mykey3 并集
OK
127.0.0.1:6379> pfcount mykey3 	# 查看并集的数量!
(integer) 14

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

如果允许容错,那么一定可以使用Hyperloglog!
如果不允许容错,就使用set或者自己的数据类型即可!

3、 Bitmaps

位存储
统计用户信息,活跃,不活跃!登录、未登录!打卡,365打卡!两个状态的,都可以使用Bitmaps!Bitmaps位图,数据结构! 都是操作二进制位来进行记录,就只有0和1两个状态

使用bitmap来记录周一到周日的打卡
周一:1 周二:0 周三: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) 1
127.0.0.1:6379> setbit sign 6 0
(integer) 0

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

查看某一天是否打卡

127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 6
(integer) 0

  • 1
  • 2
  • 3
  • 4
  • 5

统计操作,统计打开的天数!

127.0.0.1:6379> bitcount sign 	# 统计这周的打卡记录,就可以看到是否右全勤
(integer) 3
  • 1
  • 2

五、事务

Redis事务本质:一组命令的集合!一个事务的所有命令都会被序列化,在事务执行的过程中,会按照顺序执行!
一次性、顺序性、排他性,来执行一系列的命令

------- 队列 set set set 执行 -----------
  • 1

Redis事务没有隔离级别的概念
所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行! Exec
Redis单条命令保证原子性的,但是事务不保证原子性!
redis事务:

  • 开启事务(Multi)
  • 命令入队( —=- )
  • 执行事务(Exec)

1、 正常执行事务

127.0.0.1:6379> multi				# 开启事务
OK
# 命令入队
127.0.0.1:6379(TX)> set k1 k1
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 k3
QUEUED
127.0.0.1:6379(TX)> exec		# 执行事务
1) OK
2) OK
3) "v2"
4) OK
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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 k2 v2
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)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3、编译型异常(代码有问题!命令有错!)

事务中所有的命令都不会被执行

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)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> getset k3 		# 错误的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> set k5 v5
QUEUED
127.0.0.1:6379(TX)> exec			# 执行事务报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5				# 所有的命令都不会被执行
(nil)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

4、运行时异常(1/0)

如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行的。错误命令抛出异常!

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 "v1"
QUEUED
127.0.0.1:6379(TX)> incr k1			# 会执行的时候失败
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) OK
2) (error) ERR value is not an integer or out of range	# 虽然第二条命令报错了,但是依旧执行成功了
3) OK
4) OK
5) "v3"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

5、监控,Watch

悲观锁:

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

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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

127.0.0.1:6379> watch money 			# 监视 money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby moey 10
QUEUED

127.0.0.1:6379(TX)> incrby out 10
QUEUED
127.0.0.1:6379(TX)> exec			# 执行之前另外的一个线程修改了值,这个时候,就会导致事务执行失败
(nil)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

如果修改失败,获取最新值就好

127.0.0.1:6379> unwatch				# 如果发现事务执行失败,就先解锁
OK
127.0.0.1:6379> watch money			# 获取最新的值,再次监视,select version
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 money 10
QUEUED
127.0.0.1:6379(TX)> exec	# 比对监视的值是否发生了变化,如果没有变化,那么可以执行成功,如果变化了就执行失败
1) (integer) 980
2) (integer) 1000

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

六、Jedis

使用Jedis来操作Redis

1、什么是Redis

Jedis是Redis官方推荐的java连接开发工具,使用java操作Redis中间件!如果要使用Java操作Redis,那么一定要对Redis十分熟悉。

  1. 导入对应的依赖
    <dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>4.2.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.1.37</version>
        </dependency>
    </dependencies>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  1. 编码测试
  • 连接数据库
  • 操作命令
  • 断开连接
import redis.clients.jedis.Jedis;

public class TestPing {
    public static void main(String[] args) {
        //1、new Jedis  对象即可
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        //jedis所有的命令就是我们之前学习的所有指令!
        System.out.println(jedis.ping());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

输出:PONG

2、常用的API

1、对key操作的命令

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", "kuang"));
        System.out.println("新增<'password','password'>的键值对:" + jedis.set("password", "password"));
        System.out.println("系统中所有的键如下:");
        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的数目L:" + jedis.dbSize());
        System.out.println("删除所有数据库中的key:" + jedis.flushAll());
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

2、对String操作的命令


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", "value1Change"));
        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.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) {
            throw new RuntimeException(e);
        }
        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));
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

3、对List操作的命令

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", "LinkedList");
        jedis.lpush("collections", "HashSet");
        jedis.lpush("collections", "TreeSet");
        jedis.lpush("collections", "TreeMap");
        System.out.println("collections的内容" + jedis.lrange("collections", 0, -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", "HashMap"));
        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("修改collection指定下标为1的内容:" + jedis.lset("collections", 1, "Linkedlist"));
        System.out.println("collections的内容:" + jedis.lrange("collections", 0, -1));
        System.out.println("================================");
        System.out.println("collections的长度:" + jedis.llen("coolections"));
        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 ));
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

4、对Set操作的命令

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", "e3", "e4", "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("e5是否在eleSet中" + jedis.sismember("eleSet", "e5"));
        System.out.println("===================================");
        System.out.println(jedis.sadd("eleSet1", "e1", "e2", "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中删除e1并存入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"));
        jedis.sinterstore("eleSet4", "eleSet1", "eleSet2"); //求交集并将交集保存到dstkey的集合
        System.out.println("eleSet4的元素:" + jedis.smembers("eleSet4")6;

    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

5、对Hash操作的命令

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<>();
        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"));
        System.out.println("散列hash的所有键为:" + jedis.hkeys("hash"));
        System.out.println("散列hash的所有值为:" + jedis.hvals("hash"));
        System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6:" + jedis.hsetnx("hash","key6","value6"));
        System.out.println("散列hash的所有键值对为:" + jedis.hgetAll("hash"));
        System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6:" + jedis.hsetnx("hash","key6","value60"));
        System.out.println("散列hash的所有键值对为:" + jedis.hgetAll("hash"));
        System.out.println("删除一个或多个键值对" + jedis.hdel("hash","key2","key3")) ;
        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中是否存在key4:" + jedis.hexists("hash","key4"));
        System.out.println("获取hash中的值:" + jedis.hmget("hash","key1"));
        System.out.println("获取hash中的值:" + jedis.hmget("hash","key1","key2","key4"));
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

6、事务

import com.alibaba.fastjson.JSONObject;
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);
        jedis.flushDB();

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello", "world");
        jsonObject.put("name", "xiaohong");

        //开启事务
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();

       
        try {
            multi.set("user1", result);
            multi.set("user2", result);
            int i = 1 / 0;    //代码抛出异常事务执行失败
            multi.exec();       //执行事务
        } catch (Exception e) {
            multi.discard();    //放弃事务
        } finally {
            System.out.println(jedis.mget("user1", "user2"));
            jedis.close();  //关闭连接
        }

    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/682796
推荐阅读
相关标签
  

闽ICP备14008679号