Redis 是一个高性能的key-value数据库,非关系型数据库。
NoSQL概述
NoSQL,泛指非关系型的数据库。随着互联网web2.0网站的兴起,传统的关系数据库在处理web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,出现了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,特别是大数据应用难题。摘自百度百科
为什么要使用NoSQL
- 单机mysql的年代:
90年代,一个基本的网站访问量一般不会太大,单个数据库完全足够!那个时候,更多的去使用静态网页html,服务器根本没有太大的压力。
瓶颈:
- 数据量太大,一个机器放不下了
- 数据的索引(B+Tree),一个机器内存也放不下
- 访问量(读写混合),一个服务器承受不了
- Memcached(缓存)+ MySQL + 垂直拆分(读写分离)
网站80%的情况都是在读,每次都要去查询数据库的话就十分麻烦!所以说我们希望减轻数据库的压力,我们可以使用缓存来保证效率!
发展过程:优化数据结构和索引 –> 文件缓存(IO)–> Memcached(当时最热门的技术)
- 分库分表 + 水平拆分 + MySQL集群
技术和业务在发展的同时,对人的要求也越来越高!
本质:数据库(读,写)
早些年MyISAM:表锁,十分影响效率!高并发下就会出现严重的锁问题
转战lnnodb:行锁
慢慢的就开始使用分库分表来解决写的压力!MySQL
在那个年代推出了表分区!这个并没有多少公司使用!
MySQL的集群,很好满足哪个年代的所有需求!
- 如今的年代
2010-2020 十年之间,世界已经发生了翻天覆地的变化;(定位,也是一种数据,音乐,热榜!)
MSQL 等关系型数据库就不够用了!数据量很多,变化很快~
MySQL有的使用它来存储一些比较大的文件,博客,图片!数据库表很大,效率就低了!如果有一种数据库来专门处理这种数据,MySQL压力就变得十分小(研究如何处理这些问题!)大数据的IO压力下,表几乎没法更大!
目前一个基本的互联网项目!
为什么要用NoSQL
用户的个人信息,社交网络,地理位置。用户自己产生的数据,用户日志等等爆发式增长!
这时候我们就需要使用NOSQL数据库的,Nosql 可以很好的处理以上的情况!
什么是NoSQL
NOSQL= Not Only SQL(不仅仅是SQL)
关系型数据库:表格,行,列
泛指非关系型数据库的,随着veb2.0互联网的诞生!传统的关系型数据库很难应对web2.0时代!尤其是超大规模的高并发的社区!暴露出来很多难以克服的问题,NOSQL在当今大数据环境下发展的十分迅速,Redis是发展最快的,而且是我们当下必须要掌握的一个技术!
很多的数据类型:用户的个人信息、社交网络、地理位置等。这些数据类型的存储不需要一个固定的格式!不需要多月的操作就可以橫向扩展的!Map<String,object>使用键值对来控制!
NoSQL特点
解耦!
1、方便扩展(数据之间没有关系,很好扩展!)
2、大数据量高性能(Redis
一秒写8万次,读取11万,NoSQL的缓存记录级,是一种细粒度的缓存,性能会比较高!
3、数据类型是多样型的!(不需要事先设计数据库!随取随用!如果是数据量十分大的表,很多人就无法设计了!)
4、传统
RDBMS 和 NoSQL
1 |
传统的 RDBMS(Relational Database Management System,关系数据库管理系统) |
1 |
Nosql |
了解:3v+3高
大数据时代的3V:主要是描述问题的
- 海量Volume
- 多样Variety
- 实时Velocity
大数据时代的3高:主要是对程序的要求
- 高并发
- 高可扩
- 高性能
真正在公司中的实践:NOSQL + RDBMS 一起使用才是最强的!
技术没有高低之分,就看你如何去使用!(提升内功,思维的提高!)
阿里巴巴演进分析
思考问题:这么多东西难道都是在一个数据库中的吗?
技术急不得,越是慢慢学 ,才能越扎实!
开源才是技术的王道!
任何一家互联网的公司,都不可能只是简简单单让用户能用就好了!
大量公司做的都是相同的业务;(竞品协议)
随着这样的竞争,业务是越来越完善,然后对于开发者的要求也是越来越高!
没有什么是加一层解决不了的!
1 |
# 1、商品的基本信息 |
要知道,一个简单地网页背后的技术一定不是大家所想的那么简单!
大型互联网应用问题:
- 数据类型太多了!
- 数据源繁多,经常重构!
- 数据要改造,大面积改造?
解决问题:
这里以上都是NoSQL入门概述,不仅能够提高大家的知识,还可以帮助大家了解大厂的工作内容!
NoSQL的四大分类
KV键值对:
新浪:Redis
美团:Redis + Tair
阿里、百度:Redis + memecache
文档型数据库(bson格式 和json一样):
- MongoDB (一般必须要掌握)
- MongoDB是一个基于分布式文件存储的数据库,C++ 编写,主要用来处理大量的文档!
- MongoDB 是个介于关系型数据库和非关系型数据中的产品!MongoDB 是非关系型数据库中功能最丰富,最像关系型数据库的!
- ConthDB
列存储数据库:
- HBase
图形关系数据库:
- 不是存图形的,放的是关系。比如:朋友圈社交网络,广告推荐。
- Neo4j,InfoGrid
四者对比
敬畏之心可以使人进步!
Redis入门
概述
Redis是什么?
Redis(Remote Dictionary Server ),即远程字典服务。
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
免费和开源!是当下最热门的
NoSQL技术之一!也被人们称之为结构化数据库!
Redis 能干嘛?
1、内存存储、持久化,内存中是断电即失、所以说持久化很重要 (rdb、aof)
2、效率高,可以用于高速缓存
3、发布订阅系统
4、地图信息分析
5、计时器、计数器(浏览量!)
6、……
特性
1、多样的数据类型
2、持久化
3、集群
4、事务
5、……
学习中需要用到的东西
下载地址:http://www.redis.cn/download.html
注意:Windows版本在Github上下载(停更很久了),Redis推荐在Linux系统上搭建。
Mac OS安装
参考官网”下载页面”下方的文档:http://www.redis.cn/download.html
进入要保存redis源程序的路径,使用下边的命令下载redis:
wget http://download.redis.io/releases/redis-6.0.6.tar.gz
扩展:建议使用homebrew安装redis,见后边笔记。
解压
解压Redis的安装包!程序一般放在/opt
下
1 |
暂时申请root权限(输入当前用户的密码): |
编译&安装
1 |
编译redis(进入解压后的文件目录后): |
Redis的默认安装路径/usr/local/bin
(用户自行编译安装时默认的可执行程序的安装位置)
homebrew安装
笔记摘自:
- 安装homebrew。笔记见这篇博客。
- homebrew安装redis
注意:使用homebrew安装的软件,会安装在homebrew的根目录下(一般是/opt/homebrew)。所以redis的相关文件也在其中。本案例中,redis的配置文件在/opt/homebrew/etc/redis.conf
使用homebrew安装redis。
1 |
命令安装 |
配置
可执行程序,在
/usr/local/bin
- 使用homebrew安装的redis,在/opt/homebrew
/bin
- 使用homebrew安装的redis,在/opt/homebrew
相关的配置文件,在
/opt
- 使用homebrew安装的redis,在/opt/homebrew
/etc/redis.conf
- 使用homebrew安装的redis,在/opt/homebrew
扩展:opt有可选的意思,用来安装附加软件包。是用户级的程序目录,安装到/opt目录下的程序,它所有的数据、库文件等等都是放在同个目录下面。
在/usr/local/bin
下新建一个myconf文件夹,专门保存程序运行所需要的配置文件。将opt下的redis默认配置文件拷贝一份到myconf文件夹中,初始配置文件redis.conf,一般在/opt/redis-版本号/redis.conf
。
1 |
//创建myconf文件夹,并将配置文件redis.conf备份到该文件夹下: |
后台启动
1.Redis默认不是后台启动的,需要修改配置文件。(注意:homebrew下载的redis,使用brew命令启动,默认是以守护线程的方式运行)
1 |
//使用vim编辑器打开配置文件: |
protected
protected模式,会导致远程连接失败。在学习阶段可将protected模式关闭,方便远程连接。
2.关闭protected模式,以使用redis可视化工具redisinsight连接虚拟机中的redis服务器。
1 |
1.修改配置文件 |
配置文件详细介绍,见后边的笔记。
启动&关闭
启动Redis服务
1 |
会默认调用/usr/local/bin路径下的redis-server程序(默认安装路径),后边指定配置文件(前边配置后台方式运行的默认配置文件,这种启动方式不会打印出redis的logo) |
测试:
1 |
使用内置的客户端与Redis进行交互(会默认调用/usr/local/bin路径下的redis-cli程序),后边指定已启动的Redis服务的端口号,默认为6379(以配置文件中的为准) |
1 |
#命令ps查看启动的redis服务进程: |
测试性能
redis-benchmark 是一个压力测试工具!官方自带的性能测试工具!
命令参数:
测试:
1 |
#测试本地redis服务(默认6379端口),100个并发,10万条请求 |
基础的知识
redis默认有16个数据库。
1 |
#打开配置文件 |
默认使用的是第0个(0-15)。
1 |
qingshuaidebaoluo@qingshuaidebaoluoMac-mini ~ % redis-cli |
Redis是单线程的
明白Redis是很快的,官方表示,Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了!
Redis
是C 语言写的,官方提供的数据为 100000+ 的QPS(每秒查询率),完全不比同样是使用key-vale的Memecache差!
Redis 为什么单线程还这么快?
- 误区1:高性能的服务器一定是多线程的?
- 误区2:多线程(CPU上下文会切换!)一定比单线程效率高!
- 先去CPU>内存>硬盘的速度要有所了解!
核心:redis 是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!!!),对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个CPU上,在内存情况下,这个就是最佳的方案!
五大数据类型
官网文档
全段翻译:
Redis 是一个开源( BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件MQ。它支持多种类型的数据结构,如
字符串(strings),散列(hashes),列表 (lists),集合(sets),有序集合 ( sorted sets)与范围查询,bitmaps,
hyperloglogs 和 地理空间( geospatial)索引半径查询。Redis 内置了 复制 (replication),LUA脚本 ( Luascripting),
LRU驱动事件( LRUeviction),事务(transactions) 和不同级别的磁盘持久化(persistence),并通过
Redis哨兵(Sentinel)和自动分区 ( Cluster)提供高可用性 (high availability )。
Redis Key
键值对&数据库
- keys命令,查看所有的key
- flushdb命令,清空当前数据库中的所有 key
- flushall命令,清空所有数据库中的 key
- set命令,添加一个key/value键值对
- get命令,查看value
- move命令,移动键值对到其他数据库
- select命令,切换数据库(默认16个,0-15)
1 |
启动redis,并指定配置文件: |
过期时间
- exists命令,查看是否存在某个key
- expire命令,设置过期时间
- persist命令,移除过期时间
- ttl命令,查看过期时间
1 |
查看是否存在key为name的键值对,1-存在,0-不存在 |
类型&删除
- type命令,查看value的类型
- del命令,删除键值对
1 |
查看类型 |
String
追加&长度
- append命令,往value值后边追加字符(拼接到后边)
- strlen命令,获取value的长度(string length ==> strlen)
1 |
查看redis服务是否已启动 |
数值增减
incr命令,数字值增一(increase,增加)
decr命令,数字值减一(decrease,减少)
incrby命令,增加指定的数量
decrby命令,减少指定的数量
1 |
添加一个键值对。key为num,value为0 |
范围
- getrange / substr命令,获取value中某个范围内的字符(头尾索引都包括)
- setrange命令,替换指定位置开始的字符串(注意是替换不是插入)
1 |
查看key为name的键值对的value |
新建相关
- setex命令,set with expire,添加键值对与设置过期时间
- setnx命令,set if not exist,键值对不存在则新建(在分布式锁中会常常使用)
- mset命令,同时创建多对键值对
- msetnx命令,如果不存在,则创建键值对(同时创建多对)。原子性操作,要么全部创建,要么全部不创建。
1 |
查看所有key |
- getset命令,先获取再设置(键值对),不存在则返回nil
1 |
如果不存在值,则返回nil |
应用场景
value除了可以是字符串还可以是数字!
- 计数器
- 统计多单位的数量
- 粉丝数
- 对象缓存存储
案例
- 利用mset命令、mget命令,保存、获取对象。(对比Hash中的案例,比较两者的优缺点)
1 |
一般用法:使用set、get命令保存对象 |
总结:Hash更适合对象信息的保存(经常变动),String更适合字符串存储。
List
List列表,基本的数据类型,有序可重复。在redis里面,我们可以把list当作栈、队列、阻塞队列使用。
所有的list命令都是L开头的。(命令不区分大小写)
基本命令
将List看作是一个横着放的容器,添加元素时从左边放入,取出时从右边取
Lpush命令,L - list,push - 推进,往list中添加元素(同时创建list)。L也可以看作是left,即从左边推进(添加)元素(一个或多个元素)。
Rpush命令,R - right,push - 推进,往list的右边推进(添加)元素
Lrange命令,L - list,range - 范围,根据索引获取list中某段范围内的元素。用法与前边的getrange类似。
1 |
切换到数据库2(默认16个,0-15) |
移除
- Lpop命令,L - list,pop - 移除(爆裂),从list中移除元素。L也可以看作是left,从list的左边开始移除元素(一个或多个)
- Rpop命令,R - right,pop - 移除,从list的右边开始移除元素
1 |
查看名为list1的list中的所有元素 |
- Lrem命令,L - list,也可以看作是left,rem - remove移除。移除指定的值,从左边开始 移除list中的n个 指定的元素
1 |
新建mylist,同时往其中添加元素one two three four two one(list中可以有重复的元素) |
- Ltrim命令,L - list,也可以看作left,trim - 修剪。截取保留指定索引的元素,修剪掉其他元素。
1 |
清空当前数据库 |
- rpoplpush命令,由rpop命令与lpush命令组成。先将列表最后一个元素移除,再将其移到其他列表中
1 |
新建列表mylist |
添加
- Lset命令,L - list,set - 添加元素(参考前边的set命令)。向list中指定索引位置添加元素,前提是存在该list(使用keys *命令或exists命令查看是否存在某个list)
1 |
查看mylist02中全部的元素 |
- Linsert命令,L - list,insert - 插入,往list中插入元素。
1 |
创建名为mylist的list,并往其中添加元素0 1 2 3 4 |
索引&长度
- Lindex命令,L - list,index - 索引,根据索引获取list中的某个元素
- Llen命令,L - list,len - length长度,获取list的长度
1 |
新建list1,同时往其中添加元素1到10 |
小结
- 他实际上是一个链表,before Node after , left ,right 都可以插入值
- 如果key 不存在,创建新的链表
- 如果key存在,新增内容
- 如果移除了所有值,空链表,也代表不存在!
- 在两边插入或者改动值,效率最高!中间元素,相对来说效率会低一点~
应用场景:消息排队,消息队列 (Lpush Rpop),栈( Lpush Lpop)等。
Set
set集合。set中的值是不能重复的(无序不可重复)。
基本命令
- sadd命令,s - set集合,add - 添加元素。往set集合中添加元素,若该集合不存在则创建集合并添加元素,若存在则往其中添加元素。(可同时添加多个)
- smembers命令,s - set集合,members - 成员的复数即很多的成员。查看set集合中的所有元素
- sIsMember命令,s - set集合,is - 是否存在,member - 成员即set集合中的元素。判断某set集合中是否存在某一元素
- scard命令,s - set集合,card - 卡片、梳理。获取set集合中的元素个数
1 |
往名为myset的set集合中添加元素1314,若不存在集合myset则创建再往其中添加元素 |
移除
- srem命令,s - set集合,rem - remove移除。移除集合中的某个元素
- spop命令,s - set集合,pop - 砰,可理解为移除。随机移除集合中的一个元素
- smove命令,s - set集合,move - 移动。将元素从一个集合移动到另一个集合中
1 |
移除集合中的元素1314 |
获取
- srandmember命令,s - set集合,rand - random随机,member - 成员。随机获取集合中的元素
- sDiff命令,s - set集合,diff - difference差异。求两集合的差集
- sInter命令,s - set集合,inter - intersection交叉。求两集合的交集
- sUnion命令,s - set集合,union - 联盟。求两集合的并集
1 |
往myset集合中添加元素 520与1314 |
应用场景
微博,A用户将所有关注的人放在一个set集合中!将它的粉丝也放在一个集合中!
共同关注,共同爱好,二度好友,推荐好友!(六度分割理论)
Hash(哈希)
Hash是一个 string类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。(field - value映射可以看作是键值对key - value)
创建
- hset命令,h - hash,set - 创建/添加。创建一个Hash,并往其中添加键值对(一个或多个)
- hsetnx命令,set if not exist,键值对不存在则新建
1 |
清空所有数据库 |
获取
- hget命令,h - hash,get - 获取。获取Hash中的某个field的value值
- hmget命令,h - hash,m - more多个,get - 获取。获取Hash中的多个field的value值
- hgetall命令,h - hash,get - 获取,all - 全部。获取Hash中的全部的键值对(键与值,field与value)
- hlen命令,h - hash,len - length长度。获取某个Hash的长度(多少对键值对)
- hkeys命令,h - hash,keys - 所有的key。获取某个Hash的所有key(即键值对的键 - key)
- hvals命令,h - hash,vals - 所有的value。获取某个Hash的所有value(即键值对的值 - value)
1 |
|
删除
- hdel命令,h - hash,del - delete删除。根据field,删除Hash中的某个键值对
1 |
删除 名为myhash2的Hash中的 field为key1的键值对 |
数值增减
与String中的incrby相似,但又不同。这里增减都是使用一个命令
- hincrby命令,增加指定的数量(使用负数,实现减少效果)
1 |
127.0.0.1:6379> hset myhash f5 5 |
案例
- 利用mset命令、mget命令,保存、获取对象。(对比String中的案例,比较两者的优缺点)
1 |
使用hset创建一个名为user:1的Hash(以"类名:id"的方式命名)保存对象信息,将对象的属性以键值对的形式保存在Hash中 |
总结:Hash更适合对象信息的保存(经常变动),String更适合字符串存储。
Zset
Zset - 有序集合。在set的基础上增加了一个值。可用于排序。
1 |
set k1 v1 |
创建
- zadd命令,z - zset,add - 添加。添加一个或多个值。
1 |
创建名为myset的zset,添加一个值,key为myset,value为one,score为1 |
获取
获取值
- zrange命令,z - zset,range - 范围。根据索引范围获取值。(0 到 -1,获取全部)
1 |
获取全部值 |
获取值&排序
- zrangebyscore命令,score - 成绩、分值。根据score的值,从小到大排序。
- zrangebyscore zset名称 区间-小 区间-大
- zrevrangebyscore命令,rev -
reverse,颠倒。根据score的值,从大到小排序。
- zrevrangebyscore zset名称 区间-大 区间-小
- withscores,获取zset中的数据并带上对应的score
- -inf 无穷小,+inf 无穷大
1 |
根据score的值,从小到大排序,区间是无穷小到无穷大即所有数据 |
获取数量
- zcard命令,card - 卡片、梳理。获取集合中的元素个数。
- zcount命令,count - 数量。查看指定区间的元素数量。
1 |
清空数据库 |
删除
- zrem命令,rem - remove,移除。移除指定的元素(zset,有序不重复集合)
1 |
移除指定的元素(zset,有序不重复集合) |
应用场景
思路:set排序,存储班级成绩表,工资表排序。
普通消息,1,重要消息,2,带权重进行判断。
排行榜应用实现,取Top N测试。
三种特殊数据类型
geospatial
geospatial,地理位置。geo可用于推算地理位置的信息,两地之间的距离,方圆几里的人。
应用场景:朋友的定位,附近的人,打车距离计算。
创建
- geoadd命令,geo - geospatial,地理位置,add - 添加。添加一个或多个值(经纬度)。
1 |
geoadd |
获取
- geopos命令,获取当前定位(经纬度,先经度再纬度)
- geodist命令,dist - 距离。计算两地之间的距离。(假设地球是完美的球形)
- georadius命令。详情见下边。
- georadiusbymember命令。详情见下边。
- geohash命令。详情见下边。
1 |
:输入地名(前边设置的value,值),返回 |
- georadius命令,radius - 半径。以给定的经纬度为中心,找出某一半径内的元素。
1 |
获取 以给定的经纬度(110 30)为中心,1000km内的元素 |
- georadiusbymember,radius - 半径,bymember - 基于member(前边添加值时,一个值包括“经度、纬度、名称member”)。找出位于指定元素周围的其它元素。
1 |
以 beijin 为中心,获取半径为1000km的范围的其它元素 |
- geohash命令,hash - 哈希。返回一个或多个位置元素的geohash表示。将二维的经纬度转化为一维的字符串,如果两个字符串越接近,那么距离越近。
1 |
获取 beijin 与 chongqin 的位置信息的哈希表示 |
删除
- geo底层的实现原理其实就是zset,我们可以使用zset命令来操作geo。例如使用“zrem”来删除geospatial中的元素
1 |
使用type命令查看其属于5大数据类型中的哪一种,即可以使用对应的命令来操作 |
hyperloglog
redis hyperloglog 基数统计的算法。redis2.8.9版本开始支持。
优点:占用的内存是固定,2^64不同的元素的基数,只需要12KB内存,如果要从内存角度来比较的话hyperloglog 是首选。
A{1,3,5,7,9}、B{1,3,5,9}
基数(不重复的元素) = 4,可以接受误差
应用场景:网页的UV(一个人访问一个网站多次,但是还是算作一个人)。传统的方式,set保存用户的id,然后就可以统计set中的元素数量作为标准判断。这个方式如果保存大量的用户id,就会比较麻烦!我们的目的是为了计数而不是保存用户id。
0.81%错误率,统计UV任务,可以忽略不计的。
- PFadd命令,创建。
- PFcount命令,统计。
- PFmerge命令,合并。
1 |
创建第一组元素 mykey |
bitmaps
bitmaps,位存储。
应用场景:统计疫情感染人数:0 0 0 1。统计用户信息,活跃,不活跃。登录,未登录。两个状态的,都可以使用Bitmaps
- setbit命令,创建
- setbit key offset value
- getbit命令,查看
- bitcount命令,统计
1 |
案例:使用bitmaps来记录周一到周日的打卡情况。 |
事务
Redis事务本质:一组命令的集合。一个事务中的所有命令都会被序列化,在事务执行过程中会按照顺序执行。一次性、顺序性、排他性。
1 |
---- 队列 set set set 执行 ---- |
Redis事务没有隔离级别的概念。
所有的命令在事务中,并没有直接被执行。只有发起执行命令的时候才会执行,Exec
注意:Redis单条命令是保证原子性的(要么都执行要么都不执行),但是事务不保证原子性。
Redis的事务:
- 开启事务 - multi
- 命令入队 - …
- 执行事务 - exec
锁:redis可以实现乐观锁,watch
执行事务
正常执行事务:
1 |
开启事务 |
放弃事务
discard,放弃事务
1 |
开启事务 |
编译型异常
编译型异常(代码有问题,redis命令拼写错误,能在编译时检测到),事务中所有的命令都不会被执行
1 |
清空数据库 |
运行时异常
运行时异常,如果事务队列中存在语法性错误(redis命令的拼写没有错误,使用上有错误,只有在运行时才会发现),那么执行命令的时候,其它命令是可以正常执行的。
1 |
清空数据库 |
监控,watch。面试常问
悲观锁:认为什么时候都会出问题,无论做什么都会加锁
乐观锁:
认为什么时候都不会出问题,不会上锁。更新数据的时候去判断一下,在此期间是否有人修改过这个数据
获取version
更新的时候比较version
Redis监听案例:
正常执行:
1 |
添加两个key/value,用于记录 总金额 和 支出金额 |
测试多线程修改值,使用watch可以当作redis的乐观锁操作。
- watch key名,监听(加乐观锁)
- 事务执行结束后(失败或成功都会)自动解除监听(解锁)
- unwatch,解除监听(手动解锁)
1 |
模拟进程1: |
Jedis
Jedis,使用Java来操作Redis。
Jedis是官方推荐的java连接开发工具,使用java操作redis中间件。
1、导入依赖
1 |
<!-- https://mvnrepository.com/artifact/redis.clients/jedis --> |
2、编码测试
- 连接数据库
- 操作命令
- 断开连接
案例
1 |
|
事务操作
1 |
|
SpringBoot整合
jedis -> lettuce
SpringBoot操作数据:springData,jpa、jdbc、mongodb、redis
SpringBoot是和SpringData齐名的项目
说明:在SpringBoot 2.x之后,原来使用的jedis被替换为了lettuce。
jedis:采用直连,多个线程操作的情况是不安全的,如果要避免需使用jedis pool连接池,更像BIO模式。
lettuce:采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况。可以减少线程数据,更像是NIO模式。
SpringBoot整合:
- 导入依赖
- 配置连接
- 测试
导入依赖
1 |
<!-- spring-boot整合redis --> |
配置连接
源码分析
1 |
//maven的autoconfigure包下的spring.factories中找到redis的auto配置类,使用注解”@EnableConfigurationProperties“绑定的文件就是redis的配置文件,通过配置文件我们可以了解到redis哪些参数可配置 |
配置:
1 |
#application.properties文件 |
测试
1 |
|
问题
问题:出现乱码。
原因是默认序列化配置导致,默认序列化方式是JDK序列化,会对字符串进行转义。要解决这个问题,需要自定义配置类,使用redis提供的序列化实现(有string、json等)。
自定义配置类
自定义配置类,替换默认的redisTemplate。类RedisConfig中,返回bean,名为myRedisTemplate。
1 |
package com.qsdbl.nazox_demo.configuration; |
使用&测试
指定使用我们自定义的myRedisTemplate
1 |
|
定义工具类
自定义redis工具类,简化redisTemplate的使用。
1 |
package com.qsdbl.nazox_demo.utils; |
使用&测试
1 |
|
Redis.conf详解
建议:启动redis时使用我们自定义的配置文件(多了解一下redis配置)
1 |
使用homebrew安装的redis,配置文件为/opt/homebrew/etc/redis.conf。 |
配置后台启动、关闭protected模式,见前边的笔记。
单位
1 |
配置文件unit单位,对大小写不敏感 |
包含
通用的配置文件,可使用include包含
1 |
include /path/to/local.conf |
网络
1 |
绑定的ip(默认只允许本机连接) |
通用
1 |
开启保护模式 |
快照
redis是内存数据库,如果不进行持久化操作,那么数据断电就丢失。
持久化,在规定时间内执行了多少次操作,会持久化到文件.rdb.aof
1 |
Save the DB to disk. |
安全
可以设置密码,默认没有
1 |
启动redis客户端,连接redis服务器 |
限制
限制客户端
1 |
最大连接数量 |
aof
append only模式,aof
1 |
默认不开启,默认使用rdb方式持久化。 |
Redis持久化
RDB 做全量持久化,AOF 做增量持久化。Redis 4.0 之后推出 RDB-AOF 混合持久化模式,作为默认配置来使用:
- 由于 RDB 是间隔一段时间后才会进行持久化,在此期间内如果 Redis 服务出现问题,则会丢失这一段时间内的数据,因此需要 AOF 来配合使用
- 在 Redis 重启时,会使用 BGSAVE 命令生成的 RDB 文件来重新构建内容,再使用 AOF 来重新执行近期的写指令,来实现数据的完整恢复
RDB操作

在指定时间间隔内将内存中的数据集快照写入磁盘,即snapshot快照。它恢复时是将快照文件直接读到内存中。
redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,持久化过程都结束了再这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作,确保了极高性能。如果要进行大规模数据恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加高效。RDB的缺点是最后一次持久化后的数据可能会丢失。redis默认就是RDB,一般不需要修改其配置。
rdb保存的文件是dump.rdb,在配置文件中的“快照”部分进行配置:
1 |
快照文件名 |
触发机制
触发rdb规则,生成dump.rdb文件
- 执行sava命令或触发save规则(配置文件中的save配置,见上边conf详解中的快照)
- 执行flushall命令
- 退出redis(shutdown命令)
如何恢复?
只需要将rdb文件放在redis启动目录下就可以,redis启动的时候会自动检查dump.rdb恢复其中的数据
查看需要存放的位(rdb文件与aof文件等等):
1 |
通过下边的命令查看 或 查看配置文件(见上边conf详解中的快照) |
小结
优点:
- 适合大规模的数据恢复(主从复制中,rdb就用在从机上备份数据)
- 对数据的完整性要求不高
缺点:
- 需要一定的时间间隔进行操作,如果redis意外宕机了,最后一次修改数据就没有了
- fork进程的时候,会占用一定的内容空间
AOF操作
AOF,append only file
将所有的命令都记录下来,恢复的时候就把这个文件全部再执行一遍。默认文件无限追加。

以日志的形式来记录每个写操作,将redis执行过的所有指令记录下来(读操作不记录),只会追加文件不会改写文件(前边redis指令),redis启动会读取该文件,根据日志文件的内容将指令从前到后执行一次以完成数据的恢复,重新构建数据。
配置
配置文件:
1 |
默认不开启,需要手动配置。只需更改为yes即可(重启生效。默认是RDB-AOF混合持久化模式) |
重写规则:
- 为了解决AOF文件体积膨胀的问题,Redis提供了AOF重写功能:Redis服务器可以创建一个新的AOF文件来替代现有的AOF文件,新旧两个文件所保存的数据库状态是相同的。
- 重复或者无效的操作记录不写入、过期的数据不再写入、多条命令合并
小结
优点:
- 使用always模式,每一次修改都同步,文件的完整性会更好
- 使用默认的everysec模式,每秒同步一次,可能会丢失一秒的数据
- 使用no模式,从不同步,可获得最好的效率
缺点:
- 相对于数据文件,aof远远大于rdb,修复的速度也比rdb慢。
- aof运行效率也要比rdb慢,所以redis默认的配置就是rddb持久化。
扩展
扩展:
- RDB持久化方式能够在指定的时间间隔内对你的数据进行快照存储。
- AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以Redis协议追加保存每次写的操作到文件末尾,Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。
- 只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化
- 性能建议
- 因为RDB文件只用作后备用途,建议只在Slave(从机)上持久化RDB文件,而且只要15分钟备份一次就够了,只保留 save 900 1 这条规则。
- 如果Enable AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了,代价一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上,默认超过原大小100%大小重写可以改到适当的数值。
- 如果不Enable AOF,仅靠Master-Slave Replication(主从复制)实现高可用性也可以,能省掉一大笔IO,也减少了rewrite时带来的系统波动。代价是如果Master/Slave 同时挂掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个,微博就是这种架构。
Redis发布订阅
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。微信、微博、关注系统。
Redis客户端可以订阅任意数量的频道。
订阅/发布消息图:消息发送者、频道、消息订阅者

下图展示了频道channel1,以及订阅这个频道的三个客户端 – client2、client5和client1之间的关系:

当有新消息通过publish命令发送给频道channel1时,这个消息就会被发送给订阅它的三个客户端:

命令
命令 | 描述 |
---|---|
PSUBSCRIBE pattern [pattern …] | 订阅一个或多个符合给定模式的频道 |
PUBSUB subcommand [argument [argument …]] | 查看订阅与发布系统状态 |
PUBLISH channel message | 将信息发送到指定的频道 |
PUNSUBSCRIBE [pattern [pattern …]] | 退订所有给定模式的频道 |
SUBSCRIBE channel [channel …] | 订阅给定的一个或多个频道的信息 |
UNSUBSCRIBE [channel [channel …]] | 退订给定的频道 |
测试
订阅端:
1 |
qsdbl@macbook ~ % redis-cli |
发送端:
1 |
qsdbl@macbook ~ % redis-cli |
原理
Redis是使用C实现的,通过分析 Redis 源码里的 pubsub.c 文件,了解发布和订阅机制的底层实现,籍此加深对 Redis 的理解。
Redis 通过
PUBLISH、 SUBSCRIBE 和 PSUBSCRIBE 等命令实现发布和订阅功能。
通过 SUBSCRIBE 命令订阅某频道后,redis-server
里维护了一个字典,字典的键就是一个个 channel,而字典的值则是一个链表,链表中保存了所有订阅这个 channel 的客户端。SUBSCRIBE
命令的关键,就是将客户端添加到给定 channel 的订阅链表中。
通过 PUBLISH 命令向订阅者发送消息,redis-server
会使用给定的频道作为键,在它所维护的 channel 字典中查找记录了订阅这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有订阅者。
Pub/Sub
从字面上理解就是发布(Publish)与订阅(Subscribe),在Redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。
使用场景
1、实时消息系统
2、实时聊天(频道当作聊天室,将消息回显给所有人)
3、订阅、关注系统都可以
稍微复杂的场景可以使用消息中间件来实现。
Redis主从复制
概念
主从复制,是指将一台Redis服务器的数据,复制到其他Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。Master以写为主,Slave以读为主。
默认情况下,每台Redis服务器都是主节点。一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。
作用
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个节点分担读负载,可以大大提高Redis服务器的并发量。
- 高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制说Redis高可用的基础。
一般来说,要将Redis运用于工程项目中,只使用一台Redis是万万不能的(宕机),原因如下:
- 从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力过大
- 从容量上,单个Redis服务器内存容量有限,就算一台Redis服务器内存容量为256GB,也不能将所有内存作为Redis存储内存,一般来说,单台Redis最大使用内存不应该超过20GB。
电商网站上的商品,一般都是一次上传,无数次浏览的,说专业点也就是“多读少写”。对于这种场景,可以使用下边的架构:

主从复制,读写分离。80%的情况下都是在进行读操作,减缓服务器的压力,架构中经常使用,一主二从。
配置
只配置从库,不配置主库。默认情况下,每台Redis服务器都是主节点,只需要配置从机即可。
1 |
查看当前库的信息(replication - 复制) |
使用命令的方式配置主从机,是临时的,重启失效(恢复为主机)。在配置文件中进行配置才是永久生效。
1 |
# 配置文件: |
案例
- 本地模拟集群
- 使用多个配置文件,启动多个redis-server(每个redis服务都是相互独立的)
- 运行端口、pid文件名、日志文件名、rdb快照文件名等配置不同
从机
只需要配置从机,使用命令slaveof指认主机即可。
1 |
qsdbl@macbook homebrew % redis-cli -p 6380 |
主机
不需要配置。
1 |
qsdbl@macbook homebrew % redis-cli -p 6379 |
注意
主机可以写,从机不能写只能读。主机中所有信息和数据,都会自动被从机保存。
1 |
# 从机 |
主机断开连接(宕机、网络原因等),依然可以从“从机”读数据,但是没有写操作。与主机恢复连接后,依然可以获取到主机后边写入的数据。
复制原理
- slave启动成功连接到master后,会发送一个sync同步命令。master接收到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,并完成一次完成同步。
- 全量复制:slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
- 增量复制:master继续将新的所有收集到的修改命令依次传给slave,完成同步。
- 只要是重新连接master,一次完成同步(全量复制)将被自动执行,保证数据一定能在从机中读取到。
扩展
也可以组成链式结构。主 - 从 - 从 - 从
配置命令也是使用前边的“slaveof”,只不过全部指向主机改成链式。
若主机宕机了,要在剩下的从机中选择一个作为主机。可使用命令“slaveof no one”进行配置。
原主机恢复后,不会主动恢复之前的关系,不过可以使用“slaveof”命令配置为从机。
哨兵模式
概念
前边,主从切换需要人工干预,当“主”服务器宕机后,需要手动把一台“从”服务器切换为“主”服务器,费时费力还会造成一段时间内服务不可用。哨兵模式,Redis自2.8开始正式提供Sentinel(哨兵)架构,能够在后台监控主机是否故障,如果发生故障则通过自动选举把其中一台“从”服务器转换为“主”服务器。
哨兵模式是一种特殊的模式,Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
作用:
- 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器
- 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。(除了监控各个服务器之外,哨兵之间也会互相监控)
假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象称为“主观下线”。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为“客观下线”。
测试
P34 05:00
Redis缓存穿透和雪崩
P28 01:00
学习ing,持续更新中!