Redis--狂神说Redis基础汇总(完结)

2021.6.12-2021.6.14:端午学学玩玩弄完了Redis基础的汇总,越学越觉得自己知识量的匮乏。

参考链接:狂神说Java--Redis汇总:https://www.bilibili.com/video/BV1S54y1R7SB?p=1

Redis五种基本数据类型:

  • 1.String字符串

    OK
    127.0.0.1:6380> set k1 1
    OK
    127.0.0.1:6380> INCR k1
    2
    127.0.0.1:6380> DECR k1
    1
    127.0.0.1:6380> keys *
    k1
    127.0.0.1:6380> set k2 huyuqiao
    OK
    127.0.0.1:6380> GETRANGE K2 0 3 127.0.0.1:6380> GETRANGE k2 0 3
    huyu
    127.0.0.1:6380> GETRANGE k2 0 -1
    huyuqiao
    127.0.0.1:6380> SETRANGE k2 1 XX
    8
    127.0.0.1:6380> get k2
    hXXuqiao
    127.0.0.1:6380> SETEX k3 30 "hello, world" #set exist:存在或不存在都替换
    OK
    127.0.0.1:6380> ttl k3
    28
    127.0.0.1:6380> ttl k2
    -1
    127.0.0.1:6380> keys *
    k1
    k2
    127.0.0.1:6380> ttl k3
    -2
    127.0.0.1:6380> setnx mykey "redis" #set if not exist:不存在就set,存在就set失败,还是原来值
    1
    127.0.0.1:6380> ttl mykey
    -1
    127.0.0.1:6380> setnx mykey "mongodb"
    0
    127.0.0.1:6380> get mykey
    redis
    127.0.0.1:6380> ttl k2 #永久返回-1
    -1
    127.0.0.1:6380> ttl k3 #过期返回-2
    -2
    127.0.0.1:6380>
    127.0.0.1:6380> mset user:1:name huyuqiao user:1:age 22
    OK
    127.0.0.1:6380> mget user:1:name user:1:age
    huyuqiao
    22
    127.0.0.1:6380>
  • List

    127.0.0.1:6380> FLUSHALL
    OK
    127.0.0.1:6380> clear
    127.0.0.1:6380> LPUSH list 1
    1
    127.0.0.1:6380> LPUSH list 2
    2
    127.0.0.1:6380> LPUSH list 3
    3
    127.0.0.1:6380> LRANGE list 0 -1
    3
    2
    1
    127.0.0.1:6380> RPUSH list a
    4
    127.0.0.1:6380> LPOP list
    3
    127.0.0.1:6380> LRANGE 0 -1
    ERR wrong number of arguments for 'lrange' command 127.0.0.1:6380> LRANGE list 0 -1
    2
    1
    a
    127.0.0.1:6380> LINDEX list 0
    2
    127.0.0.1:6380> LLEN list
    3
    127.0.0.1:6380> FLUSHALL
    OK
    127.0.0.1:6380> clear
    127.0.0.1:6380> LPUSH list one
    1
    127.0.0.1:6380> LPUSH list two
    2
    127.0.0.1:6380> LPUSH list two
    3
    127.0.0.1:6380> LREM list 1 one #移除list中等于one的一个元素(最后加入的移除掉)
    1
    127.0.0.1:6380> LRANGE list 0 -1
    two
    two
    127.0.0.1:6380> LREM list 2 one
    0
    127.0.0.1:6380> LREM list 2 two
    2
    127.0.0.1:6380> LRANGE list 0 -1 127.0.0.1:6380> FLUSHALL 127.0.0.1:6380> LPUSH list one
    1
    127.0.0.1:6380> LPUSH list two
    2
    127.0.0.1:6380> LPUSH list three
    3
    127.0.0.1:6380> LPUSH list four
    4
    127.0.0.1:6380> LTRIM list 1 2
    OK
    127.0.0.1:6380> LRANGE list 0 -1
    three
    two
    127.0.0.1:6380>
  • Set

    127.0.0.1:6380> FLUSHALL
    OK
    127.0.0.1:6380> SADD myset "hello"
    1
    127.0.0.1:6380> sadd myset "world"
    1
    127.0.0.1:6380> sadd myset "huyuqiao"
    1
    127.0.0.1:6380> smembers myset
    hello
    huyuqiao
    world
    127.0.0.1:6380> SISMEMBER myset huyuqiao
    1
    127.0.0.1:6380> sadd myset "HYQ"
    1
    127.0.0.1:6380> SMEMBERS myset
    hello
    huyuqiao
    HYQ
    world
    127.0.0.1:6380> SREM myset hello
    1
    127.0.0.1:6380> scard myset
    3
    127.0.0.1:6380> SMEMBERS myset
    huyuqiao
    HYQ
    world
    127.0.0.1:6380> SRANDMEMBER myset
    world
    127.0.0.1:6380> SRANDMEMBER myset
    huyuqiao
    127.0.0.1:6380>
  • Hash

    huyuqiao
    127.0.0.1:6380> FLUSHALL
    OK
    127.0.0.1:6380> clear
    127.0.0.1:6380> hset myhash field1 huyuqiao
    1
    127.0.0.1:6380> hmset myhash field1 hello field2 world
    OK
    127.0.0.1:6380> hmget myhash field1 field2
    hello
    world
    127.0.0.1:6380> hgetall myhash
    field1
    hello
    field2
    world
    127.0.0.1:6380> hlen myhash
    2
    127.0.0.1:6380> HEXISTS myhash field1
    1 127.0.0.1:6380> HKEYS myhash
    field1
    field2
    127.0.0.1:6380> HVALS myhash
    hello
    world
    127.0.0.1:6380> HSETNX myhash field4 hello #hash设置
    1
    127.0.0.1:6380> HGETALL myhash
    field1
    hello
    field2
    world
    field4
    hello
    127.0.0.1:6380>
  • Zset

    127.0.0.1:6380> zadd salary 100 huyuqiao
    1
    127.0.0.1:6380> zadd salary 200 HUYUQIAO
    1
    127.0.0.1:6380> zadd salary 300 HYQ
    1
    127.0.0.1:6380> ZRANGEBYSCORE salary -inf +inf
    huyuqiao
    HUYUQIAO
    HYQ
    127.0.0.1:6380> zrange salary 0 -1
    huyuqiao
    HUYUQIAO
    HYQ
    127.0.0.1:6380> zrem salary huyuqiao
    1
    127.0.0.1:6380> ZRANGEBYSCORE salary -inf +inf
    HUYUQIAO
    HYQ

Redis三种特殊数据类型:

1.GeoSpatical

适用场景:获取附近人,好友距离,网约车定位功能。

# 添加位置经纬度
127.0.0.1:6379> GEOADD china:city 116.41667 39.91667 北京
(integer) 1
127.0.0.1:6379> GEOADD china:city 121.43333 31.23000 上海
(integer) 1
127.0.0.1:6379> GEOADD china:city 106.45000 29.56667 重庆
(integer) 1
127.0.0.1:6379> GEOADD china:city 114.06667 22.61667 深圳
(integer) 1
127.0.0.1:6379> GEOADD china:city 120.20000 30.26667 杭州
(integer) 1
127.0.0.1:6379> GEOADD china:city 108.95000 34.26667 西安
(integer) 1 # 查看不同位置经纬度
127.0.0.1:6379> GEOPOS china:city 北京 西安 #查看不同位置之间距离(默认是m)
127.0.0.1:6379> GEODIST china:city 北京 上海
"1066981.1340"
127.0.0.1:6379> GEODIST china:city 北京 上海 km
"1066.9811"
127.0.0.1:6379> GEODIST china:city 北京 重庆 km
"1465.8918"
127.0.0.1:6379> # 查看指定经纬度周边范围内的地方(可以以此扩散到周围指定范围内的指定人数的好友)
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km
重庆
西安
深圳
杭州
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km
重庆
西安
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist #查看某经纬度周围500km的所有城市和距离/km
重庆
346.0548
西安
484.7511
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord #查看某经纬度周围500km的所有城市和经纬度
重庆
106.4500012993812561
29.56666939001875249
西安
108.95000249147415161
34.2666710302806834
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km count 1 #查看某经纬度周围500km的三个城市
重庆
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km count 3 #查看某经纬度周围500km的三个城市
重庆
西安
127.0.0.1:6379> #查看地点周边的所有城市(类似于定位)
127.0.0.1:6379> GEORADIUSBYMEMBER china:city 北京 1000 km
北京
西安
127.0.0.1:6379> GEORADIUSBYMEMBER china:city 上海 1000 km
杭州
上海
127.0.0.1:6379> #将城市的二维经纬度转成一位hash字符串
127.0.0.1:6379> GEOHASH china:city 北京 重庆
wx4g14s53n0
wm78nq6w2f0
127.0.0.1:6379> #GEO底层是zset,所有可以用zset命令来操作
127.0.0.1:6379> zrange china:city 0 -1 #查看所有城市
重庆
西安
深圳
杭州
上海
北京
127.0.0.1:6379> zrem china:city 北京 #删除北京这个城市
2.Hyperloglog

适用场景: 网站UV量。传统用set统计,但若存在大量用户id,则太消耗内容且麻烦,若只为计数且允许有错误率(0.81%),则可行,否则还是用set统计

基数:集合中不重复元素个数。如{1, 3, 5, 5 ,7}则为{1,3,5,7},基数为4

127.0.0.1:6379> clear
127.0.0.1:6379> PFADD mykey a b c d e f g h i j #设置mykey 集合
(integer) 1
127.0.0.1:6379> PFCOUNT mykey #统计mykey 集合基数数量
(integer) 10
127.0.0.1:6379> PFADD mykey2 i j z x c v b n m
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 9
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 #2个集合取并集
OK
127.0.0.1:6379> PFCOUNT mykey3
(integer) 15
127.0.0.1:6379>
3.Bitmaps

适用场景:判断、统计活跃、不活跃,登录、未登录这些非1即0的场景

[root@VM-8-11-centos ~]# redis-cli -a root --raw
127.0.0.1:6379> setbit sign 0 1 #设置sign用户某天是否打卡
(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) 0
127.0.0.1:6379> SETBIT sign 6 0
(integer) 0
127.0.0.1:6379> GETBIT sign 3 #得到sign用户某天打卡情况
(integer) 1
127.0.0.1:6379> GETBIT sign 6

Redis事务:

Reidsi事务:没有隔离级别概念,即事务不保证原子性(事务内部有多条命令,不一定,可以实现部分成功、部分失败),但是单条命令保证原子性

1.Redis事务流程:
  • 开启事务(multi)
  • 命令入队
  • 执行事务(exec)
#开启-结束事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec
OK
OK
v2
OK
127.0.0.1:6379> #放弃事务:之前队列代码全部rollback
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 asdfasdf
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> EXEC
ERR EXEC without MULTI 127.0.0.1:6379> get k2
v2
2.Redis事务非原子性

之所以事务非原子性,是因为有两种异常:

  • 编译性异常(检查性异常):代码错了,根本就没某个语句、函数、配置文件等

    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379> set k1 v1
    QUEUED
    127.0.0.1:6379> set k2 v2
    QUEUED
    127.0.0.1:6379> gasdfa k3 #语句错误,相当于没执行,在编译时候就出错了
    ERR unknown command 'gasdfa' 127.0.0.1:6379> set k4 v4
    QUEUED
    127.0.0.1:6379> EXEC
    EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get k4 127.0.0.1:6379> get k1
  • 运行时异常(非检查性异常):没有某个变量、某个对象没有new等(不会全部rollback)

    127.0.0.1:6379> set k1 "v1"
    OK
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> incr k1
    QUEUED
    127.0.0.1:6379> set k2 v2
    QUEUED
    127.0.0.1:6379> get k2
    QUEUED
    127.0.0.1:6379> EXEC
    ERR value is not an integer or out of range OK
    v2 #上面出错了,但是依然不影响下面语句执行,所以证明了redis事务是不保证原子性的
    127.0.0.1:6379>

Redis乐观锁:

乐观锁:不会上锁,更新数据时才会比较version是否被人修改过(redis事务中如果被修改过,则事务不会执行成功)

悲观锁:无论做什么,都会上锁,效率低下但是安全

​ Redis事务中用<font color=red size=3>Watch</font>实现乐观锁,中途如果被修改,导致version变更,则事务全部都不会成功

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 #Redis乐观锁:watch监视money
OK
127.0.0.1:6379> MULTI #在另一个窗口将money改成了101
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> EXEC
127.0.0.1:6379> get money
101
127.0.0.1:6379>

SpringBooot--配置Redis源代码

Jedis:直连,多线程下不安全,类似于BIO模式(Springboot2.X后被淘汰)

Lettuce:采用Netty,多线程下共享,类似于NIO模式

autoconfig->srping.factories中找到redis相关配置

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration { @Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
} @Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
} }

SpringBooot--自定义RedisTemplate、RedisUtil

1.RedisTemplate序列化配置
package com.empirefree.springboot.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
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; /**
* @program: springboot
* @description: RedisTemplate配置
* @author: huyuqiao
* @create: 2021/06/13 16:05
*/ @Configuration
public class RedisConfig {
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
// Json序列化配置
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.WRAPPER_ARRAY);
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;
}
}
2.RedisUtil配置(CRUD操作string,map,list,set)
package com.empirefree.springboot.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils; import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit; /**
* @program: springboot
* @description: Redis工具类
* @author: huyuqiao
* @create: 2021/06/13 16:14
*/ @Component
public final class RedisUtil { @Autowired
private RedisTemplate<String, Object> redisTemplate; // =============================common============================
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
} /**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
}
}
} // ============================String============================= /**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
} /**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/ public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/ public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
} /**
* 递减
* @param key 键
* @param delta 要减少几(小于0)
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
} // ================================Map================================= /**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
} /**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
} /**
* HashSet
* @param key 键
* @param map 对应多个键值
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* HashSet 并设置时间
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
} /**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
} /**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
} /**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
} // ============================set============================= /**
* 根据key获取Set中的所有值
* @param key 键
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
} /**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
} /**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
} /**
* 获取set缓存的长度
*
* @param key 键
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
} /**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/ public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
} // ===============================list================================= /**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
} /**
* 获取list缓存的长度
*
* @param key 键
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
} /**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
} /**
* 将list放入缓存
*
* @param key 键
* @param value 值
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} } /**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} } /**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/ public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/ public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
} } }

Redis--持久化

持久化:在指定时间间隔内将内存数据存入磁盘中,断电也能恢复数据,使用快照文件读到内存中。

1.Redis--RDB(默认推荐)
  • 保存过程:父进程fork一个子进程,将数据持久化到临时文件中,持久化结束,再替换上次的RDB正式文件。

  • 触发条件:

    • save满足:命令save 900 1 即是在15分钟内修改了1次 即会触发RDB。
    • 执行FlushAll命令
    • 退出Redis,会产生RDB文件
  • 适用场景:适合大规模数据恢复且数据完整性不敏感的情况。

2.Redis--AOF(重启后是默认先载入AOF,因为数据更完整)
  • 保存过程:父进程fork一个子进程,以日志形式将所有指令记录下来(读操作不记录),然后将数据只追加不改写到AOF文件,然后替换上次的AOF文件

  • 触发条件:appendfsync always/everysec/no 命令

  • 适用场景:对恢复数据完整性要求严格

  • 重写场景:不断追加文件到一个阈值,则会重写aof文件

Redis--发布订阅

发布订阅:可以做消息推送、聊天室等等

#发布者:往Redis某渠道中发消息,所有订阅者都可以接收到
127.0.0.1:6379> PUBLISH huyuqiao "hello,world"
1 #订阅者:订阅Redis中某个渠道channel
127.0.0.1:6379> SUBSCRIBE huyuqiao
subscribe
huyuqiao
1
message
huyuqiao
hello,world

Redis--主从复制(主写从读)

1.Linux配置文件
  • daemonize的no改成yes
  • port、pidfile、logfile、dbfilename改成对于新redis名
  • 如果redis有密码,需要在slave中配置或者在master中去掉密码
#master断了,slaver依然是原来master的slaver。但是slaver断了,master就没有slaver了
>>slaveof no one #关闭slave状态,变成master
>>shutdown #停止redis,其slave状态停止,下次启动变成master,且原来master下的该redis就没有了 redis-server redis80.conf #启动80redis窗口。
kill -s 9 pid #关闭某个进程
2. 复制原理
  • 全量复制:slave启动后,发送sync同步命令给master,然后同步master中所有数据
  • 增量复制:master写数据后,slave实时得到。

Redis--哨兵模式

流程:如下,在启动哨兵后,关闭79主服务,就从80,81中投票选举了80为主服务,然后把79,80都当成了slave,所以下次79启动的时候会默认是slave。另外,由于数据是增量复制,所以数据在80中保存完好,且79启动后就全量复制到79中了

#哨兵模式下的日志

8044:X 14 Jun 16:35:06.885 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
8044:X 14 Jun 16:35:06.885 # Sentinel ID is c0ce22fc8365ff48663b7db710ce8c359529c3d9
8044:X 14 Jun 16:35:06.885 # +monitor master mymaster 127.0.0.1 6379 quorum 1
8044:X 14 Jun 16:35:51.556 # +sdown master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.556 # +odown master mymaster 127.0.0.1 6379 #quorum 1/1
8044:X 14 Jun 16:35:51.556 # +new-epoch 1
8044:X 14 Jun 16:35:51.556 # +try-failover master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.572 # +vote-for-leader c0ce22fc8365ff48663b7db710ce8c359529c3d9 1
8044:X 14 Jun 16:35:51.572 # +elected-leader master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.572 # +failover-state-select-slave master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.624 # +selected-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.624 * +failover-state-send-slaveof-noone slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.684 * +failover-state-wait-promotion slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.826 # +promoted-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.826 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.914 * +slave-reconf-sent slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:52.831 * +slave-reconf-inprog slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:52.831 * +slave-reconf-done slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:52.912 # +failover-end master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:52.912 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380
8044:X 14 Jun 16:35:52.912 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380
8044:X 14 Jun 16:35:52.912 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380

Redis--缓存穿透、击穿、雪崩

  • 缓存穿透:缓存和数据库中都没有数据

    • 解决方案:1.布隆过滤器 2.存储空对象:数据库没找到后,redis中临时存一个空对象
  • 缓存击穿:某个key值过期,来了大量访问
    • 解决方案:1.永不过期 2.分布式锁:一个线程获取,其他线程等待
  • 缓存雪崩:海量key值过期,来了大量访问
    • 解决方案:1.redis高可用:多设置几台redis 2.限流降级:缓存失效后,通过加锁或者队列来控制读数据库写缓存的数量 3.数据预热:大量数据加载到缓存中,根据不同访问量来设置不同过期时间

Redis--狂神说Redis基础汇总(完结)的更多相关文章

  1. redis基础:redis下载安装与配置,redis数据类型使用,redis常用指令,jedis使用,RDB和AOF持久化

    知识点梳理 课堂讲义 课程计划 1. REDIS 入 门 (了解) (操作)   2. 数据类型 (重点) (操作) (理解) 3. 常用指令   (操作)   4. Jedis (重点) (操作) ...

  2. Redis(一)基础数据结构

    1.目录 Redis 基础数据结构 string (字符串) list (列表) hash (字典) set (集合) zset (集合) 容器型数据结构的通用规则 过期时间 2.Redis 基础数据 ...

  3. springboot配置redis+jedis,支持基础redis,并实现jedis GEO地图功能

    Springboot配置redis+jedis,已在项目中测试并成功运行,支持基础redis操作,并通过jedis做了redis GEO地图的java实现,GEO支持存储地理位置信息来实现诸如附近的人 ...

  4. 狂神说redis笔记(三)

    八.Redis.conf 容量单位不区分大小写,G和GB有区别 可以使用 include 组合多个配置问题 网络配置 日志 # 日志 # Specify the server verbosity le ...

  5. redis从0-1学习记录(完结)

    1. NoSQL(not only sql):不仅仅是数据库,非关系型数据库,关系型数据库是以表格的行列进行存储的,而非关系型数据库是以键值对进行存储,不需要固定的格式.非关系型数据库的特点,方便扩展 ...

  6. Redis基本认识和基础学习-基本命令

    Redis 基本介绍 REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统. Redis是一个开源的使用ANS ...

  7. Redis服务搭建与基础功能示例

    一.Redis简介 Redis是一个非关系型远程内存数据库,它也是一个Key-value模型的数据库.Redis支持5种数据类型(string.list.set.sorted set.hash),可以 ...

  8. Redis源码研究—基础知识

    1. Redis 是什么 Redis是一个开源的使用ANSI C语言编写的基于内存的key/value存储系统,与memcache类似,但它支持的value类型更多,包括:字符串(string).链表 ...

  9. Redis学习笔记之基础篇

    Redis是一款开源的日志型key-value数据库,目前主要用作缓存服务器使用. Redis官方并没有提供windows版本的服务器,不过微软官方开发了基于Windows的Redis服务器Micro ...

随机推荐

  1. jupyter中那些神奇的第三方拓展魔术命令

    1 简介 无论是jupyter notebook还是jupyter lab,都可以使用ipython中的众多自带魔术命令来实现丰富的辅助功能,诸如%time之类的. 这些都已经是老生常谈的知识没什么好 ...

  2. .NET Core 中依赖注入框架详解 Autofac

    本文将通过演示一个Console应用程序和一个ASP.NET Core Web应用程序来说明依赖注入框架Autofac是如何使用的 Autofac相比.NET Core原生的注入方式提供了强大的功能, ...

  3. ThreadLocal引起的一次线上事故

    > 线上用户存储数据后查看提示无权限 前言 不知道什么时候年轻的我曾一度认为Java没啥难度,没有我实现不了的需求,没有我解不了的bug 直到我遇到至今难忘的一个bug . 线上用户存储数据后查 ...

  4. 『居善地』接口测试 — 4、Requests库发送GET请求

    目录 1.使用Requests库发送带参数的GET请求 2.查看GET请求的内容 3.带请求头.参数的Get请求 Requests库GET请求是使用HTTP协议中的GET请求方式对目标网站发起请求. ...

  5. QFNU-11.08training

    7-1  阅览室 题目: 天梯图书阅览室请你编写一个简单的图书借阅统计程序.当读者借书时,管理员输入书号并按下S键,程序开始计时:当读者还书时,管理员输入书号并按下E键,程序结束计时.书号为不超过10 ...

  6. .Net Core·热加载的实现及测试

    阅文时长 | 0.25分钟 字数统计 | 460字符 主要内容 | 1.引言&背景 2.解决原理&方法 3.声明与参考资料 『.Net Core·热加载的实现及测试』 编写人 | SC ...

  7. Powershell阻止确认

    要阻止弹出确认提示,需要设置-Confirm为false, new-VM -Name $hostname -Template $template -VMHost 10.11.31.5 -OSCusto ...

  8. Linux查看PCIe版本及速率# lspci -vvv |grep Width -i

    Linux查看PCIe版本及速率 https://www.cnblogs.com/lsgxeva/p/9542975.html# lspci -vvv |grep Width -i # lspci | ...

  9. 【转载】有图 KVM折腾记..

    KVM折腾记...https://lengjibo.github.io/KVM%E6%8A%98%E8%85%BE%E8%AE%B0/  Veröffentlicht am 2018-09-20 |  ...

  10. TCP/IP协议三次握手_四次挥手

    TCP/IP协议 TCP是一种面向连接的端到端的可靠传输协议. TCP报头格式 三次握手的过程 一.客户端发送一个连接请求,发送一个随机数X,这时客户端的端口状态变为SYN_SENT状态. 二.服务端 ...