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单线程为什么还这么快?

  1. 误区:高性能的服务器一定是多线程的

  2. 误区:多线程(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特殊启动方式

image-20211031132037337

RDB优点:

RDB缺点

AOF

RDB存储的弊端

  • 存储数据量较大,效率较低,基于快照思想,每次读写都是全部数据,当数据量比较大时,效率非常低
  • 大数据量下的IO性能较低
  • 基于fork创建子线程,会产生额外的内存消耗
  • 宕机带来的数据丢失风险

解决思路

  • 不写全数据,仅仅记录部分数据
  • 降低区分数据是否改变的难度,改记录数据为记录操作过程
  • 对所有的操作均进行记录,排除丢失数据风险
概念

启动AOF相关配置

AOF写数据三种策略

AOF重写

AOF重写规则

AOF重写方式

RDB和AOF对比

posted @ 2021-10-25 22:33  正在努力的澎澎  阅读(56)  评论(0)    收藏  举报