Redis(Remote Dictionary Server) 是一个使用 C 语言编写的,开源的(BSD许可)高性能非关系型(NoSQL)的键值对数据库。
本篇内容包括:Redis 简介(为什么快?为什么单线程?优点,缺点等),Redis 在 Java Web 中的应用,Redis 安装(Win、Linux、Mac 场景下的安装)等内容
一、Redis 简介
Redis 是 C 语言开发的一个开源的(遵从 BSD 协议)高性能键值对(key-value)的内存数据库,可以用作数据库、缓存、消息中间件等。
它是一种 NoSQL(not-only sql,泛指非关系型数据库)的数据库。是一种基于内存的数据库,并且提供一定的持久化功能。
Redis 作为一个内存数据库:性能优秀,数据在内存中,读写速度非常快,支持并发 10W QPS。单进程单线程,是线程安全的,采用 IO 多路复用机制。丰富的数据类型,支持字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等。支持数据持久化。可以将内存中数据保存在磁盘中,重启时加载。主从复制,哨兵,高可用。可以用作分布式锁。可以作为消息中间件使用,支持发布订阅。
1、Redis为什么快呢?
redis的速度非常的快,单机的redis就可以支撑每秒10几万的并发,相对于mysql来说,性能是mysql的几十倍。速度快的原因主要有几点:
- 完全基于内存操作
- C语言实现,优化过的数据结构,基于几种基础的数据结构,redis做了大量的优化,性能极高
- 使用单线程,无上下文的切换成本
- 基于非阻塞的IO多路复用机制
2、Redis为何选择单线程?
- 避免过多的上下文切换开销。程序始终运行在进程中单个线程内,没有多线程切换的场景。
- 避免同步机制的开销:如果 Redis选择多线程模型,需要考虑数据同步的问题,则必然会引入某些同步机制,会导致在操作数据过程中带来更多的开销,增加程序复杂度的同时还会降低性能。
- 实现简单,方便维护:如果 Redis使用多线程模式,那么所有的底层数据结构的设计都必须考虑线程安全问题,那么 Redis 的实现将会变得更加复杂。
3、那为什么Redis6.0之后又改用多线程呢?
Redis 使用多线程并非是完全摒弃单线程,Redis 还是使用单线程模型来处理客户端的请求,只是使用多线程来处理数据的读写和协议解析,执行命令还是使用单线程。
这样做的目的是因为redis的性能瓶颈在于网络 IO 而非 CPU,使用多线程能提升 IO 读写的效率,从而整体提高 Redis 的性能。
4、Redis的优势
- Redis支持保存多种数据结构(支持string,list,set,sorted set,hash),其单个value的最大限制是1GB,因此Redis可以用来实现很多有用的功能;
- 因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB;
- Redis也可以对存入的Key-Value设置expire时间,因此也可以被当作一 个功能加强版的memcached来用;
- Redis可以将内存的数据利用快照和日志的形式保存到硬盘上,这样在发生类似断电或者机器故障的时 候,内存中的数据不会“丢失”;
- 除了上述功能以外,Redis还提供了键过期、发布订阅、事务、流水线、Lua脚本等附加功能。
5、Redis的缺点
Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
总之,如果在合适的场景使用好Redis,它就会像一把瑞士军刀一样所向披靡、无往不利!!!
二、Redis 在 Java Web 中的应用
1、Redis 的使用场景
Redis 根据使用数据类型的不同,对应的使用场景有很多,例如:
- 缓存(String / Hash 类型):缓存如用户信息,视频信息等;
- 分布式锁(String 类型):setnx 方法,「key不存在才插入」,可以用它来实现分布式锁;
- 计数器(String 类型):incr/decr 方法,自增自减,由于是原子性,可以用来计数统计浏览数、点赞数等;
- 限制请求次数(String 类型):也是利用 incr 方法,限制请求次数访问者的 ip 和其他信息作为 key,访问一次增加一次计数,超过次数则返回 false;
- 数据共享分布式(String 类型):因为 Redis 是分布式的独立服务,可以在多个应用之间共享,所有可以实现需要例如分布式 Session;
- 购物车(Hash 类型):类似存储商品信息,大 key 为商家 id,小 key 是商品 id 即 goodsId,value 为该 goodsId 的详细信息;
- 消息队列(List / Stream 类型):二者都可以实现消息队列,而 Redis5.0 新增的 Stream,解决了 List 实现的消息队列不能持久化和不能重复消费的问题;
- 点赞、踩、收藏(Set 类型):Set 集合无需、不可重复,可以保证一个用户只能点一个赞;
- 共同关注(Set 类型):Set 类型支持交集运算,所以可以用来计算共同关注的好友、公众号等;
- 抽奖活动(Set 类型):存储某活动中中奖的用户名 ,Set 类型因为有去重功能,可以保证同一个用户不会中奖两次;
- 排行榜(ZSet 类型):有序集合保留了集合不能有重复成员的特性(分值可以重复),但不同的是,有序集合中的元素可以排序,所以可以用作排行榜功能的实现;
- 此外利用 Redis 的特殊数据类型也可以实现一些相应的功能:Bit 位运算用来签到统计、HyperLogLogs 统计基数用来百万级网络 UV,还有 GEO 地理位置、用来打车等;
2、缓存
在日常对数据库的访问中,读操作的次数远超写操作,比例大概在 1:9
到 3:7
,所以需要读的可能性是比写的可能大得多的。当我们使用SQL语句去数据库进行读写操作时,数据库就会去磁盘把对应的数据索引取回来,这是一个相对较慢的过程。
如果我们把数据放在 Redis 中,既直接放在内存之中,让服务端直接去读取内存中的数据,那么这样速度明显就会快上不少,并且会极大减小数据库的压力,但是使用内存进行数据存储开销也是比较大的,限于成本的原因,一般我们只是使用 Redis 存储一些常用和主要的数据,比如用户登录的信息等。
一般而言在使用 Redis 进行存储的时候,我们需要从以下几个方面来考虑:
- 业务数据常用吗?命中率如何?如果命中率很低,就没有必要写入缓存;
- 该业务数据是读操作多,还是写操作多?如果写操作多,频繁需要写入数据库,也没有必要使用缓存;
- 业务数据大小如何?如果要存储几百兆字节的文件,会给缓存带来很大的压力,这样也没有必要。
在考虑了这些问题之后,如果觉得有必要使用缓存,那么就使用它!
使用 Redis 作为缓存的读取逻辑如下图所示:
从上图我们可以知道以下两点:
- 当第一次读取数据的时候,读取 Redis 的数据就会失败,此时就会触发程序读取数据库,把数据读取出来,并且写入 Redis 中;
- 当第二次以及以后需要读取数据时,就会直接读取 Redis,读到数据后就结束了流程,这样速度就大大提高了。
从上面的分析可以知道,读操作的可能性是远大于写操作的,所以使用 Redis 来处理日常中需要经常读取的数据,速度提升是显而易见的,同时也降低了对数据库的依赖,使得数据库的压力大大减少。
使用 Redis 作为缓存的写入逻辑如下图所示:
从流程可以看出,更新或者写入的操作,需要多个 Redis 的操作,如果业务数据写次数远大于读次数那么就没有必要使用 Redis。
3、高速读/写的场合
在如今的互联网中,越来越多的存在高并发的情况,比如天猫双11、抢红包、抢演唱会门票等,这些场合都是在某一个瞬间或者是某一个短暂的时刻有成千上万的请求到达服务器,如果单纯的使用数据库来进行处理,就算不崩,也会很慢的,轻则造成用户体验极差用户量流失,重则数据库瘫痪,服务宕机,而这样的场合都是不允许的!
所以我们需要使用 Redis 来应对这样的高并发需求的场合,我们先来看看一次请求操作的流程图:
我们来进一步阐述这个过程:
- 当一个请求到达服务器时,只是把业务数据在 Redis 上进行读写,而没有对数据库进行任何的操作,这样就能大大提高读写的速度,从而满足高速响应的需求;
- 但是这些缓存的数据仍然需要持久化,也就是存入数据库之中,所以在一个请求操作完 Redis 的读/写之后,会去判断该高速读/写的业务是否结束,这个判断通常会在秒杀商品为0,红包金额为 0 时成立,如果不成立,则不会操作数据库;如果成立,则触发事件将 Redis 的缓存的数据以批量的形式一次性写入数据库,从而完成持久化的工作。
三、Redis 安装
1、Windows 下安装
- 官方没有 Windows版本的 Redis
- Windows版本下载地址:https://github.com/MicrosoftArchive/redis/releases,下载 Redis-x64-3.2.100.msi
- 双击刚下载好的msi格式的安装包(Redis-x64-3.2.100.msi):
- 开始安装
- 选择“同意协议”,点击下一步继续:
- 选择“添加Redis目录到环境变量PATH中”,这样方便系统自动识别Redis执行文件在哪里:
- 端口号可保持默认的6379,并选择防火墙例外,从而保证外部可以正常访问Redis服务:
- 设定最大值为100M。作为实验和学习,100M足够了:
- 右击“计算机”>选择“管理”。在左侧栏中依次找到并点击“计算机管理(本地)”> 服务和应用程序 > 服务。再在右侧找到Redis名称的服务,查看启动情况。如未启动,则手动启动之。正常情况下,服务应该正常启动并运行了
- 最后来测试一下 Redis 是否正常提供服务。进入 Redis 的目录使用
cmd
。输入redis-cli
并回车。(redis-cli是客户端程序)如图正常提示进入,并显示正确端口号,则表示服务已经启动
- 实际测试一下读写。输入
set mykey1 "I love you all!"
并回车,用来保存一个键值。再输入get mykey1
,获取刚才保存的键值
2、Linux 下安装
- Redis官网 https://redis.io/ 直接点击下载得到:redis-6.2.4.tar.gz
- 把 redis-6.2.4.tar.gz 移动到/usr/local/ 目录下 :
mv redis-6.2.4.tar.gz /usr/local/
- 解压 redis-6.2.4.tar.gz :
tar -zxvf redis-6.2.4.tar.gz
- 由于redis是由C语言编写的,它的运行需要C环境,因此我们需要先安装gcc :
yum install gcc-c++
- 进入到 /usr/local/redis-6.2.4/ 目录下,进行编译与安装 :
cd redis-6.2.4
、make
、cd ./src
、 make install
- Redis不是默认后台启动的,需要求改一下配置文件:
vi redis.conf
将 daemonize
属性改为 yes
- 编辑 redis.conf配置文件,开启redis远程访问服务:
- 配置文件中的
bind 127.0.0.1
这一行给注释掉,这里的bind指的是只有指定的网段才能远程访问这个redis,注释掉后,就没有这个限制了 - 配置文件中的
protected-mode
设置成 no
(默认是设置成yes的, 防止了远程访问,在redis3.2.3版本后)
- 启动redis服务:
redis-server redis.conf
- 启动redis客户端服务:
redis-cli -p 6379
- 打开RedisDesktopManager,测试服务是否开启 以及 是否可以远程访问Redis\
3、Mac 下安装(使用Homebrew)
- 没有安装Homebrew,首先安装npm国内的吧,快一些:
/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"
; - 使用Homebrew安装命令:
brew install redis
; - 查看安装及配置文件位置:
- Homebrew安装的软件会默认在
/usr/local/Cellar/
路径下 - redis的配置文件
redis.conf
存放在/usr/local/etc
路径下
- 启动redis服务:
redis-server /usr/local/etc/redis.conf
或 brew services start redis
或 redis-server
- 查看redis服务进程:
ps axu | grep redis
- redis-cli连接redis服务,redis默认端口号6379,默认auth为空:
redis-cli -h 127.0.0.1 -p 6379