Redis-详细解析

Redis

1、认识Redis

1.1什么是Redis

Redis是以key-value形式存储,和传统的关系型数据库不一样.不一定遵循传统数据库的一些基本要求.(非关系型的,分布式的,开源的,水平可拓展的)
优点:
对数据高并发读写(直接是内存中进行读写的)
对海量数据的高效率存储和访问
对数据的可拓展性和高可用性.
单线程操作,每个操作都是原子操作,没有并发相关问题(redis 6)

缺点:
redis(ACID处理非常简单)
无法做太复杂的关系数据库模型

Redis是以key-value store存储.
键可以包含:(string)字符串,哈希,(list)链表,(set)集合,(zset)有序集合.这些数据集合都指出push/pop,add/remove及取交集和并集以及更丰富的操作.redis支持各种不同方式排序,为了保证效率,数据都是缓存在内存中.它可以从周期性的把更新的数据写入到磁盘或者把修改操作写入追加的文件中.

redis定位是缓存, 提高数据读写速度, 减轻对数据库存储与访问压力

1.2redis优势

性能极高 – Redis能支持超过 10W次每秒的读写频率。丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
丰富的特性 – Redis还支持 publish/subscribe(发布/订阅), 通知, key 过期等等特性。

下面是官方的bench-mark数据:

  • 测试完成了50个并发执行100000个请求。
  • 设置和获取的值是一个256字节字符串。
  • Linuxbox是运行Linux2.6,这是X3320Xeon2.5ghz。
  • 文本执行使用loopback接口(127.0.0.1)。
    结果:读的速度是110000次/s,写的速度是81000次/s。

2、数据类型

2.1 String类型

​ String类型包含多种类型的特殊类型,并且是二进制安全的.比如序列化的对象进行存储,比如一张图片进行二进制存储.,比如一个简单的

Map map

map.put(“name”, Json.toJsonString(user));

类型命令 key 参数数据

set key value  =>  存入键值对
get key  =>  根据键取出值
incr key  =>  把值递增1
decr key  =>  把值递减1
del key  =>  根据键删除键值对
setex key timeout value  =>  存入键值对,timeout表示失效时间,单位s
ttl key => 可以查询出当前的key还剩余多长时间过期
setnx key value  =>  如果key已经存在,不做操作, 如果key不存在,直接添加

incrby key num => 偏移值
mset k1 v1 k2 v2 ... => 批量存入键值对
mget k1 k2 ... => 批量取出键值
append key 'value' => 原值后拼接新内容
setnx key value => 存入键值对,键存在时不存入
setex key timeout value => 存入键值对,timeout表示失效时间,单位s
ttl =>可以查询出当前的key还剩余多长时间过期
setrange key index value => 修改键对应的值,index表示开始的索引位置

应用场景:****

​ 计数器:许多运用都会使用redis作为计数的基础工具,他可以实现快速计数、查询缓存的功能,同时数据可以异步落地到其他的数据源。
如:视频播放数系统就是使用redis作为视频播放数计数的基础组件。

​ incr viewnum 1

​ 共享session:出于负载均衡的考虑,分布式服务会将用户信息的访问均衡到不同服务器上,用户刷新一次访问可能会需要重新登录,为避免这个问题可以用redis将用户session集中管理,
在这种模式下只要保证redis的高可用和扩展性的,每次获取用户更新或查询登录信息都直接从redis中集中获取。

2.2 hash类型

​ Hash类型是String类型的field和value的映射表.或者说是一个String集合.它特别适合存储对象,相比较而言,讲一个对象存储在Hash类型里要比存储在String类型里占用更少的内存空间,并方便存储整个对象

Mp> map

hset key hashkey hashvalue => 存入一个hash对象
hget key hashkey => 根据hash对象键取去值
hexists key hashkey => 判断hash对象是含有某个键
hdel key hashkey => 根据hashkey删除hash对象键值对


hincrby key hashkey 递增值 => 递增hashkey对应的值
hlen key => 获取hash对象键的数量
hkeys key => 获取hash对象的所有键
hvals key => 获取hash对象的所有值
hgetall key => 获取hash对象的所有数据

同样有hsetnx,其作用跟用法和setnx一样

应用场景:

哈希结构相对于字符串序列化缓存信息更加直观,并且在更新操作上更加便捷。
所以常常用于用户信息等管理,但是哈希类型和关系型数据库有所不同,哈希类型是稀疏的,
而关系型数据库是完全结构化的,关系型数据库可以做复杂的关系查询,而redis去模拟关系型复杂查询

开发困难,维护成本高

共享session:
key:user_token

value:

class User{
private String userame;
private String password;
private int age;
}
user(“dafei”, “666”, 18)


方案1: 将user对象转换json格式字符串存redis 【侧重于查, 改非常麻烦】

key value

user_token : “{name:dafei, age:19, password:666}”


方案2: 将user对象转换hash对象存redis【侧重于改,查询相对麻烦】

key value

user_token : {
name:ddafei
age 19
password: 666
}

2.3 list类型

​ Redis中的List类似Java中的queue,也可以当做List来用.
List类型是一个链表结构的集合,其主要功能有push,pop,获取元素等.更详细的说,List类型是一个双端链表的结构,我们可以通过相关操作进行集合的头部或者尾部添加删除元素,list的设计非常简单精巧,即可以作为栈,又可以作为队列.满足绝大多数需求.

Map

rpush key value => 往列表右边添加数据
lrange key start end => 范围显示列表数据,全显示则设置0 -1 
lpush key value => 往列表左边添加数据
lpop key => 弹出列表最左边的数据
rpop key => 弹出列表最右边的数据
llen key => 获取列表长度

linsert key before/after refVal newVal => 参考值之前/后插入数据
lset key index value => 根据索引修改数据
lrem key count value => 在列表中按照个数删除数据
ltrim key start end => 范围截取列表
lindex key index => 根据索引取列表中数据

应用场景:

1.用户收藏文章列表:

xxxx_user_articles:uid [aid1, aid2, aid3…]

2.4 set类型

Set集合是string类型的无序集合,set是通过hashtable实现的,对集合我们可以取交集,并集,差集.

sadd key value => 往set集合中添加元素
smembers key => 列出set集合中的元素
srem key value => 删除set集合中的元素
spop key count => 随机弹出集合中的元素


sdiff key1 key2 => 返回key1中特有元素(差集)
sdiffstore var key1 key2 => 返回key1中特有元素存入另一个set集合
sinter key1 key2 => 返回两个set集合的交集
sinterstore var key1 key2 => 返回两个set集合的交集存入另一个set集合
sunion  key1 key2 => 返回两个set集合的并集
sunionstore var key1 key2 => 返回两个set集合的并集存入另一个set集合
smove key1 key2 value => 把key1中的某元素移入key2中
scard key => 返回set集合中元素个数
sismember key value => 判断集合是否包含某个值
srandmember key count => 随机获取set集合中元素

应用场景:

1,去重;

2,抽奖;
1,准备一个抽奖池:sadd luckydraw 1 2 3 4 5 6 7 8 9 10 11 12 13
2,抽3个三等奖:spop luckydraw 3
3,抽2个二等奖:spop luckydraw 2
4,抽1个二等奖:spop luckydraw 1

2.4 sorted_set类型

zadd key score column => 存入分数和名称
zincrby key score column => 偏移名称对应的分数
zrange key start end => 按照分数升序输出名称
zrevrange key start end => 按照分数降序输出名称


zrank key name => 升序返回排名
zrevrank key name => 降序返回排名
zcard key => 返回元素个数
zrangebyscore key min max [withscores] => 按照分数范围升序输出名称
zrevrangebyscore key max min [withscores] => 按照分数范围降序输出名称
zrem key name => 删除名称和分数
zremrangebyscore key min max [withscores] => 根据分数范围删除元素
zremrangebyrank key start end => 根据排名删除元素
zcount key min max => 按照分数范围统计个数

总结:

如果确定使用redis, 此时需要考虑使用哪个数据类型 【偏redis原生数据结构】

  • 1>如果要排序选用zset
  • 2>如果数据是多个且允许重复选用list
  • 3>如果数据是多个且不允许重复选用set
  • 4>剩下的使用string

hash —> 转换成json格式

{
  key1:value1
  key2:value2        ------> "{key1:value1, key2:value2}"
}

JSON.toJsonString(map)

有些公司约定: 所有的redis的key跟value都使用字符串(排除使用zset场景) 【偏redis String类型json结构】

  • 1>如果要排序选用zset

  • 2>剩下的使用string

优点: java操作中如果使用各种类型: list set 其他的 操作redis时需要明确指定的泛型, 麻烦
所以有些公司统一规范, 统一使用字符串, 减少泛型操作

Listlist = ...
Set set = ....
Map map = ....


List  list = redis对象.getList
Set set =redis对象.getSet   
Map map  =  redis对象.getMap
 
  

怎么设计 key 与 value值
key的设计

  1:唯一性
  2:可读性
    设计keyvalue 缓存用户收藏文章列表
    key                                    value
      article_favor_list:uid1         [1,2,3,4]
      article_favor:uid2         [1,2,3,4]
  3:灵活性
  4:时效性

value值
根据需求决定

3、redis进阶

3.1redis高级命令

返回满足的所有键 **keys *** (可以模糊查询)

exists 是否存在指定key

expire 设置某个key的过期时间.使用ttl查看剩余时间

persist 取消过期时间

flushdb 清空当前数据库,flushall清空所有数据库

select 选择数据库 数据库为0到15(一共16个数据库) 默认进入的是0数据库

move [key] [数据库下标] 讲当前数据中的key转移到其他数据库中

randomkey 随机返回数据库里的一个key

rename重命名key

echo 打印名

dbsize 查看数据库的key数量

info 获取数据库信息

config get 实时传储收到的请求(返回相关的配置信息)

**config get *** 返回所有配置

3.2redis安全性

​ 因为redis速度非常快,所以在一台比较好的服务器下,一个外部用户在一秒内可以进行15w次的密码尝试,这意味你需要设定非常强大的密码来方式暴力破解.
vi编辑redis.conf文件,找到下面进行保存修改

requirepass [密码]
重启服务器 pkill redis-server
再次进入127.0.01:6379>keys *
(error)NOAUTH Authentication required.
会发现没有权限进行查询auth [密码]
输入密码则成功进入
每次进入的时候都需要输入免密,还有种简单的方式:

redis-cli -a [密码]

3.3 redis事务

Redis的事务非常简单,使用方法如下:
首先是使用multi方法打开事务,然后进行设置,这时设置的数据会放到队列里进行保存.最后使用exec执行.把数据依次存储到redis中.使用discard方法取消事务.

3.4 redis持久化机制

Redis是一个支持持久化的内存数据库,也就是说redis需要经常将内存中中的数据同步到硬盘来保证持久化.
Redis持久化的两种方式:

3.4.1RDB方式

snapshotting(快照)默认方式.将内存中以快照的方式写入到二进制文件中.默认为dump.rdb.可以配置设置自动做快照持久化方式.我们可以配置redis在n秒内如果超过m个key就修改自动做快照.

Snapshotting设置:
save 900 1  #900秒内如果超过1个Key被修改则发起快照保存
save 300 10 #300秒内如果超过10个key被修改,则发起快照保存
save 60 10000

3.4.2AOF方式

append-only file (缩写aof)的方式,由于快照方式是在一定时间间隔做一次,所以可能发生reids意外down的情况就会丢失最后一次快照后的所有修改的数据.aof比快照方式有更好的持久化性,是由于在使用aof时,redis会将每一个收到的写命令都通过write函数追加到命令中,当redis重新启动时会重新执行文件中保存的写命令来在内存中重建这个数据库的内容.这个文件在bin目录下:

appendonly.aof

aof不是立即写到硬盘中,可以通过配置文件修改强制写到硬盘中.
aof设置:

appendonly yes //启动aof持久化方式有三种修改方式
#appendfsync always//收到命令就立即写到磁盘,效率最慢.但是能保证完全的持久化
#appendfsync everysec//每秒写入磁盘一次,在性能和持久化方面做了很好的折中
#appendfsync no //完全以依赖os 性能最好,持久化没保证

3.5 Redis内存淘汰机制及过期Key处理

https://www.cnblogs.com/maguanyue/p/12090414.html

Redis内存淘汰机制及过期Key处理
  “天长地久有时尽,此恨绵绵无绝期。”好诗!好诗啊!即使是天长地久,也总会有尽头,那么,Redis的内存是不是也会有时尽呢?答案是肯定的。那么,当Redis的内存满了以后,再来新的请求,我们该怎么办呢?这时候,大家就应该来了解Redis的内存淘汰策略了,了解了相关的知识点后,就能明白“Redis内存有时尽”后,会发生些什么。

Redis内存淘汰机制

Redis内存淘汰机制是指当内存使用达到上限(可通过maxmemory配置,0为不限制,即服务器内存上限),根据一定的算法来决定淘汰掉哪些数据,以保证新数据的存入。

常见的内存淘汰机制分为四大类:

1. LRU**:**LRU是Least recently used,最近最少使用的意思,简单的理解就是从数据库中删除最近最少访问的数据,该算法认为,你长期不用的数据,那么被再次访问的概率也就很小了,淘汰的数据为最长时间没有被使用,仅与时间相关。

2. **LFU:**LFU是Least Frequently Used,最不经常使用的意思,简单的理解就是淘汰一段时间内,使用次数最少的数据,这个与频次和时间相关。

3. **TTL:**Redis中,有的数据是设置了过期时间的,而设置了过期时间的这部分数据,就是该算法要解决的对象。如果你快过期了,不好意思,我内存现在不够了,反正你也要退休了,提前送你一程,把你干掉吧。

4. 随机淘汰:生死有命,富贵在天,是否被干掉,全凭天意了。

通过maxmemroy-policy可以配置具体的淘汰机制,看了网上很多文章说只有6种,其实有8种,可以看Redis5.0的配置文件,上面有说明:

1. volatile-lru => 找出已经设置过期时间的数据集,将最近最少使用(被访问到)的数据干掉。
2. volatile-ttl => 找出已经设置过期时间的数据集,将即将过期的数据干掉。
3. volatile-random => 找出已经设置过期时间的数据集,进行无差别攻击,随机干掉数据。
4. volatile-lfu => 找出已经设置过期时间的数据集,将一段时间内,使用次数最少的数据干掉。
5. allkeys-lru =>与第1个差不多,数据集从设置过期时间数据变为全体数据。
6. allkeys-lfu => 与第4个差不多,数据集从设置过期时间数据变为全体数据。
7. allkeys-random => 与第3个差不多,数据集从设置过期时间数据变为全体数据。
8. no-enviction => 什么都不干,报错,告诉你内存不足,这样的好处是可以保证数据不丢失,这也是系统默认的淘汰策略。

Redis过期Key清除策略

Redis中大家会对存入的数据设置过期时间,那么这些数据如果过期了,Redis是怎么样把他们消灭掉的呢?我们一起来探讨一下。下面介绍三种清除策略:

**惰性删除:**当访问Key时,才去判断它是否过期,如果过期,直接干掉。这种方式对CPU很友好,但是一个key如果长期不用,一直存在内存里,会造成内存浪费。

**定时删除:**设置键的过期时间的同时,创建一个定时器,当到达过期时间点,立即执行对Key的删除操作,这种方式最不友好。

**定期删除:**隔一段时间,对数据进行一次检查,删除里面的过期Key,至于要删除多少过期Key,检查多少数据,则由算法决定。举个例子方便大家理解:Redis每秒随机取100个数据进行过期检查,删除检查数据中所有已经过期的Key,如果过期的Key占比大于总数的25%,也就是超过25个,再重复上述检查操作。

Redis服务器实际使用的是惰性删除和定期删除两种策略:通过配合使用这两种删除策略,可以很好地在合理使用CPU和避免浪费内存之间取得平衡。

好的,相关知识介绍完毕,希望这篇文章能对你有所帮助!

4、实际运用

4.1Jedis基本使用

导入Jedis相关的依赖:

	<parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.4.3version>
        <relativePath/>
    parent>

   <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starterartifactId>
        dependency>
    <dependency>
        <groupId>redis.clientsgroupId>
        <artifactId>jedisartifactId>
    dependency>

   <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
   dependency>

4.1.1普通API

@Test
public void testJedisPool() {
    // 1:创建Jedis连接池
    JedisPool pool = new JedisPool("localhost", 6379);
    // 2:从连接池中获取Jedis对象
    Jedis jedis = pool.getResource();
    /* 设置密码
	jedis.auth(密码); */
    // 3:TODO
    System.out.println(jedis);
    // 4:关闭资源
    jedis.close();
    pool.destroy();
}
---------------------------------------------
	@Test
    public void testTool(){
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        最大连接数, 默认8个
        config.setMaxTotal(100);
        最大空闲连接数, 默认8个
        config.setMaxIdle(20);
        //获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1
        config.setMaxWaitMillis(-1);
        //在获取连接的时候检查有效性, 默认false
        config.setTestOnBorrow(true);
        JedisPool pool = new JedisPool(config,"192.168.122.128",6379,5000,"wolfcode");
        Jedis j = pool.getResource();
        String name = j.get("name");
        System.out.println(name);
        j.close();
        pool.close();
        pool.destroy();
    }

4.2集成SpringBoot

导入依赖:

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-redisartifactId>
    dependency>

   <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
   dependency>

配置:application.properties

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=admin

普通API:

@Autowired
private StringRedisTemplate redisTemplate;

@Test
public void testRedisTemplate() {
    // 操作string
    redisTemplate.opsForValue().xx();
    // 操作hash
    redisTemplate.opsForHash().xx();
    // 操作list
    redisTemplate.opsForList().xx();
    // 操作set
    redisTemplate.opsForSet().xx();
    // 操作zset
    redisTemplate.opsForZSet().xx();
}
-----------------------------------------
redisTemplate.opsForValue();//操作字符串
redisTemplate.opsForHash();//操作hash
redisTemplate.opsForList();//操作list
redisTemplate.opsForSet();//操作set
redisTemplate.opsForZSet();//操作有序set

你可能感兴趣的:(redis)