redis入门

Redis入门

redis是什么?

redis (Remote Dictionary Server)远程服务字典

redis可以做什么?

  1. 内存存储,持久化,内存是断电即失去,持久化(rdb,aof)
  2. 效率高,用于高速缓存
  3. 发布定阅
  4. 地图信息分析
  5. 计数器,计数器

特性

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

学习准备

  1. 官网https://redis.io/
  2. 中文网
  3. 基于Linux学习

性能测试工具

 ./redis-benchmark -h localhost -p 6379 -c 100 -n 10000

基础知识

  1. 一共有16个数据库

  2. 默认使用的是第0个

  3. 可以通过select进行切换数据库

    127.0.0.1:6379> select 1
    OK
    127.0.0.1:6379[1]> set name xxx
    OK
    127.0.0.1:6379[1]> dbsize
    (integer) 1
    
  4. 查看数据库所有的key

    keys *
    
  5. 清除数据

    # 删除当前数据库的数据
    flushdb
    # 删除redis中的全部数据
    flushall
    

redisd 5是单线程的

redis是内存操作,cpu不是redis的性能瓶颈,瓶颈是内存和网络带宽。

为什么redis是单线程的还会这么快?

首先多线程的程序cpu会上下文切换,上下文切换可能会消耗大量的时间。

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

五大数据类型

数据库,缓存,消息中间件MQ

Redis-key

keys * # 查看所有的key
set key value # 设置key
exists key # 是否存在key
move key 1# 移动key 的位置
del key # 删除key
expire key time (秒) #设置过期时间
ttl key # 查看当前key 的剩余时间
type key # 查看数据类型

String(字符串)

set key value # 设置值
get key # 获取key
keys * # 查看key
exists key  # key十分存在
strlen key  # 字符串长度
append key value # 追加字符串
incr key # 自增 i++
decr key # 自减 i--
incrby key value # 自增步长

# 字符串的范围 getrange
getrange key start end # 截取字符串
getrange key 0 -1

# 字符串替换
setrange key offset value

#setex(set with expire)
#setnx(如果这个key不存在就设置这个值)在分布式锁中经常使用

#mset 批量设置 原子性的操作
#mget 批量获取多个值

#对象
set user:1 {name:zhangsan,age:3}

#getset 先执行get再执行set 可以用于一些更新的操作

常用的场景:

  • 计数器

  • 统计多单位的数量

List

基本的数据类型,列表

我们可以把list玩成 队 栈

所有的list命令都是以L开头的

lpush key value # 将一个值或者多个值插入列表的头部
#插入值
LPUSH key
RPUSH key

LRANGE key start end 
LRANGE key 0 -1 #查询所有的值
#移除值
LPOP 
RPOP
#通过下标获取list 中的某一个值
lindex key value 
#获取list 的长度
Llen key
#移除指定的值!
Lrem key count value

# trim修建 list截断
ltrim key start end # 通过下标截取指定的长度,这个list已经被改变了,截取了只剩下的元素了

#移除列表的最后一个元素,并将元素push到新的列表中
rpoplpush 

#list  的 set 方法更近index来更新值 如果list不存在则set失败。相当于是一个更新的操作
lset key index value

#Linsert 将具体的value插入到列表那期中的某个元素的前面或者后面
Linsert

消息队列,队,栈

Set(值不能重复)

sadd key value  # 添加元素
smembers key # 
sismember key value
scard key # 获取set集合中的内容元素个数
srem key value # 移除set中的指定元素

set 是无需集合
srandmember key # 随机抽选出一个元素
srandmember key number # 随机抽取指定个数的元素

# 随机删除指定的key
spop key #随机删除key

# 将一个指定的key移动到另外一个集合中
smove 原来的set 指定的set value

# 数字集合
- 差集    sdiff key1 key2   不同的
- 交集    sinter  key1 key2 相同的
- 并集    sunion key1 key2

微博 的共同关注

Hash(哈希)

Map集合, key-Map

hset key field value # set 一个具体的key-value
hget key field # 获取一个字段
hmset key field value field value #同时set多个key value
hgetall key # 获取全部的数据
hdel key field  #删除hash指定的key字段!对应的value值也就消失了


hlen key #获取hash表的字段长度
hexists key field # 判断hash中的指定字段是否存在

hkeys key # 获取所有的field
hvalus key # 获取所有的value

hincrby key field value #自增
hsetnx myhash field value #不存在就可以设置

hash更加适合于对象的存储

Zset(有序集合)

在set 的基础上增加一个值 score

zadd key score value
zrangebyscore key 

zset 排行榜

重要的事情,带权重进行判断

三种特殊数据类型

geospatial 地理空间

longitude 经度

latitude 维度

GEOADD

GEOADD key longitude latitude member

GEOPOS

GEOPOS key member #查询经度纬度

GEODIST

GEODIST key member1 member2 [unit]

指定单位的参数 unit 必须是以下单位的其中一个:

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。

GEORADIUS 给指定的

radius 半径

GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]

以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。

范围可以使用以下其中一个单位:

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。

geo 的底层其实就是Zset!我们可以使用Zset 的命令来操作geo!

Hyperloglog

什么是基数?

基数是不重复的元素

简介

网站UV(一个人的访问只能算一次)

使用set保存用户的id

会存储大量的用户的id,会十分麻烦

优点:占用的内存空间十分小的 2^64 只需要12KB

测试使用

PFadd key value  # 添加内容

PFcount key # 统计数量

PFmerge key3 key1 key2 # 合并求交集

允许容错可以使用Hyperloglog

不允许的话使用set的数据类型

Bitmap

位存储

统计用户信息

比如打卡,用户是否登录。只有两个状态的都可以使用Bitmap

Bitmaps位图,数据结构!

getbit key value
setbit key value
bitcount key

事务

要么同时成功要么同时失败。

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

Redis的事务本质:一组命令的集合!

事务中的所有命令都会被序列化

一次性,顺序性,排他性

redis 事务没有隔离级别的概念

Exec

redis的事务:

  • 开启事务:multi
  • 命令入队:。。。
  • 执行事务:exec

锁:redis可以实现乐观锁

正常执行事务

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> exec
1) OK
2) "v1"

放弃事务

discard # 取消事务,事务队列的命令都不会执行

编译型异常,事务中的所有的命令都不会执行

运行时异常,事务队列中存在一些语法型错误,其他命令是可以正常执行的,错误命令会抛出异常

监视!(watch,unwant)乐观锁

悲观锁:

  • 很悲观,什么时候都会出现问题,无论什么时候都会出现问题。

乐观锁:

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

使用watch可以做redis的乐观锁进行操作

使用unwant进行解锁

  1. 如果发现事务执行失败,就解锁
  2. 获取最新的值,再次监控
  3. 对比监视的值是否发生改变,如果没有发生改变,就可以执行事务,发生改变则执行失败

jedis 使用

1.导入依赖

 
        
        
            redis.clients
            jedis
            3.3.0
        
        
        
            com.alibaba
            fastjson
            1.2.68
        
    

2.测试代码(测试和本地的redis链接)

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

3.事务测试

  • 连接数据库
  • 操作数据
  • 断开连接
public class test02 {
    public static void main(String[] args) {
        //连接数据库
        Jedis jedis = new Jedis("127.0.0.1",6379);
        jedis.flushDB();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("name","summer");
        jsonObject.put("age","20");
        //开启事务 操作数据
        Transaction multi = jedis.multi();
        String res = jsonObject.toString();
        try {
            multi.set("user:1",res);
            multi.set("user:2",res);
            multi.exec();
        }catch (Exception e){
            multi.discard();
            e.printStackTrace();
        }finally {
            System.out.println(jedis.get("user:1"));
            System.out.println(jedis.get("user:2"));
            //关闭连接
            jedis.close();
        }
    }
}

springboot整合

springboot操作数据:都是spring-data-XXXX

springboot2.X之后,原来的jedis被换成lettuce了。

jedis:采用

lettuce:采用netty,实例可以在多个线程池中进行共享,不存在线程不安全的问题。

1.导入依赖

 
            org.springframework.boot
            spring-boot-starter-data-redis-reactive
 

2.配置连接

# 配置redis的连接
spring.redis.host=127.0.0.1
spring.redis.port=6379

3.测试!

@SpringBootTest
class Redis02SpringbootApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
        redisTemplate.opsForValue().set("name","summer");
        System.out.println(redisTemplate.opsForValue().get("name"));
    }
}

需要序列化

在企业中,我们所有的pojo都要进行序列化!!!

java程序中直接给redis传输对象会报错,需要序列化。

真实开发中一般都是拿json来传递对象的。

解决方法让对象进行序列化 ==implements Serializable==

public class User implements Serializable {
    private String name ;
    private Integer age;
}

默认的redis序列化是jdk 的序列化,我们需要使用json进行序列化。

自定义RedisTemplate

package com.summer.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    // 自己定义了一个 RedisTemplate
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
        // 我们为了自己开发方便,一般直接使用 
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(factory);
        // Json序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // String 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

RedisUtils

package com.summer.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * RedisTemplate 工具类
 *
 * @author summer
 * @version 2020/06/24
 */
@Component
public class RedisUtils {

    private RedisTemplate redisTemplate;

    @Autowired
    public void setRedisTemplate(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
//- - - - - - - - - - - - - - - - - - - - -  公共方法 - - - - - - - - - - - - - - - - - - - -

    /**
     * 给一个指定的 key 值附加过期时间
     *
     * @param key
     * @param time
     * @return
     */
    public boolean expire(String key, long time) {
        return redisTemplate.expire(key, time, TimeUnit.SECONDS);
    }

    /**
     * 根据key 获取过期时间
     *
     * @param key
     * @return
     */
    public long getTime(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 根据key 获取过期时间
     *
     * @param key
     * @return
     */
    public boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 移除指定key 的过期时间
     *
     * @param key
     * @return
     */
    public boolean persist(String key) {
        return redisTemplate.boundValueOps(key).persist();
    }

    //- - - - - - - - - - - - - - - - - - - - -  String类型 - - - - - - - - - - - - - - - - - - - -

    /**
     * 根据key获取值
     *
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 将值放入缓存
     *
     * @param key   键
     * @param value 值
     * @return true成功 false 失败
     */
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 将值放入缓存并设置时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒) -1为无期限
     * @return true成功 false 失败
     */
    public void set(String key, String value, long time) {
        if (time > 0) {
            redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
        } else {
            redisTemplate.opsForValue().set(key, value);
        }
    }

    /**
     * 批量添加 key (重复的键会覆盖)
     *
     * @param keyAndValue
     */
    public void batchSet(Map keyAndValue) {
        redisTemplate.opsForValue().multiSet(keyAndValue);
    }

    /**
     * 批量添加 key-value 只有在键不存在时,才添加
     * map 中只要有一个key存在,则全部不添加
     *
     * @param keyAndValue
     */
    public void batchSetIfAbsent(Map keyAndValue) {
        redisTemplate.opsForValue().multiSetIfAbsent(keyAndValue);
    }

    /**
     * 对一个 key-value 的值进行加减操作,
     * 如果该 key 不存在 将创建一个key 并赋值该 number
     * 如果 key 存在,但 value 不是长整型 ,将报错
     *
     * @param key
     * @param number
     */
    public Long increment(String key, long number) {
        return redisTemplate.opsForValue().increment(key, number);
    }

    /**
     * 对一个 key-value 的值进行加减操作,
     * 如果该 key 不存在 将创建一个key 并赋值该 number
     * 如果 key 存在,但 value 不是 纯数字 ,将报错
     *
     * @param key
     * @param number
     */
    public Double increment(String key, double number) {
        return redisTemplate.opsForValue().increment(key, number);
    }

    //- - - - - - - - - - - - - - - - - - - - -  set类型 - - - - - - - - - - - - - - - - - - - -

    /**
     * 将数据放入set缓存
     *
     * @param key 键
     * @return
     */
    public void sSet(String key, String value) {
        redisTemplate.opsForSet().add(key, value);
    }

    /**
     * 获取变量中的值
     *
     * @param key 键
     * @return
     */
    public Set members(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 随机获取变量中指定个数的元素
     *
     * @param key   键
     * @param count 值
     * @return
     */
    public void randomMembers(String key, long count) {
        redisTemplate.opsForSet().randomMembers(key, count);
    }

    /**
     * 随机获取变量中的元素
     *
     * @param key 键
     * @return
     */
    public Object randomMember(String key) {
        return redisTemplate.opsForSet().randomMember(key);
    }

    /**
     * 弹出变量中的元素
     *
     * @param key 键
     * @return
     */
    public Object pop(String key) {
        return redisTemplate.opsForSet().pop("setValue");
    }

    /**
     * 获取变量中值的长度
     *
     * @param key 键
     * @return
     */
    public long size(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    /**
     * 根据value从一个set中查询,是否存在
     *
     * @param key   键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    /**
     * 检查给定的元素是否在变量中。
     *
     * @param key 键
     * @param obj 元素对象
     * @return
     */
    public boolean isMember(String key, Object obj) {
        return redisTemplate.opsForSet().isMember(key, obj);
    }

    /**
     * 转移变量的元素值到目的变量。
     *
     * @param key     键
     * @param value   元素对象
     * @param destKey 元素对象
     * @return
     */
    public boolean move(String key, String value, String destKey) {
        return redisTemplate.opsForSet().move(key, value, destKey);
    }

    /**
     * 批量移除set缓存中元素
     *
     * @param key    键
     * @param values 值
     * @return
     */
    public void remove(String key, Object... values) {
        redisTemplate.opsForSet().remove(key, values);
    }

    /**
     * 通过给定的key求2个set变量的差值
     *
     * @param key     键
     * @param destKey 键
     * @return
     */
    public Set difference(String key, String destKey) {
        return redisTemplate.opsForSet().difference(key, destKey);
    }


    //- - - - - - - - - - - - - - - - - - - - -  hash类型 - - - - - - - - - - - - - - - - - - - -

    /**
     * 加入缓存
     *
     * @param key 键
     * @param map 键
     * @return
     */
    public void add(String key, Map map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 获取 key 下的 所有  hashkey 和 value
     *
     * @param key 键
     * @return
     */
    public Map getHashEntries(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 验证指定 key 下 有没有指定的 hashkey
     *
     * @param key
     * @param hashKey
     * @return
     */
    public boolean hashKey(String key, String hashKey) {
        return redisTemplate.opsForHash().hasKey(key, hashKey);
    }

    /**
     * 获取指定key的值string
     *
     * @param key  键
     * @param key2 键
     * @return
     */
    public String getMapString(String key, String key2) {
        return redisTemplate.opsForHash().get("map1", "key1").toString();
    }

    /**
     * 获取指定的值Int
     *
     * @param key  键
     * @param key2 键
     * @return
     */
    public Integer getMapInt(String key, String key2) {
        return (Integer) redisTemplate.opsForHash().get("map1", "key1");
    }

    /**
     * 弹出元素并删除
     *
     * @param key 键
     * @return
     */
    public String popValue(String key) {
        return redisTemplate.opsForSet().pop(key).toString();
    }

    /**
     * 删除指定 hash 的 HashKey
     *
     * @param key
     * @param hashKeys
     * @return 删除成功的 数量
     */
    public Long delete(String key, String... hashKeys) {
        return redisTemplate.opsForHash().delete(key, hashKeys);
    }

    /**
     * 给指定 hash 的 hashkey 做增减操作
     *
     * @param key
     * @param hashKey
     * @param number
     * @return
     */
    public Long increment(String key, String hashKey, long number) {
        return redisTemplate.opsForHash().increment(key, hashKey, number);
    }

    /**
     * 给指定 hash 的 hashkey 做增减操作
     *
     * @param key
     * @param hashKey
     * @param number
     * @return
     */
    public Double increment(String key, String hashKey, Double number) {
        return redisTemplate.opsForHash().increment(key, hashKey, number);
    }

    /**
     * 获取 key 下的 所有 hashkey 字段
     *
     * @param key
     * @return
     */
    public Set hashKeys(String key) {
        return redisTemplate.opsForHash().keys(key);
    }

    /**
     * 获取指定 hash 下面的 键值对 数量
     *
     * @param key
     * @return
     */
    public Long hashSize(String key) {
        return redisTemplate.opsForHash().size(key);
    }

    //- - - - - - - - - - - - - - - - - - - - -  list类型 - - - - - - - - - - - - - - - - - - - -

    /**
     * 在变量左边添加元素值
     *
     * @param key
     * @param value
     * @return
     */
    public void leftPush(String key, Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * 获取集合指定位置的值。
     *
     * @param key
     * @param index
     * @return
     */
    public Object index(String key, long index) {
        return redisTemplate.opsForList().index("list", 1);
    }

    /**
     * 获取指定区间的值。
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public List range(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 把最后一个参数值放到指定集合的第一个出现中间参数的前面,
     * 如果中间参数值存在的话。
     *
     * @param key
     * @param pivot
     * @param value
     * @return
     */
    public void leftPush(String key, String pivot, String value) {
        redisTemplate.opsForList().leftPush(key, pivot, value);
    }

    /**
     * 向左边批量添加参数元素。
     *
     * @param key
     * @param values
     * @return
     */
    public void leftPushAll(String key, String... values) {
//        redisTemplate.opsForList().leftPushAll(key,"w","x","y");
        redisTemplate.opsForList().leftPushAll(key, values);
    }

    /**
     * 向集合最右边添加元素。
     *
     * @param key
     * @param value
     * @return
     */
    public void leftPushAll(String key, String value) {
        redisTemplate.opsForList().rightPush(key, value);
    }

    /**
     * 向左边批量添加参数元素。
     *
     * @param key
     * @param values
     * @return
     */
    public void rightPushAll(String key, String values) {
        redisTemplate.opsForList().rightPushAll(key, values);
    }

    /**
     * 以集合方式向右边添加元素。
     *
     * @param key
     * @param values
     * @return
     */
    public void rightPushAll(String key, Collection values) {
        redisTemplate.opsForList().rightPushAll(key, values);
    }

    /**
     * 向已存在的集合中添加元素。
     *
     * @param key
     * @param value
     * @return
     */
    public void rightPushIfPresent(String key, Object value) {
        redisTemplate.opsForList().rightPushIfPresent(key, value);
    }

    /**
     * 向已存在的集合中添加元素。
     *
     * @param key
     * @return
     */
    public long listLength(String key) {
        return redisTemplate.opsForList().size(key);
    }

    /**
     * 移除集合中的左边第一个元素。
     *
     * @param key
     * @return
     */
    public void leftPop(String key) {
        redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 移除集合中左边的元素在等待的时间里,如果超过等待的时间仍没有元素则退出。
     *
     * @param key
     * @return
     */
    public void leftPop(String key, long timeout, TimeUnit unit) {
        redisTemplate.opsForList().leftPop(key, timeout, unit);
    }

    /**
     * 移除集合中右边的元素。
     *
     * @param key
     * @return
     */
    public void rightPop(String key) {
        redisTemplate.opsForList().rightPop(key);
    }

    /**
     * 移除集合中右边的元素在等待的时间里,如果超过等待的时间仍没有元素则退出。
     *
     * @param key
     * @return
     */
    public void rightPop(String key, long timeout, TimeUnit unit) {
        redisTemplate.opsForList().rightPop(key, timeout, unit);
    }
}






你可能感兴趣的:(redis入门)