Redis 学习

前言

  在web发展的前期,关系型数据库可以说是一统江山,即使是现在mysql、oracle使用也非常的广泛,但那个时候web网站用户访问量也不高,更不用说高并发了。但是到了如今的大数据时代,网站访问量越来越高,如果网站的数据还是存储在关系型数据库中,对数据的获取和修改都依赖与磁盘的IO,可以说是很慢的。于是NoSQL应运而生。

一、Redis概述

  Redis是一种NoSQL数据库,也叫非关系型数据库,其他的NoSQL如MongoDB都是使用的很广泛的NOSQL。

  Redis是一种Key-Value类型基于内存的数据库,基于内存我们就可以联想到数据读取速度磁盘IO<内存<CPU,由此可见在内存中肯定是比关系型数据库去操作磁盘文件要快的多的。整个数据库数据都加载在内存中,因为是纯内存操作,Redis的性能非常出色,每秒钟可以出超过10万次请求,可以说是目前性能最快的Key-Value数据库。

  Redis还支持非常丰富的数据类型,如String、Hash、List、Set、Sort Set,不像memcached只支持String。此外Redis的单个Value的最大限制是1GB,而memcached只能保存1MB的数据。

  Redis还支持事务、持久化,你试想纯内存操作,如果服务挂了怎么办?所以,Redis提供了持久化,分为两种持久化RDB和AOF。这里我自己理解为RDB你可以手动执行SAVE命令将数据保存在.rdb 文件中,这个文件存的是数据,但是这个操作会阻塞其他请求(不推荐)。AOF是你可以修改Redis的配置文件,可以在多少秒中有多少次改动就触发保存的动作,该文件存的是执行的语句,由于是主线程fork一个子线程所以不会阻塞其他请求。

  而Redis的主要缺点应该就是内存了,总不能数据量大小超过物理内存吧!所以Redis是不能作为海量数据库来存储数据的。

  其实这几点也是面试官可能会问到的。

二、Redis的使用场景

  既然都是Key-Value型的数据库,那么肯定可以当数据库来使用。其次,纯内存操作,查询和修改数据非常快,那么肯定可以想到用作缓存Cache(我觉得只要是数据都在内存中的组件,应该都可以作为缓存来使用)。然后,丰富的数据类型,使得Redis可以当队列、排行榜、计数器来使用。

缓存

  为什么需要使用缓存?什么是缓存?如果在对数据库的请求中,读的次数远远大于写的次数,那么你就可以使用缓存。

  但也不是所有场景全用缓存:

  1)在业务中数据常用吗?数据命中率如何?如果命中率很低,那么也不适用缓存。

  2)在业务中是读数据多还是写数据多,如果是写数据大于读数据,那么也不适用缓存。

  3)数据大小如何?如果你读取的数据非常大,那么对系统内存开销也是很大的,同样不适用缓存。

  总结为何使用缓存:某数据经常需要读取、项目中读取数据远远大于写数据、放入缓存中的数据大小不是很大,对性能影响不大

  一般缓存的步骤:

    用户发起一个读请求首先回去读缓存,缓存命中直接返回,如果缓存中没有命中,那么去查询数据库,将数据库查询到的结果放入缓存中。

  但随之而来的就是Redis缓存的各种问题了,比如缓存穿透、缓存雪崩、缓存击穿,点击Redis缓存问题与持久化。还比如在分布式环境中缓存与数据一致性问题等等。

高速读/写的场合

  在如今的互联网中,越来越多的存在高并发的情况,比如天猫双11、抢红包、抢演唱会门票等,这些场合都是在某一个瞬间或者是某一个短暂的时刻有成千上万的请求到达服务器,如果单纯的使用数据库来进行处理,就算不崩,也会很慢的,轻则造成用户体验极差用户量流失,重则数据库瘫痪,服务宕机,而这样的场合都是不允许的!

  所以我们需要使用 Redis 来应对这样的高并发需求的场合,我们先来看看一次请求操作的流程图:

 

 

  我们来进一步阐述这个过程:

  1. 当一个请求到达服务器时,只是把业务数据在 Redis 上进行读写,而没有对数据库进行任何的操作,这样就能大大提高读写的速度,从而满足高速响应的需求;
  2. 但是这些缓存的数据仍然需要持久化,也就是存入数据库之中,所以在一个请求操作完 Redis 的读/写之后,会去判断该高速读/写的业务是否结束,这个判断通常会在秒杀商品为0,红包金额为0时成立,如果不成立,则不会操作数据库;如果成立,则触发事件将 Redis 的缓存的数据以批量的形式一次性写入数据库,从而完成持久化的工作。

  其实跟消息队列相识,使用消息队列来抗住大量的请求。

三、数据类型的使用

String

  string是redis最基本的数据类型,与memcached一样。string类型是二进制安全的,可以存储任何数据,比如图片。value最大能存储512M 。

  常用命令:set/get/decr/incr/mget等;

  应用场景:String是最常用的一种数据类型,普通的key/value存储都可以归为此类;

  实现方式:String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr、decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。

127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set name hanlaoshi
OK
127.0.0.1:6379> set location beijing
OK
127.0.0.1:6379> keys *
1) "location"
2) "name"

Hash

  常用命令:hget/hset/hgetall等

  应用场景:我们要存储一个用户信息对象数据,其中包括用户ID、用户姓名、年龄和生日,通过用户ID我们希望获取该用户的姓名或者年龄或者生日;

  实现方式:Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口。Key是用户ID, value是一个Map。这个Map的key是成员的属性名,value是属性值。这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field), 也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据。

  当前HashMap的实现有两种方式:当HashMap的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,这时对应的value的redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。

  hash是一个string类型的field和value的映射表。hash特别适合存储对象。

redis> HMSET myhash field1 "Hello" field2 "World"
"OK"
redis> HGET myhash field1
"Hello"
redis> HGET myhash field2
"World"

List

  常用命令:lpush/rpush/lpop/rpop/lrange等;

  应用场景:Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现;

  实现方式:Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。

  List可以安装插入顺序来排序,可以添加一个元素至左边或右边。

127.0.0.1:6379> lpush mylist01 100 200 300
(integer) 3

127.0.0.1:6379> lrange mylist01 0 1
1) "300"
2) "200"

Set

  常用命令:sadd/spop/smembers/sunion等;

  应用场景:Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的;

  实现方式:set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。

zset(sorted set:有序集合)

  Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

  不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

  zset的成员是唯一的,但分数(score)却可以重复。

  zadd 命令,添加元素到集合,元素在集合中存在则更新对应score

实例:

redis 127.0.0.1:6379> zadd runoob 0 redis
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 mongodb
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabitmq
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabitmq
(integer) 0
redis 127.0.0.1:6379> > ZRANGEBYSCORE runoob 0 1000
1) "mongodb"
2) "rabitmq"
3) "redis"

 

  

 

 

  

posted @ 2020-09-22 16:30  圣-保罗  阅读(45)  评论(0)    收藏  举报