Redis复习
Redis
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]> set name hepeng
OK
127.0.0.1:6379[3]> DBSIZE #查看数据库大小
(integer) 1
127.0.0.1:6379[3]> get name #查看值
"hepeng"
127.0.0.1:6379[3]> keys * #查看所有key
1) "name"
127.0.0.1:6379[3]> select 0
OK
DBSIZE #查看数据库大小
127.0.0.1:6379> dbsize
(integer) 8
keys * #查看所有key
127.0.0.1:6379> keys *
1) "views"
2) "k13"
3) "v13"
4) "v11"
5) "name"
6) "list"
7) "key1"
8) "k11"
flushdb #清空当前库
127.0.0.1:6379[3]> flushdb #清空当前库
OK
127.0.0.1:6379[3]> keys *
(empty list or set)
flushall #清空全部数据库
127.0.0.1:6379>flushall #清空全部数据库
为什么redis的端口是6379?
作者是痴汉,用的明星名字,粉丝效应
Reddis是单线程
明白Redis是很快的,官方表示,Redis是基于内存操作,CPU并不是Redis的瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了。
Redis是C语言写的,官方的数据是每秒10万+的QPS,这个不比Memecache差!
Redis单线程为什么还这么快?
-
误区:高性能的服务器一定是多线程的
-
误区:多线程(CPU上下文会切换)一定比单线程效率高
核心:redis是将所有数据全部放在内存中的,所以说使用单线程操作效率就是最高的,多线程(CPU上下文切换消耗),对于内存系统来说,,如果没有上下文操作效率就是最高的,每次读写都是在一个CPU上的,在内存情况下,这个就是最佳方案。
五大数据类型
Redis 是一种开源(BSD 许可)、内存中数据结构存储,用作数据库、缓存和消息代理*。Redis 提供了诸如字符串、散列、列表、集合、带范围查询的排序集合、位图、超级日志、地理空间索引和流等数据结构。Redis 内置复制、Lua 脚本、LRU 驱逐、事务和不同级别的磁盘持久化,并通过 Redis Sentinel 和 Redis Cluster 自动分区提供高可用性。
Redis-Key
keys pattern
查询模式规则
* 匹配任意数量的任意符号
? 配合一个任意符号
[] 匹配一个指定符号
EXISTS 键 #获取key是否存在 键存在返回1,不存在返回0
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> exists name1
(integer) 0
127.0.0.1:6379>
move 键 1 从当前库中的name移动到数据库1中 ,1代表数据库
127.0.0.1:6379> move name 1
(integer) 1
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys
(error) ERR wrong number of arguments for 'keys' command
127.0.0.1:6379[1]> keys *
1) "name"
127.0.0.1:6379[1]>
expire key seconds 设置过期时间,10秒后过期
pexpire key millisecondes 设置毫秒值
expireat key timestamp 设置时间戳
pexpireat key millisecondes-timestamp
ttl key 查看当前key的剩余时间
pttl key
persist 切换key从时效性转换为永久性
127.0.0.1:6379[1]> keys *
1) "name"
127.0.0.1:6379[1]> expire name 10
(integer) 1
127.0.0.1:6379[1]> ttl name
(integer) 2
127.0.0.1:6379[1]> ttl name
(integer) -2
127.0.0.1:6379[1]> get name
(nil)
127.0.0.1:6379[1]>
127.0.0.1:6379> set aa 123
OK
127.0.0.1:6379> ttl aa #-1代表永久有效
(integer) -1
127.0.0.1:6379> ttl bb #-2代表目前不存在
(integer) -2
127.0.0.1:6379> setex cc 100 123 #设置cc存在100秒
OK
127.0.0.1:6379> ttl cc
(integer) 94
127.0.0.1:6379> ttl cc
(integer) 93
127.0.0.1:6379> set dd 123
OK
127.0.0.1:6379> ttl dd
(integer) -1
127.0.0.1:6379> expire dd 10 #将永久有效设置为存在10秒
(integer) 1
127.0.0.1:6379> ttl dd
(integer) 7
127.0.0.1:6379> ttl dd
(integer) 4
127.0.0.1:6379> ttl dd
(integer) 2
127.0.0.1:6379> ttl dd
(integer) -2
type 键 查看键的类型
127.0.0.1:6379> set name itheima
OK
127.0.0.1:6379> type name
string
127.0.0.1:6379> lpush list1 aa
(integer) 1
127.0.0.1:6379> type list1
list
del key #删除指定key
sort #排序
sort排序改变只是查询出来的顺序,并不能改变list初始顺序
sort list1 alpha asc
sort list1 alpha desc #逆序
127.0.0.1:6379> lpush list1 cc
(integer) 1
127.0.0.1:6379> lpush list1 bb
(integer) 2
127.0.0.1:6379> lpush list1 dd
(integer) 3
127.0.0.1:6379> lpush list1 aa
(integer) 4
127.0.0.1:6379> lrange list1 0 -1
1) "aa"
2) "dd"
3) "bb"
4) "cc"
127.0.0.1:6379> sort list1 alpha #默认只对数字排序,对字母排序加参数alpha
1) "aa"
2) "bb"
3) "cc"
4) "dd"
127.0.0.1:6379> lrange list1 0 -1
1) "aa"
2) "dd"
3) "bb"
4) "cc"
rename key newkey #改名
renamenx key newkey #存在才改,不存在就不改
127.0.0.1:6379> lpush list1 aa #添加数据
(integer) 1
127.0.0.1:6379> rename list1 list2 #改名
OK
127.0.0.1:6379> lrange list1 0 -1 #查看
(empty list or set)
127.0.0.1:6379> lrange list2 0 -1 #查看
1) "aa"
String
字符串类型
append 追加
127.0.0.1:6379> set key1 v1
OK
127.0.0.1:6379> get key1
"v1"
127.0.0.1:6379> append key1 "hello"
(integer) 7
127.0.0.1:6379> get key1
"v1hello"
127.0.0.1:6379>
strlen key1 查看key的长度
127.0.0.1:6379> get key1
"v1hello"
127.0.0.1:6379> append key1 ",hepeng"
(integer) 14
127.0.0.1:6379> strlen key1
(integer) 14
127.0.0.1:6379> get key1
"v1hello,hepeng"
127.0.0.1:6379>
getrange 截取字符串 range 字符串范围
127.0.0.1:6379> get key1
"helloworld"
127.0.0.1:6379> getrange key1 0 3 #是闭区间的
"hell"
127.0.0.1:6379> getrange key1 0 -1 #获取全部字符串,和get key 是一样的
"helloworld"
SETRANGE 替换
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 #设置key3的值为hello 30秒后过期
OK
127.0.0.1:6379> ttl key3
(integer) 18
127.0.0.1:6379> ttl key3
(integer) 15
127.0.0.1:6379> get key3
"hello"
127.0.0.1:6379> setnx mykey "redis" #如果mykey不存在就创建
(integer) 1
127.0.0.1:6379> get mykey
"redis"
127.0.0.1:6379> keys *
1) "key2"
2) "views"
3) "mykey"
4) "key1"
5) "list"
6) "v11"
7) "k11"
8) "k13"
9) "v13"
10) "name"
127.0.0.1:6379> setnx mykey "MongoDB" #如果存在就不创建
(integer) 0
127.0.0.1:6379> get mykey
"redis"
mset 批量设置值
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 k4 v4
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k1"
3) "k4"
4) "k2"
mget 批量获取值
127.0.0.1:6379> mget k1 k2 k3 k4
1) "v1"
2) "v2"
3) "v3"
4) "v4"
msetnx 是原子性操作 要么一起成功要么一起失败
127.0.0.1:6379> msetnx k4 v4 k5 v5
(integer) 0
127.0.0.1:6379> get k5
(nil)
设置user:1对象,用json来保存对象
127.0.0.1:6379> set user:1 {name:zhangsan,age:3}
OK
127.0.0.1:6379> get user:1
"{name:zhangsan,age:3}"
这里的key是一个巧妙的设计:user:{id}:{filed}
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 23
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "23"
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"
CAS 比较并交换
数据结构是相同的
List(有序,可重复)
所有的List命令都是以L开头的
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"
lpop rpop
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"
127.0.0.1:6379> lindex list 1 #通过下标获取值
"one"
127.0.0.1:6379> lindex list 0
"two"
lrem 移除list集合中指定个数的value Llen返回列表长度
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> llen list #返回列表长度
(integer) 3
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> lrange list 0 -1
1) "three"
2) "three"
3) "two"
127.0.0.1:6379> lrem list 2 three
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "two"
ltrim
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> rpush mylist "hello1"
(integer) 3
127.0.0.1:6379> rpush mylist "hello12"
(integer) 4
127.0.0.1:6379> rpush mylist "hello3"
(integer) 5
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "world"
3) "hello1"
4) "hello12"
5) "hello3"
127.0.0.1:6379> ltrim mylist 1 2 #通过下标截取指定长度,这个list已经被改变了,截断了值剩下截取的元素
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "world"
2) "hello1"
rpoplpush #移除列表的最后一个元素,将他移动到另一个新列表中
127.0.0.1:6379> lpush mylist "hello"
(integer) 1
127.0.0.1:6379> lpush mylist "hello1"
(integer) 2
127.0.0.1:6379> lpush mylist "hello2"
(integer) 3
127.0.0.1:6379> lpush mylist "hello3"
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "hello3"
2) "hello2"
3) "hello1"
4) "hello"
127.0.0.1:6379> rpoplpush mylist list #查看新列表中,确实存在该值
"hello"
127.0.0.1:6379> lrange list 0 -1
1) "hello"
lset 将列表中指定下标的值替换为另一个值,更新操作
127.0.0.1:6379> exists list #判断这个列表是否存在,如果不存在列表我们去更新就会报错
1) 0
127.0.0.1:6379> lrange list 0 -1
1) "hello"
127.0.0.1:6379> lpush list value
(integer) 2
127.0.0.1:6379> lrange list 0 0
1) "value"
127.0.0.1:6379> lset list 0 item #如果存在就会更新当前下标的值
OK
127.0.0.1:6379> lrange list 0 -1
1) "item"
2) "hello"
linsert 将某个具体的value插入到列中某个元素的前面或者后面
127.0.0.1:6379> lpush list "hello"
(integer) 1
127.0.0.1:6379> lpush list "hello1"
(integer) 2
127.0.0.1:6379> lpush list "hello2"
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "hello2"
2) "hello1"
3) "hello"
127.0.0.1:6379> linsert list before "hello2" "other"
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "other"
2) "hello2"
3) "hello1"
4) "hello"
127.0.0.1:6379> linsert list after "hello" "java"
(integer) 5
127.0.0.1:6379> lrange list 0 -1
1) "other"
2) "hello2"
3) "hello1"
4) "hello"
5) "java"
总结
Set(无序,不重复)
命令都是以S开头
ladd 添加元素 smembers list 查看集合
127.0.0.1:6379> sadd list "hello" #set集合中添加元素
(integer) 1
127.0.0.1:6379> sadd list "redis"
(integer) 1
127.0.0.1:6379> sadd list "loveredis"
(integer) 1
127.0.0.1:6379> smembers list
1) "hello"
2) "redis"
3) "loveredis"
127.0.0.1:6379> sismember list "hello" #判断某一个值是否在集合中,返回1表示存在,0为不存在
(integer) 1
127.0.0.1:6379> sismember list "java"
(integer) 0
127.0.0.1:6379> smembers list
1) "hello"
2) "redis"
3) "loveredis"
127.0.0.1:6379> scard list #获取当前列表元素个数
(integer) 3
127.0.0.1:6379> sismember list "java" 如果元素存在,则添加不了
(integer) 0
127.0.0.1:6379> srem list "hello" #移除set集合中的指定元素
(integer) 1
127.0.0.1:6379> smembers list
1) "redis"
2) "loveredis"
127.0.0.1:6379> srandmember list # 随机获取集合中的元素
"loveredis"
127.0.0.1:6379> srandmember list
"redis"
127.0.0.1:6379> srandmember list 2 #随机获取两个元素
1) "redis"
2) "loveredis"
127.0.0.1:6379> smembers list
1) "c++"
2) "redis"
3) "loveredis"
127.0.0.1:6379> spop list # 随机移除set集合中的元素
"loveredis"
127.0.0.1:6379> spop list
"c++"
127.0.0.1:6379> smembers list
1) "redis"
将一个指定的值移动到另外一个set集合中
127.0.0.1:6379> smembers myset
1) "hepeng"
2) "hello"
3) "redis"
4) "java"
127.0.0.1:6379> smembers myset2
1) "haha"
127.0.0.1:6379> smove myset myset2 "java"
(integer) 1
共同关注 (并集)
数字集合类:
差集 SDIFF
并集 SINTER
交集 SUNION
127.0.0.1:6379> smembers key1
1) "b"
2) "a"
3) "c"
127.0.0.1:6379> smembers key2
1) "e"
2) "d"
3) "c"
差集
127.0.0.1:6379> sdiff key1 key2
1) "a"
2) "b"
交集
127.0.0.1:6379> sinter key1 key2
1) "c"
并集
127.0.0.1:6379> sunion key1 key2
1) "e"
2) "c"
3) "b"
4) "a"
5) "d"
共同好友,共同爱好,二度好友,推荐好友!
Hash
-
存储需求:对一系列存储的数据进行编组,方便管理,典型应用存储对象信息
-
需要的存储结构:一个存储空间保存多个键值对数据
-
hash类型:底层使用哈希表结构实现数据存储
-
hash存储结构优化
- 如果field数量比较少,存储结构优化为类数组结构
- 如果field数量比较多,存储结构使用HashMap结构
基本操作
127.0.0.1:6379> hset user:123 name itcast #添加数据
(integer) 1
127.0.0.1:6379> hset user:123 age 12
(integer) 1
127.0.0.1:6379> hget user:123 name #获取数据
"itcast"
127.0.0.1:6379> hgetall user:123 #获取全部数据
1) "name"
2) "itcast"
3) "age"
4) "12"
127.0.0.1:6379> hdel user:123 age #删除一个数据
(integer) 1
127.0.0.1:6379> hgetall user:123
1) "name"
2) "itcast"
127.0.0.1:6379> hlen user:123 #查看数据长度
(integer) 1
127.0.0.1:6379> hmset user:123 a a1 b b1 c c1 #批量添加数据
OK
127.0.0.1:6379> hlen user:123
(integer) 4
127.0.0.1:6379> hgetall user:123
1) "name"
2) "itcast"
3) "a"
4) "a1"
5) "b"
6) "b1"
7) "c"
8) "c1"
扩展操作
127.0.0.1:6379> hmset h1 a 123 b 345 #批量添加数据
OK
127.0.0.1:6379> hincrby h1 a 100 #给数据值增加
(integer) 223
127.0.0.1:6379> hincrby h1 a -100 #给数据值减少
(integer) 123
127.0.0.1:6379> hkeys h1 #查看所有key
1) "a"
2) "b"
127.0.0.1:6379> hvals h1 #查看所有value
1) "123"
2) "345"
注意事项
案例
卖充值卡
127.0.0.1:6379> hmset id:001 c30 1000 c50 1000 c100 1000
OK
127.0.0.1:6379> hincrby id:001 c50 -2
(integer) 998
127.0.0.1:6379> hincrby id:001 c30 -5
(integer) 995
127.0.0.1:6379> hincrby id:001 c100 -1
(integer) 999
127.0.0.1:6379> hincrby id:001 c100 -1
(integer) 998
127.0.0.1:6379> hincrby id:001 c100 -1
(integer) 997
127.0.0.1:6379> hgetall id:001
1) "c30"
2) "995"
3) "c50"
4) "998"
5) "c100"
6) "997"
IDEA操作jedis
代码示例
1.创建maven工程,在pom文件中导入jedis的坐标
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
2.操作各种数据类型
/**
* @Author: hepeng
* @Date: 2021/10/31 12:18
*/
public class JedisTest {
public static void main(String[] args) {
//1.获取连接对象
Jedis jedis = new Jedis("127.0.0.1",6379);
//2.执行操作
//操作string类型
jedis.set("name","itheima");
String hello = jedis.get("hello");
System.out.println(hello);
//操作list
jedis.lpush("list1","a","b","c","d");
List<String> list = jedis.lrange("list1", 0, -1);
for (String s : list) {
System.out.println(s);
}
//操作set
jedis.sadd("set1","abc","abc","acd","def");
Long len = jedis.scard("set1");
System.out.println(len);
//3.关闭连接
jedis.close();
}
}
JedisUtils工具类制作
package com.cloudcore.util;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.ResourceBundle;
/**
* @Author: hepeng
* @Date: 2021/10/31 12:35
*/
public class JedisUtils {
private static int maxTotal;
private static int maxIdel;
private static String host;
private static int port;
private static JedisPoolConfig jpc;
private static JedisPool jp;
static {
ResourceBundle bundle = ResourceBundle.getBundle("redis");
maxTotal = Integer.parseInt(bundle.getString("redis.maxTotal"));
maxIdel = Integer.parseInt(bundle.getString("redis.maxIdel"));
host = bundle.getString("redis.host");
port = Integer.parseInt(bundle.getString("redis.port"));
//Jedis连接池配置
jpc = new JedisPoolConfig();
jpc.setMaxTotal(maxTotal);
jpc.setMaxIdle(maxIdel);
//连接池对象
jp = new JedisPool(jpc,host,port);
}
public static Jedis getJedis(){
return jp.getResource();
}
}
配置文件 redis.properties
redis.maxTotal=50
redis.maxIdel=10
redis.host=127.0.0.1
redis.port=6379
Zset
三种特殊的数据类型
geospatial
hyperloglog
bitmaps
持久化
什么是持久化
利用永久性存储介质将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持久化
持久化用于防止数据的以外丢失,确保数据安全性
持久化过程保存什么
- RDB:保存数据快照
- AOF:保存数据操作过程
RDB
RDB启动方式
save
- 手动执行一次保存操作
save
save指定相关配置
由于Redis是单线程任务序列
注意:save指令的执行会阻塞当前Redis服务器,直到当前RDB过程完成为止,有可能会造成长时间阻塞,线上环境不建议使用
bgsave
- 手动启动后后台保存操作,但不是立即执行
bgsave
bgsave指令相关配置
bgsave工作原理
注意:bgsave指令是针对save阻塞问题做的优化,Redis内部所有涉及到RDB操作都采用bgsave的方式,save命令可以放弃使用
save自动配置
内部走的还是bgsave指令
900秒内只要有一个key发生变化就会触发
300秒内只要有10个key发生变化就会触发
三种方式对比
RDB特殊启动方式
RDB优点:
RDB缺点
AOF
RDB存储的弊端
- 存储数据量较大,效率较低,基于快照思想,每次读写都是全部数据,当数据量比较大时,效率非常低
- 大数据量下的IO性能较低
- 基于fork创建子线程,会产生额外的内存消耗
- 宕机带来的数据丢失风险
解决思路
- 不写全数据,仅仅记录部分数据
- 降低区分数据是否改变的难度,改记录数据为记录操作过程
- 对所有的操作均进行记录,排除丢失数据风险
概念
启动AOF相关配置
AOF写数据三种策略
AOF重写
AOF重写规则
AOF重写方式