Redis学习笔记 01、知识概述及安装
@[toc]
前言
本篇博客是Redis的学习笔记,若文章中出现相关问题,请指出!
所有博客文件目录索引:博客目录索引(持续更新)
一、NoSQL数据库简介
NoSQL(Not only SQL。非关系型数据库)是为了解决性能问题而产生出来的技术。Redis就是NoSQL的一个应用。
目的:就是为了提高我们应用的性能。
使用场景
例如阿里巴巴初始阶段是php+mysql -> 多台mysql进行读写分离 -> 分库分表+水平拆分+mysql集群
- 之后会有中间件如sharding-jdbc、mycat数据库中间件进行分库分表。
一定要规避磁盘I/O与网络I/O,Redis将数据缓存在内存中,减少服务器磁盘IO
- 参考文章:网络IO和磁盘IO详解
1.1、技术发展及其解决痛点
技术的分类
1、解决功能性的问题:Java、Jsp、RDBMS、Tomcat、HTML、Linux、JDBC、SVN
2、解决扩展性的问题:Struts、Spring、SpringMVC、Hibernate、Mybatis
3、解决性能的问题:NoSQL、Java线程、Hadoop、Nginx、MQ、ElasticSearch
时代发展(出现的问题及缓存数据库解决带来的好处)
Web1.0
的时代,数据访问量很有限,用一夫当关的高性能的单点服务器可以解决大部分问题。
随着Web2.0
的时代的到来,用户访问量大幅度提升,同时产生了大量的用户数据。加上后来的智能移动设备的普及,所有的互联网平台都面临了巨大的性能挑战。
此时随着用户量的增多,流量急速上涨,此时不同服务器就会出现对应的压力,如下图:而缓存服务器则能够很有效的解决这些压力
①针对于服务器的CPU及内存压力
部署集群时,session的共享存储问题。
方案:
1. 存储到客户端,使用cookie存储。缺点:安全很难保证。
2、存在文件服务器或数据库里。缺点:大量的IO操作,效率低。
3. 进行session复制。将session复制到其他服务器上。缺点:造成空间极大浪费,数据冗余。
4.(较好突出方案) 使用缓存数据库,多个应用服务来访问同一个缓存数据库,通过从缓存数据库中查询来判断是否登陆。并且在内存中速度,数据结构简单。
核心:对于redis的数据就是存在内存中,读取的速度更快。
②针对于数据库服务的IO压力
解决IO压力。
若是不使用缓存数据库,原始方案会对数据库进行水平、垂直切分,读写分离。缺点:破坏一定的业务逻辑。
最好的方案是使用缓存数据库,将一些频繁查询的数据放到缓存数据库中,减少IO读操作,极大减小查询的速度。
1.2、NoSQL数据库
1.2.1、NoSQL数据库概述
概述
NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库。
主要分为四大类如下:
- key-value数据库:键值对形式存储,如
redis
。 - 列存储数据库:分布式存储海量数据,一个键指向多个列(如第一行五列,第二行一百列)。大数据中常用的有
HBase
(存储百万级的列,亿万级的行)。 - 文档型数据库:将结构化、半结构化的文档以特定格式存储如JSON格式。一个文档相当于关系型数据库中的一条记录,也是处理信息的基本单位,常用的有
MongoDB
。 - 图形数据库:使用图形理论来存储实体之间的关系信息,最主要的组成部分是节点集、连接节点的关系。例如java中的
Neo4j
,一般用于朋友圈社交网络,广告推荐!实际应用如脉脉。
NoSQL 不依赖业务逻辑方式存储,而以简单的key-value模式存储。因此大大的增加了数据库的扩展能力。
- 不遵循SQL标准。
- 不支持ACID:原子性(atomicity,或称不可分割性)、一致性(consistency)、隔离性(isolation,又称独立性)、持久性(durability)
- 远超于SQL的性能。
适用场景
对数据高并发的读写
海量数据的读写
对数据高可扩展性的
(用不着sql的和用了sql也不行的情况,请考虑用NoSql)
应用场景:秒杀功能,高并发的操作。
不适用场景
需要事务支持
基于sql的结构化查询存储,处理复杂的关系,需要即席查询。
1.2.2、Memcache
Memcache
:只支持字符串类型,并且用于与mysql辅助搭配来进行持久化的数据库。
1.2.3、Redis
1.2.4、MongoDB
MongoDB
:类似于json的格式,存储的数据可以更加复杂,能够支持二进制数据及大型对象存储。也就是支持的数据类型更多,尤其是JSON。
1.3、行列数据库
在大数据中,我们普通在数据库中的行式数据库(一条记录包含多个消息,存在一行上),就会存在很大的性能问题。
下面就是我们在数据库中正常存储的多条信息记录。
1.3.1、行数据库
在大数据中,由于需要获取大量的数据库,若是直接读取如上述表存储的数据,就需要进行一行一行一个个字段进行读,为了解决这个问题,就出现了行数据库
:将原本一行的信息数据存储在一个单元格中。
如下将一行信息存储为一个单元格记录。
性能说明:
- 针对于读取一条记录完整信息,性能提升 √ 如:
select *from users where id = 3
- 若是要提取一组中的聚合信息,如平均年龄,相当来说性能就不高了 x 如:
select avg(age) from users
上面举的sql例子是针对于原始数据库查询的角度来进行判定。
1.3.2、列数据库(概念+数据库举例)
概念介绍
简而言之就是将某一列数据来进行单独单元格的存储。
专门针对列来进行存储,好处就是若是对某个属性进行聚合操作就会有很大的优势,同样也有缺点,若是查询指定某条数据的各个信息,那就需要通过多条查询才能进行合并。
举例
查询年龄的平均值 快 (OLAP分析型处理)
查询id为3的人员信息 慢 (OLTP事务型处理)
总而言之两者的目的都是为了提高我们访问的效率,各自解决了对应的问题,这些是大数据时代产生的一些数据存储方式。
相关类型数据库
①Hbase
HBase是Hadoop项目中的数据库。它用于需要对大量的数据进行随机、实时的读写操作的场景中。
HBase的目标就是处理数据量非常庞大的表,可以用普通的计算机处理超过10亿行数据,还可处理有数百万列元素的数据表。
②Cassandra
Apache Cassandra是一款免费的开源NoSQL数据库,其设计目的在于管理由大量商用服务器构建起来的庞大集群上的海量数据集(数据量通常达到PB级别)。在众多显著特性当中,Cassandra最为卓越的长处是对写入及读取操作进行规模调整,而且其不强调主集群的设计思路能够以相对直观的方式简化各集群的创建与扩展流程。
1.4、图关系型数据库
主要应用:社会关系,公共交通网络,地图及网络拓谱(n*(n-1)/2)。
1.5、各大数据库排名
二、Redis概述安装
为何使用Redis?对于商品抢购情景、页访问量瞬间较大,若是都进行IO操作数据库会支撑不住导致瘫痪,最终导致宕机问题。使用NoSQL技术(基于内存的数据库),提供了一定的持久化。
Redis
是一个开源的key-value
存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。
-
Redis的QPS(每秒查询率)官方介绍有100000+。
-
默认有16个数据库,默认使用的是第0个数据库。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。
-
在此基础上,Redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。
区别的是Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件。(可以永久保存),并且在此基础上实现了master-slave(主从)同步。
优点:支持每秒是十几万次的读写操作,性能远超数据库。支持集群、分布式、主从同步,具有事务能力,保证高并发下数据的安全和一致性。
- 操作具有原子性,所有的操作都是原子操作,确保两个客户端并发访问时,Redis服务器能够接受最新的值。
应用场景:缓存(读远大于写)、需要高速读/写。
- 缓存:作为缓存时的读操作逻辑为读取数据时先读取Redis若是读取成功直接返回,失败则读取数据库并写入到Redis中;写操作逻辑是在更新/写入数据库后更新/写入Redis中,若是业务数据写次数远大于读次数就没有必要使用Redis。
- 考虑是否使用缓存问题:业务数据是否常用?命中率?读多?写多?业务数据量大不大?大的话像几百兆就没必要。
- 高速读/写:针对于某一瞬间或短暂时刻,有成千上万请求到达服务器。实际上就是当一个请求到了之后,将业务数据在
Redis
上进行读写,而不会对数据库有任何操作(能够大大提升读写速度,满足高速响应)。注意了对于在Redis
进行读写的数据依旧需要进行持久化处理,在一个请求操作完读/写后,会去判断该高速读写业务是否结束(例如在秒杀商品为0,红包金额为0时成立),若是不成立不会操作数据库,一旦成立就会将Redis
的缓存的数据批量的形式写入数据库,完成持久化操作。 - 缓存,消息队列(Redis 本地支持发布/订阅),应用程序中的任何短期数据,例如,web应用程序中的会话,网页命中计数等。
2.1、应用场景
①配合关系型数据库做高速缓存
- 高频次,热门访问的数据,降低数据库IO。
- 分布式架构,做session共享。(减轻CPU与内存压力)
②多样的数据结构存储持久化数据:
2.2、下载安装及使用
windows安装
1.1、下载安装
下载地址:redis、redis5点多版本
- Github的redis仓库:Github-redis
- windows下载地址:redis的windows版本
- windows的Redis的GUI:RedisDesktopManager
找到压缩包文件解压缩即可:
解压后,其中包含客户端、服务端
1.2、安装并启动Redis服务
一定要先开启
Redis
服务
安装服务:redis-server --service-install redis.windows.conf
- 默认服务名为redis。若是想要自定义服务名可以在后面添加
--service-name 服务名
。
安装好之后就是开启服务,你可以使用命令启动或者手动去开启,这里演示手动开启:
- 启动命令:
net start redis
。 - 停止命令:
net stop redis
。
其他命令:
- 卸载服务:
redis-server --service-uninstall --service-name 服务名
或sc delete 服务名
,若是默认服务名不用添加后面参数。
启动redis服务报错
1、creating server tcp listening socket 127.0.0.1:6379: bind No error
解决方案:
按顺序输入如下命令就可以连接成功
- redis-cli.exe
- shutdown
- exit
- redis-server.exe redis.windows.conf
1.3、启动redis服务器,使用redis客户端
需要打开两个cmd命令窗口:
- 第一个cmd输入
redis-server redis.windows.conf
,即可启动服务器; - 客户端连接:双击redis-cli.exe或者在另一个cmd中输入
redis-cli -h 127.0.0.1 -p 6379
。后面跟的是地址以及端口号。
1.4、redis的GUI工具
redis-desktop-manager
windows下载地址:https://github.com/uglide/RedisDesktopManager/releases/tag/0.9.3
下载双击安装即可
之后点击连接到服务器,选择指定的端口以及ip地址即可连接!
AnotherRedisDesktopManager(国人开发)
github地址:AnotherRedisDesktopManager
Linux
2.1、前提准备(配置)
前提条件是安装编译环境:
yum install gcc-c++
Redis官网:官网下载6.2.6稳定版。
①进入到/opt/software目录下通过wget来获取redis 6.2.0压缩包
# 获取redis6.2.0版本的压缩包 | wget:下载文件的工具,通过指定的链接下载。
wget http://download.redis.io/releases/redis-6.2.0.tar.gz
# 解压缩
tar -zxvf redis-6.2.0.tar.gz
②对redis目录中内容进行编译及安装
# 进入到解压缩的文件中
cd redis-6.2.0
# 编译及安装,执行的是两条命令,执行好之后src中就有对应的可执行文件了
make & make install
make
是编译的意思。就是把源码包编译成二进制可执行文件。- 由于
redis
已经自己写好了make file 了;也就是说不用再执行configure 了,make 后编译好的文件会保存到src目录下。
- 由于
make install
:安装。- 可参考文章:make 和 make install 的区别
③将redis
对应的可执行文件复制到/usr/local/redis/bin
目录下
# 进入到redis-6.2.0/src目录下
cd src
# 在/usr/local目录下创建多级目录redis/bin
mkdir -p /usr/local/redis/bin
# 将redis/src目录下的对应可执行文件拷贝到/usr/local/redis/bin/下
cp redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-sentinel redis-server redis-trib.rb /usr/local/redis/bin/
④永久添加/usr/local/redis/bin/
到环境变量
vim /etc/profile
# 编辑/etc/profile文件,将/usr/local/redis/bin添加到环境变量中,保存退出
export PATH=/usr/local/redis/bin:$PATH
# 刷新配置文件,另其生效
source /etc/profile
⑤修改redis的配置文件redis.conf
(配置文件在/opt/software/redis6.2.0
目录下,就是解压缩后的目录中)
# 进行编辑文件
vim /opt/software/redis6.2.0/redis.conf
# 需要编辑的配置项为下面内容
protected-mode no # 关闭保护模式,允许外网访问
bind 0.0.0.0 # 允许任何ip地址访问(仅供测试,实际上指定本地地址以及集群ip地址),或者直接注释掉
requirepass 123456 # 设置密码
daemonize yes # 启动redis服务器时在后台运行
# 保存退出
:wq
# 为了方便之后进行操作将redis.conf也复制到/usr/local/redis/bin目录下(主要是之后启动服务器需要读取)
cp /opt/software/redis-6.2.0/redis.conf /usr/local/redis/bin/
安装成功:
进入到/usr/local/bin目录下查看的内容
文件介绍:
redis-benchmark:性能测试工具,可以在自己本子运行,看看自己本子性能如何
redis-check-aof:修复有问题的AOF文件,rdb和aof后面讲
redis-check-dump:修复有问题的dump.rdb文件
redis-sentinel:Redis集群使用
redis-server:Redis服务器启动命令
redis-cli:客户端,操作入口
2.2、启动服务器以及使用客户端连接
注意:上面配置了redis的密码123456,启动客户端时需要输入密码。
启动服务器:
# 进入到刚刚复制的可执行文件到的指定目录中
cd /usr/local/redis/bin
# 启动redis服务器并读取redis.conf(即配置文件)
./redis-server redis.conf
# 或者任意目录下执行启动命令。我们make install会将编译好的c进行安装,接着会将一些执行文件安装在/usr/local/bin目录中,这个redis-server就在其中
# 缺点:一旦我们ctrl+c或者关闭连接,redis服务就会直接停止掉,不推荐使用。
redis-server
说明:由于我们之前在配置文件中配置了后台运行所以启动后没有任何信息展示,若是想要关闭服务器就需要找到指定的pid进程号,需要使用命令netstat -nlp | grep redis
配置后台启动方式(额外):
通过让redis-server读取指定配置文件的方式来让redis进行后台启动,想要这样的效果,那么我们就需要对该配置文件进行修改。
# 保险起见,我们将redis的配置文件复制一份到/etc目录下,之后来修改/etc目录下的文件即可
# ps:redis.conf默认在安装目录下,我们也可以直接对其文件内容进行修改,不过最好备份一份原始的配置文件
cd /opt/redis-6.2.6
cp redis.conf /etc/redis.conf
# 进入到配置文件复制的位置进行修改redis.conf,这里我们直接来对拷贝后的文件进行修改
vim /etc/redis.conf
# 核心内容:修改redis.conf中的后台启动设置,将原本的no改为yes,表示后台启动
daemonize yes
# 本质还是通过redis-server来启动,不过会多一个操作就是启动的同时先读取redis.conf中的配置内容再启动
redis-server /etc/redis.conf # 这里redis-server随便哪个文件都可以执行,因为配置好了/usr/local/bin环境变量,后面的就是对应redis配置文件的路径
后台方式启动,不会有任何的提示信息,我们可以通过ps -ef命令来进行查看
测试是否连通:
停止redis服务器
停止redis服务方式:
1、执行redis-cli命令,进入客户端连接,接着执行shutdown即可停止redis服务,接着使用exit退出客户端连接。
2、直接执行命令:redis-cli shutdown
3、杀死进程方式(根据指定pid进程号):kill -9 15129
查看redis是否停止服务:此时就表示redis的服务不在运行中。
启动客户端
# 进入到刚刚复制的可执行文件到的指定目录中
cd /usr/local/redis/bin
# 启动客户端(本地连接不需要输入密码)
./redis-cli
输入exit后即可退出!
2.3、GUI(Redis Desktop Manager)连接远程Redis
在linux下开启防火墙端口
systemctl status firewalld # 查看防火墙状态
firewall-cmd --zone=public --list-ports # 查看防火墙所有开放的端口,看一下是否开启了6379端口(即redis服务器运行的端口)
firewall-cmd --zone=public --add-port=6379/tcp --permanent # 永久开启6379端口
firewall-cmd --reload # 让刚刚开启的端口操作生效
进行连接操作…
出现下面情况时表示连接成功!
注意:使用Xshell连接linux使用redis需要输入密码(若是使用密码的话),命令为./redis-cli -a 123456
,否则进行redis的内部操作时会报错NOAUTH Authentication required。
2.3、redis相关概述及基本命令
概述
端口6379:来源与一个女明星Alessia Merz,6379对应非智能手机键盘上Merz四个单词的位置。
默认16个数据库,类似数组下标从0开始,初始默认使用0号库。
统一密码管理,所有库同样密码。
底层应用技术
Redis是单线程+多路IO复用技术
。
- 多路复用是指使用一个线程来检查多个文件描述符(Socket)的就绪状态,比如调用select和poll函数,传入多个文件描述符,如果有一个文件描述符就绪,则返回,否则阻塞直到超时。得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启动线程执行(比如使用线程池)
接下来我们来进行比对一下三种处理操作:
# 串行:程序串行执行,若是有阻塞情况,程序会一直等待阻塞结束才会往下执行。
# 多线程+锁:这是Memcache执行操作
# 单线程+多路IO复用:redis执行操作
串行 vs 多线程+锁(memcached) vs 单线程+多路IO复用(Redis)
Redis
比对Memcache
三点不同: 支持更多数据类型,支持持久化,单线程+多路IO复用。
- 多路复用:只在一个进程里处理多个文件的 I/O。Linux 下有三种提供 I/O 多路复用的 API,分别是: select、poll、epoll。epoll 支持边缘触发和水平触发的方式,而 select/poll 只支持水平触发。
- select/poll/epoll 是如何获取网络事件的呢?在获取事件时,先把所有连接(文件描述符)传给内核,再由内核返回产生了事件的连接,然后在用户态中再处理这些连接对应的请求即可。
- 相关文章(IO多路复用):IO 多路复用是什么意思?
- I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态(对应空管塔里面的Fight progress strip槽)来同时管理多个I/O流。使用对应的数据结构来记录事件。
相关命令
selct index
:切换选择使用第几个数据库,从o开始,默认有16个,也就是[0,15]。示例:select 15
。
dbsize
:查看当前数据库有多少key。
flushdb
:清空当前数据库。
flushall
:清空所有数据库。
exipre 键 秒数
:设置指定名在指定秒数后过期。
ttl 键
:查看当前键的剩余时间。当前永久存在为(integer) -1,若是有过期时间会显示即将过期时间。
keys *
:获取当前数据库中的所有的值。
2.4、性能测试
redis-benchmark测试redis性能
测试:redis-benchmark -n 100000 -q
Java中测试Redis写操作
引入jedis的jar包:Maven-Repository
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.0</version>
</dependency>
<!-- slf4j门面日志并使用其自带日志实现类 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.5</version>
</dependency>
- 使用slf4j简单日志是为了防止报错。
测试程序:
/**
* @ClassName Test
* @Author ChangLu
* @Date 2021/4/24 16:27
* @Description TODO
*/
public class Test1 {
@Test
public void test(){
Jedis jedis = new Jedis("localhost", 6379, 10000);
int i= 0;
try {
long begin = System.currentTimeMillis();
while(true){
long end = System.currentTimeMillis();
if(end-begin >= 1000){
break;
}
i++;
jedis.set("test"+i, String.valueOf(i));
}
}finally {
jedis.close();
}
System.out.println("写入"+i+"次!");
}
}
- 点赞
- 收藏
- 关注作者
评论(0)