1.nosql
为什么要用nosql
现在是2022年,是一个大数据的时代;技术更新迭代,年轻人越来越多,所以我们需要逼着自己学习,
只有不断学习,才能让自己的亲人过的更好。
1刚开始还是单机mysql的时代
用户APP通过ADL访问到数据库,仅此而已。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DmNuBGzB-1659370129109)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220422223820986.png)]
那个年代,要给简单的网站,访问量不会太大,单个数据库完全足够了。
那个时候更多的是静态的html网页,服务器完全没有压力。
这种情况下,未来的问题是哪些:
1.数据量太大,一个机器放不下
2.数据索引,一个机器放不下
3.访问量(读写混合),要给机器放不下
只要出现上面的一种情况,则必须要升级才能满足要求
Memcached(缓存)+Mysql+垂直分离(分库读写分离)
80%的访问都是在读数据库,同样的查询不断访问数据库比较影响性能,这个时候引入缓存。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jvMFSUfW-1659370129113)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220422233419558.png)]
发展过程:优化数据结构和索引–>文件缓存(IO)–>Memcached(当时热门的缓存技术),
不缺人,缺人才一定要学的好。
什么是Nosql
nosql not only sql 不仅仅时sql
关系型数据库:mysql,行,列
非关系型数据库:redis
很多数据类型,比如个人信息,地理位置,社交网络,这些数据的存储,不需要一个固定的格式,不需要多少操作就可以横向扩展! 就像Map<String,Object>使用键值对就可以控制。
Nosql的特点
解耦:不存在耦合关系
? 1.方便扩展(数据之间没有关系,很好扩展)
? 2.大数据量,高性能(redis一秒钟写8万次,读取11万次,nosql的缓存记录级时细粒度的缓存,性能很高)
? 3.数据类型多样性(不需要事先设计表就可以用,数据量特别大的表,一般人无法设计)
? 4.RDBMS(关系型数据库) 和NoSql
传统的RDBMS
- 结构化组织
- sql
- 数据和关系都存在表中
- 操作,数据语言定义
nosq
- 不仅仅时数据
- 没有固定的查询语言
- 键值对存储,列存储,文档存储,图形数据库(社交关系)
- 最终一致性(不看过程,只要看最终结果)
- cap定理和base(一地多活)
- 高性能,高可用,高可扩(扩展)
3V+3高
大数据的3V:主要是描述问题的 1.海量Volume 2.多样Variety 3.实时Velocity 大数据的3高:主要是对程序的需求 1.高并发 2.高可扩(随时可以水平拆分,就是机器不够了,可以通过添加机器来解决) 3.高性能 (保证高性能,满足客户的体验需求)
真正的公司实际运用中,肯定时RDBMS和NOSQL同时使用。
阿里巴巴演进
技术记不得,只有学好基础,才能扎实扎实。
阿里巴巴第五代:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ac7ZZMZh-1659370129116)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220423111538952.png)]
数据架构,逐渐离谱:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aMJP3ze5-1659370129117)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220423112028944.png)]
? 名称,价格,商家信息
? 通过关系型数据库 mysql,oracle,来解决 (早年去IOE 王坚:推荐文章,阿里云的那群疯子。)
? 淘宝的mysql已经不是原来的mysql
? 文档型数据库,mongoDB;
分布式文件系统 FastDFS
- 淘宝自己的 TFS
- Google 的 GFS
- Hadoop 的HDFS 大数据必须要学
- 阿里云的 云存储
- 搜索引擎,solr,elasticsearch
- Iserch 淘宝在用, 多隆,阿里云的第一个程序员
所有牛逼的人,都有一段苦逼的岁月,只要有傻逼一样的坚持,终将牛逼。
- 内存数据库
- reids ,memache .....
- 第三方的应用
一个简单的页面,背后不是我们想的那么简单
? 大型互联网的应用:
-
数据类型太多 -
数据源太多,经常重构 -
数据改造,大面积改造 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uG99l23s-1659370129128)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220423113942652.png)] 解决问题:没有什么加层,统一的API [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PAyyPLud-1659370129129)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220423114052821.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I8DiBdl4-1659370129131)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220423114150043.png)]
缓存设计:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gOcP0Co5-1659370129132)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220423114444636.png)]
这里以上都是nosql,入门技术。
nosql的四大分类
kv键值对
1.新浪:Redis
2.美团:Redis+Tair
3.阿里+百度:Redis+memaCache
文档型数据库(bson格式和json一样)
1.MongoDB(必须掌握)
? MongoDB是一个基于分布式文件存储的数据库,c++编写,主要是用于处理大量的文档
? MongoDB是介于关系型和非关系型之间的数据库,是功能最丰富,最像关系型数据库的产品。
2.ConthDB
列存储数据库
1.HBase
2.分布式文件系统
图关系数据库(存的是关系,不是图)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m3Dctzf0-1659370129133)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220423234333797.png)]
1.他不是存图形的,存的是关系,比如,朋友圈社交网络,广告推荐。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xf8TBLCK-1659370129134)(C:\Users\Administrator\Desktop\20200410144538483.png)]
2Redis入门
概述
Redis(Remote Dictionary Server ),即远程字典服务
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis能干嘛
1.内存存储,持久化,内存是断电即失所以说,持久化是很重要的(rdb,aof)
2.效率高,可以用于高速存储
3.发布订阅系统
4.地图信息分析
5.计时器,计数器(浏览量)
6…
特性
1.多样化的数据类型
2.持久化
3.集群
4.事务
。。。。
学习中需要用到的东西
1.官网: https://redis.io/
2.中文网:https://www.redis.net.cn/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0i2A88Gb-1659370129135)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220426104501607.png)]
注意:windows在github下载(已停更很久了)。
Redis推荐在linux服务器上搭建学习使用。
windows下使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-09xuZj3q-1659370129136)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220426153850618.png)]
Linux安装
1 .下载安装包redis-7.0.0.tar.gz
2.安装redis
移动redis安装包到opt
mv redis-7.0.0.tar.gz /opt
解压安装包
tar -zxvf redis-7.0.0.tar.gz
解压完成
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NooRjN19-1659370129137)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220505234934768.png)]
3.查看redis文件,可以看到配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TYVS4mya-1659370129138)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220505235120973.png)]
4.基本的环境安装
yum install gcc-c++
make //自动配置文件
make install //确认安装
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BOVwR2vd-1659370129139)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220506213507439.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hdBFp93O-1659370129140)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220506213652347.png)]
5.redis默认安装路径 /user/local/bin
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BDbRSyds-1659370129141)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220506214213518.png)]
6.将redis配置文件复制到当前目录下,先新建目录,再拷贝到新建目录下
以后这个配置文件启动
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Joezr2b9-1659370129142)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220506214515610.png)]
7.reids默认不是后台启动,需要修改配置文件
vim redis.conf
修改后esc 退出 :wq保存,如果是root权限依然报无权限保存,可以::wq! 强制保存
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VaUqs44U-1659370129143)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220506221103851.png)]
8.启动redis
正常的启动方式,根据上面的配置文件启动
进入到安装目录
redis-server kconfig/redis.config //可能有反应,也可能没有反应
判断是否启动成功
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-md1PlQKz-1659370129144)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220506224230877.png)]
9.测试连接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9qFYLXUc-1659370129145)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220506225441438.png)]
遇到的问题:redis已启动又重新启动了
[root@iZuf666vjdw36wtck8r51sZ bin]# redis-server
338399:C 06 May 2022 22:46:21.111 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
338399:C 06 May 2022 22:46:21.111 # Redis version=7.0.0, bits=64, commit=00000000, modified=0, pid=338399, just started
338399:C 06 May 2022 22:46:21.111 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
338399:M 06 May 2022 22:46:21.112 * monotonic clock: POSIX clock_gettime
338399:M 06 May 2022 22:46:21.112 # Warning: Could not create server TCP listening socket *:6379: bind: Address already in use
338399:M 06 May 2022 22:46:21.112 # Failed listening on port 6379 (TCP), aborting.
[root@iZuf666vjdw36wtck8r51sZ bin]#
10.判断Redis是否已启动
ps -ef | grep redis
root 338060 337936 0 21:47 pts/3 00:00:00 vim redis.conf
root 338118 338072 0 22:00 pts/4 00:00:00 vim redis.conf
root 338141 338072 0 22:03 pts/4 00:00:00 vim redis.conf
root 338162 338072 0 22:09 pts/4 00:00:00 vim redis.conf
root 338175 338072 0 22:10 pts/4 00:00:00 vim redis.conf
root 338230 338072 0 22:18 pts/4 00:00:00 vim redis.conf
root 338294 338072 0 22:28 pts/4 00:00:00 redis-cli
root 338296 338072 0 22:29 pts/4 00:00:00 redis-cli
root 338298 338072 0 22:29 pts/4 00:00:00 redis-cli
root 338376 1 0 22:40 ? 00:00:00 redis-server 127.0.0.1:6379
root 338404 338072 0 22:47 pts/4 00:00:00 grep --color=auto redis
关闭线程
kill -9 338376
11.推出服务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ECfcBSbA-1659370129146)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220506225547340.png)]
12.后面需要实现单机多redis集群
性能测试
| | | |
---|
序号 | 选项 | 描述 | 默认值 | 1 | -h | 指定服务器主机名 | 127.0.0.1 | 2 | -p | 指定服务器端口 | 6379 | 3 | -s | 指定服务器 socket | | 4 | -c | 指定并发连接数 | 50 | 5 | -n | 指定请求数 | 10000 | 6 | -d | 以字节的形式指定 SET/GET 值的数据大小 | 2 | 7 | -k | 1=keep alive 0=reconnect | 1 | 8 | -r | SET/GET/INCR 使用随机 key, SADD 使用随机值 | | 9 | -P | 通过管道传输 请求 | 1 | 10 | -q | 强制退出 redis。仅显示 query/sec 值 | | 11 | –csv | 以 CSV 格式输出 | | 12 | *-l*(L 的小写字母) | 生成循环,永久执行测试 | | 13 | -t | 仅运行以逗号分隔的测试命令列表。 | | 14 | *-I*(i 的大写字母) | Idle 模式。仅打开 N 个 idle 连接并等待。 | |
测试命令:
# redis-benchmark -h localhost -p 6379 -c 100 -n 100000
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eAKU02cA-1659370129146)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220507000722270.png)]
基础知识
1.Redis一共有16个数据库,默认使用第0个
? 可以使用select 方法切换数据库
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> DBsize
(integer) 0
127.0.0.1:6379[3]> dbsize
(integer) 0
127.0.0.1:6379[3]> select 7
OK
127.0.0.1:6379[7]> dbsize
(integer) 0
127.0.0.1:6379[7]> set name huang
OK
127.0.0.1:6379[7]> dbsize
(integer) 1
127.0.0.1:6379[7]> select 3
OK
127.0.0.1:6379[3]> dbsize
(integer) 0
2.查看数据库中所有的key
127.0.0.1:6379> keys *
1) "key:__rand_int__"
2) "mylist"
3) "kuang"
4) "age"
3.清除当前数据库中所有的key
4.清楚所有数据库的key
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> flushall
Redis是单线程
官方表示,Redis是基于内存的操作,CPU不是影响性能的主要因素,Redis的性能瓶颈是内存和网络带宽,所有可以用单线程就用单线程了。
Redis的单线程为什么这么快?
误区1:高性能的服务器一定是多线程的
误区2:多线程的一定比单线程快
cpu>大于内存>硬盘
核心:Redis是基于内存的,所有数据都放在内存中,所以说单线程的操作效率是最高的,多线程需要上下文切换,反而会消耗性能。
Redis的五大数据类型
官方文档
Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。
Redis-key
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set name huang
OK
127.0.0.1:6379> set age 11
OK
127.0.0.1:6379> keys *
1) "name"
2) "age"
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> exists name2
(integer) 0
127.0.0.1:6379> move name 1
(integer) 1
127.0.0.1:6379[7]> move name 8
(integer) 1
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> expire age 10
(integer) 1
127.0.0.1:6379> ttl age
(integer) 7
127.0.0.1:6379> ttl age
(integer) 3
127.0.0.1:6379> ttl age
(integer) -2
127.0.0.1:6379> type name
string
更多的命令根据需要再官方文档查看
String(字符串)
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> get name
"huge"
127.0.0.1:6379> append name 'gege'
(integer) 8
127.0.0.1:6379> get name
"hugegege"
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> strlen name
(integer) 8
127.0.0.1:6379> append age 99
(integer) 2
127.0.0.1:6379> get age
"99"
127.0.0.1:6379> get name
"hugegege"
127.0.0.1:6379> get age
"99"
127.0.0.1:6379> append views 0
(integer) 1
127.0.0.1:6379> incr views
(integer) 1
127.0.0.1:6379> get views
"1"
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> get views
"2"
127.0.0.1:6379> decr views
(integer) 1
127.0.0.1:6379> get views
"1"
127.0.0.1:6379> incrby views 11
(integer) 12
127.0.0.1:6379> get views
"12"
127.0.0.1:6379> decrby views 4
(integer) 8
127.0.0.1:6379> get views
"8"
127.0.0.1:6379> incr name
(error) ERR value is not an integer or out of range
127.0.0.1:6379> get key1
"woshikey"
127.0.0.1:6379> getrange key1 1 6
"oshike"
127.0.0.1:6379> getrange key1 3 -1
"hikey"
127.0.0.1:6379> get key2
"wshu"
127.0.0.1:6379> setrange key2 1 bu
(integer) 4
127.0.0.1:6379> get key2
"wbuu"
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> setex name 12 huge
OK
127.0.0.1:6379> ttl name
(integer) 8
127.0.0.1:6379> ttl name
(integer) 5
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set name huge
OK
127.0.0.1:6379> get name
"huge"
127.0.0.1:6379> set name gg
OK
127.0.0.1:6379> get name
"gg"
127.0.0.1:6379> setnx name wos
(integer) 0
127.0.0.1:6379> get name
"gg"
127.0.0.1:6379> setnx age 22
(integer) 1
127.0.0.1:6379> get age
"22"
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> keys *
1) "k2"
2) "k3"
3) "name"
4) "age"
5) "k1"
127.0.0.1:6379> mget k1 k2 k3 name
1) "v1"
2) "v2"
3) "v3"
4) "gg"
127.0.0.1:6379> mget k1 k4
1) "v1"
2) (nil)
127.0.0.1:6379> msetnx k2 v2 k4 v4
(integer) 0
127.0.0.1:6379>
127.0.0.1:6379> set user:1 {name:huage,age:11}
OK
127.0.0.1:6379> get user:1
"{name:huage,age:11}"
127.0.0.1:6379> mset user:1:name huge user:1:age 12
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "huge"
2) "12"
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set name hu
OK
127.0.0.1:6379> getset name huge
"hu"
127.0.0.1:6379> get name
"huge"
127.0.0.1:6379> getset name huzu
"huge"
127.0.0.1:6379> get name
"huzu"
数据结构是相同,所有很多方法都存在。
String:的值可以是字符窜,也可以是数字,作为数字可以用来实现一些必要是统计
阅读量,粉丝数,点在数 等等
List
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oEltsbCA-1659370129148)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220508150504536.png)]
在Redis中list可以被玩成栈(先进后出)队列(先进先出)阻塞队列(两边都可以出)
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> lpush list one
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 3
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1
1) "three"
2) "two"
127.0.0.1:6379> rpush list right
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> keys *
1) "list"
127.0.0.1:6379> Lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> lpop list
"three"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "right"
127.0.0.1:6379> lpop list 2
1) "two"
2) "one"
127.0.0.1:6379> lrange list 0 -1
1) "right"
127.0.0.1:6379> lpush list left
(integer) 2
127.0.0.1:6379> rpop list
"right"
127.0.0.1:6379> lrange list 0 -1
1) "left"
127.0.0.1:6379> lpush list a b c
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "c"
2) "b"
3) "a"
4) "left"
127.0.0.1:6379> lrange list 0 -1
1) "c"
2) "b"
3) "a"
4) "left"
127.0.0.1:6379> rpush list 1 2 3 4
(integer) 8
127.0.0.1:6379> lrange list 0 -1
1) "c"
2) "b"
3) "a"
4) "left"
5) "1"
6) "2"
7) "3"
8) "4"
127.0.0.1:6379> keys *
1) "1"
2) "list"
127.0.0.1:6379> move 1 1
(integer) 1
127.0.0.1:6379> keys *
1) "list"
127.0.0.1:6379> lindex list 0
"c"
127.0.0.1:6379> lindex list 10
(nil)
127.0.0.1:6379> lindex list 7
"4"
127.0.0.1:6379> keys *
1) "list"
127.0.0.1:6379> llen list
(integer) 8
127.0.0.1:6379>
(integer) 1
127.0.0.1:6379> lpush list 1 1 2 2 3 3 4 5
(integer) 8
127.0.0.1:6379> lrange list 0 -1
1) "5"
2) "4"
3) "3"
4) "3"
5) "2"
6) "2"
7) "1"
8) "1"
127.0.0.1:6379> lrem list 1 1
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "5"
2) "4"
3) "3"
4) "3"
5) "2"
6) "2"
7) "1"
127.0.0.1:6379> lrem list 2 3
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "5"
2) "4"
3) "2"
4) "2"
5) "1"
127.0.0.1:6379> lrange list 0 -1
1) "li4"
2) "li3"
3) "li2"
4) "li1"
5) "1"
127.0.0.1:6379> ltrim list 1 3
OK
127.0.0.1:6379> lrange list 0 -1
1) "li3"
2) "li2"
3) "li1"
127.0.0.1:6379> lpush list li1 li2 li3
(integer) 3
127.0.0.1:6379> lpush mylist my1 my2 my3
(integer) 3
127.0.0.1:6379> rpoplpush list mylist
"li1"
127.0.0.1:6379> lrange list 0 -1
1) "li3"
2) "li2"
127.0.0.1:6379> lrange mylist 0 -1
1) "li1"
2) "my3"
3) "my2"
4) "my1"
127.0.0.1:6379> lset list 2 line
(error) ERR index out of range
127.0.0.1:6379> lset list 1 line
OK
127.0.0.1:6379> lrange list 0 -1
1) "li2"
2) "line"
127.0.0.1:6379>
127.0.0.1:6379> lrange list 0 -1
1) "li2"
2) "li1"
3) "li1"
4) "li"
127.0.0.1:6379> linsert list before li1 li1b
(integer) 5
127.0.0.1:6379> linsert list after li1 li1f
(integer) 6
127.0.0.1:6379> lrange list 0 -1
1) "li2"
2) "li1b"
3) "li1"
4) "li1f"
5) "li1"
6) "li"
总结
1.世界上list是一个链表,可以通过before,after,left,right都可以插入值
2.如果key不存在则创建新的链表
3.如果key以存在则新增内容
4.如果移除所有的值,则一个空链表会消失
5.从两边插入或者修改值效率高,从中间处理元素效率相对较慢
set
Set中的值是不可以重复的
127.0.0.1:6379> sadd set hello helloWorld
(integer) 2
127.0.0.1:6379> sadd set helloKuan
(integer) 1
127.0.0.1:6379> Smembers set
1) "helloWorld"
2) "helloKuan"
3) "hello"
127.0.0.1:6379> sismember set hello
(integer) 1
127.0.0.1:6379>
127.0.0.1:6379> scard set
(integer) 3
rem 移除某个元素
127.0.0.1:6379> smembers set
1) "helloWorld"
2) "helloKuan"
3) "hello"
127.0.0.1:6379> srem set hello
(integer) 1
127.0.0.1:6379> smembers set
1) "helloWorld"
2) "helloKuan"
srandmember 随机获取一个数
127.0.0.1:6379> smembers set
1) "helloWorld"
2) "helloDage"
3) "lovekuangs"
4) "helloKuan"
5) "hello"
6) "helloKuangshen"
127.0.0.1:6379> srandmember set
"lovekuangs"
127.0.0.1:6379> srandmember set
"helloKuan"
127.0.0.1:6379> srandmember set 2
1) "hello"
2) "lovekuangs"
127.0.0.1:6379>
spop 随机从set中移除一个元素
127.0.0.1:6379> smembers set
1) "helloWorld"
2) "helloDage"
3) "lovekuangs"
4) "helloKuan"
5) "hello"
6) "helloKuangshen"
127.0.0.1:6379> spop set
"lovekuangs"
127.0.0.1:6379> spop set
"helloKuangshen"
127.0.0.1:6379> smembers set
1) "helloWorld"
2) "helloDage"
3) "helloKuan"
4) "hello"
127.0.0.1:6379> spop set 2
1) "hello"
2) "helloDage"
127.0.0.1:6379> smembers set
1) "helloWorld"
2) "helloKuan"
127.0.0.1:6379>
smove
127.0.0.1:6379> smove set set2 helloWorld
(integer) 1
127.0.0.1:6379> smembers set2
1) "helloWorld"
2) "hello"
3) "set2"
-差集
-交集 比如微博中的共同关注
-并集
127.0.0.1:6379> sadd key1 a b c
(integer) 3
127.0.0.1:6379> sadd key2 c d e
(integer) 3
127.0.0.1:6379> sdiff key1 key2
1) "a"
2) "b"
127.0.0.1:6379> sinter key1 key2
1) "c"
127.0.0.1:6379> sunion key1 key2
1) "a"
2) "c"
3) "d"
4) "b"
5) "e"
可以用户共同好友,共同关注,相同爱好。。。。。。。。。。。。。。。。。。。。
Hash(哈希)
Map集合,key-map时候这个值是一个map集合,本质和String类型没有太大区别,还是一个简单的key-value
127.0.0.1:6379> hset user name huge age 11
(integer) 2
127.0.0.1:6379> hmset user address suzhou genger boy
OK
127.0.0.1:6379> hget user name
"huge"
127.0.0.1:6379> hmget user name age address genger
1) "huge"
2) "11"
3) "suzhou"
4) "boy"
127.0.0.1:6379> hgetall user
1) "name"
2) "huge"
3) "age"
4) "11"
5) "address"
6) "suzhou"
7) "genger"
8) "boy"
127.0.0.1:6379> hdel user age
(integer) 1
127.0.0.1:6379> hgetall user
1) "name"
2) "huge"
3) "address"
4) "suzhou
#######################################################################################
hlen
127.0.0.1:6379> hlen user #获取指定hash的元素数量
(integer) 3
#########################################################################################
hexists
127.0.0.1:6379> hexists user ss # 判断指定hash中指定key是否存在
(integer) 0
127.0.0.1:6379> hexists user name
(integer) 1
######################################################################################
#只获取所有的keys
#只获取所有的values
127.0.0.1:6379> hgetall user
1) "name"
2) "huge"
3) "address"
4) "suzhou"
127.0.0.1:6379> hkeys user # 获取所有的key
1) "name"
2) "address"
127.0.0.1:6379> hvals user # 获取所有的values
1) "huge"
2) "suzhou"
########################################################################################
incr
127.0.0.1:6379> hgetall user
1) "name"
2) "huge"
3) "address"
4) "suzhou"
5) "age"
6) "12"
127.0.0.1:6379> hincrby user age 1 #指定hash指定key增加指定值
(integer) 13
127.0.0.1:6379> hget user age
"13"
127.0.0.1:6379> hsetnx user gender 1 # 不存在就增加个key-value
(integer) 1
127.0.0.1:6379> hsetnx user gender 2
(integer) 0
127.0.0.1:6379> hgetall user
1) "name"
2) "huge"
3) "address"
4) "suzhou"
5) "age"
6) "13"
7) "gender"
8) "1"
hash特别适用于对象的存储。尤其是经常变动的信息。String更加适合用字符窜
Zset(有序的set)
在set的基础上增加一个值,set v1, zset score v2
127.0.0.1:6379> zadd myz 0 zo
(integer) 1
127.0.0.1:6379> zadd myz 1 z1 2 z2
(integer) 2
127.0.0.1:6379> zrange myz 0 -1
1) "zo"
2) "z1"
3) "z2
################################################################################
zrangebyscore
127.0.0.1:6379> zadd salary 2000 huge 3000 wenge 4000 muge 500 nige#添加多个元素
(integer) 4
127.0.0.1:6379> zrangebyscore salary -inf +inf # 从小到大排序值
1) "nige"
2) "huge"
3) "wenge"
4) "muge"
127.0.0.1:6379> zrangebyscore salary -inf 4000 withscores #从无穷小到4000按照score排序,展示值和scores
1) "nige"
2) "500"
3) "huge"
4) "2000"
5) "wenge"
6) "3000"
7) "muge"
8) "4000"
127.0.0.1:6379> zrangebyscore salary 550 3500 withscores #从550到3500排序
1) "huge"
2) "2000"
3) "wenge"
4) "3000"
127.0.0.1:6379> zrange salary 0 -1 withscores#zrange 默认从小到大
1) "lige"
2) "300"
3) "huge"
4) "2000"
5) "wenge"
6) "3000"
7) "muge"
8) "4000"
9) "gaoge"
10) "4200"
127.0.0.1:6379> zrevrange salary 0 -1 withscores # zrevrange默认从大到小
1) "gaoge"
2) "4200"
3) "muge"
4) "4000"
5) "wenge"
6) "3000"
7) "huge"
8) "2000"
9) "lige"
10) "300"
####################################################################################
rem 移除元素
127.0.0.1:6379> zrange salary 0 -1
1) "nige"
2) "huge"
3) "wenge"
4) "muge"
127.0.0.1:6379> zrem salary nige #移除一个nige的元素
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "huge"
2) "wenge"
3) "muge"
################################################################################
zcard 获取数量
127.0.0.1:6379> zcard salary
(integer) 3
zcount 获取范围内的数量
127.0.0.1:6379> zrange salary 0 -1 withScores #正序查询所有
1) "lige"
2) "300"
3) "huge"
4) "2000"
5) "wenge"
6) "3000"
7) "muge"
8) "4000"
9) "gaoge"
10) "4200"
127.0.0.1:6379> zcount salary 2000 4199
(integer) 3
其余的所有api,还有很多,如果工作中需要,可以根据官方文档来查找。
三种特殊数据类型
geospatial地理位置
朋友圈定位,附近的人,打车距离计算
Redis的Geo在Redis3.2版本就推出了,这个功能可以推算地理位置的信息,两地之间距离,方圆几里的人
可以查询测试一些数据:
只有六个命令:
geoadd
127.0.0.1:6379> geoadd china:city 116.41667 39.91667 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.43333 34.50000 shanghai 117.20000 39.13333 tianjing
(integer) 2
127.0.0.1:6379> geoadd china:city 113.23333 23.16667 guangzhou 113.51667 22.30000 zhuhai
(integer) 2
geopos
127.0.0.1:6379> geopos china:city beijing guangzhou
1) 1) "116.41667157411575317"
2) "39.91667095273589183"
2) 1) "113.23332935571670532"
2) "23.16667082372672581"
geodist
127.0.0.1:6379> geodist china:city beijing shanghai
"748346.9287"
127.0.0.1:6379> geodist china:city beijing shanghai km
"748.3469"
georadius
127.0.0.1:6379> georadius china:city 110 35 1000 km
1) "tianjing"
2) "beijing"
127.0.0.1:6379> georadius china:city 110 35 1000 km withcoord
1) 1) "tianjing"
2) 1) "117.19999998807907104"
2) "39.13333058676914078"
2) 1) "beijing"
2) 1) "116.41667157411575317"
2) "39.91667095273589183"
127.0.0.1:6379> georadius china:city 110 35 1000 km withcoord withdist
1) 1) "tianjing"
2) "786.7743"
3) 1) "117.19999998807907104"
2) "39.13333058676914078"
2) 1) "beijing"
2) "786.9892"
3) 1) "116.41667157411575317"
2) "39.91667095273589183"
127.0.0.1:6379> georadius china:city 110 35 1000 km withcoord withdist count 1
1) 1) "tianjing"
2) "786.7743"
3) 1) "117.19999998807907104"
2) "39.13333058676914078"
127.0.0.1:6379>
georadiusbymember
#以某个成员为中心,方圆一定范围内的其他元素
# georadiusbymember key 中心城市 距离 单位 经纬度 距离
127.0.0.1:6379> georadiusbymember china:city guangzhou 1000 km withcoord withdist
1) 1) "zhuhai"
2) "100.6836"
3) 1) "113.51667255163192749"
2) "22.29999896492555678"
2) 1) "guangzhou"
2) "0.0000"
3) 1) "113.23332935571670532"
2) "23.16667082372672581"
127.0.0.1:6379> georadiusbymember china:city guangzhou 1000 km
1) "zhuhai"
2) "guangzhou"
geohash
127.0.0.1:6379> geohash china:city guangzhou
1) "ws0eb9mgxu0"
127.0.0.1:6379> geohash china:city guangzhou beijing
1) "ws0eb9mgxu0"
2) "wx4g14s53n0"
GEO的底层实现其实是set,可以通过一些set的命令执行GEO
127.0.0.1:6379> zrange china:city 0 -1
1) "zhuhai"
2) "guangzhou"
3) "shanghai"
4) "tianjing"
5) "beijing"
127.0.0.1:6379> zrem china:city zhuhai
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "guangzhou"
2) "shanghai"
3) "tianjing"
4) "beijing"
Hyperloglog
Redis 2.8.9版本就更新了Hyperloglog数据结构!
Redis Hyperloglog技术统计的算法。
优点:占用内存是固定的,2^64不同的元素技术,只需要12kb内存,如果要从内存角度来比较,Hyperloglog是首选。
网页的UV(访问量,一个人访问一个网站多次,算作一个人)
传统的方式,set保存用户的id,然后就可以统计set元素数量
这个方法如果保存大量的用户id,就会比较麻烦,我们的目的是为了统计,而不是保存用户id。
Hyperloglog 统计,存在0.81%的错误率,
127.0.0.1:6379> pfadd key1 a b c d de fg hi j k
(integer) 1
127.0.0.1:6379> pfcount key1
(integer) 9
127.0.0.1:6379> pfadd key2 l m no p q rs t uv
(integer) 1
127.0.0.1:6379> pfcount key2
(integer) 8
127.0.0.1:6379> pfmerge key3 key1 key2
OK
127.0.0.1:6379> pfcount key3
(integer) 17
如果允许误差的统计,那么就可以使用Hyperloglog
如果不允许误差的统计,那么就可以使用set,或者自己设定数据类型。
Bitmap
位存储
只要是统计只有两个状态的数据,就可以通过位存储来实现,比如:登陆未登陆,活跃不活跃,打卡未打卡,
Bitmaps位图数据结构,通过操作二进制位来进行记录,就只有0,1 两种状态。
测试,统计一周中打卡
127.0.0.1:6379> setbit wk 0 0
(integer) 0
127.0.0.1:6379> setbit wk 1 1
(integer) 0
127.0.0.1:6379> setbit wk 2 1
(integer) 0
127.0.0.1:6379> getbit wk 2
(integer) 1
127.0.0.1:6379> setbit wk 3 1
(integer) 0
127.0.0.1:6379> setbit wk 4 0
(integer) 0
127.0.0.1:6379> setbit wk 4 1
(integer) 0
127.0.0.1:6379> bitcount wk
(integer) 4
?????????????、?如果是在一定范围内的统计,好像是有问题不会用。
事务
Redis事务的本质:一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程中,顺序执行。
一次性(一次执行完毕),顺序性(按顺序执行),排他性(不可以被打断)执行命令。
—队列 set set set 执行—
所有命令在事务中,并没有被执行,只有在发起执行命令后才被执行。
Redis事务:
开启事务:multi
命令入队:
执行事务:exec
有时候会用到锁。
Redis 的单条命令是保证原子性的,但是一个事务是不保证原子性的。
执行一个事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> get k1
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) OK
4) "v1"
取消一个事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> discard
OK
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI
编译异常,所有命令都不会执行(相当于代码有问题,写完就看出来错误)
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 1
QUEUED
127.0.0.1:6379(TX)> set k2 2
QUEUED
127.0.0.1:6379(TX)> set k3
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> set k4 4
QUEUED
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1
(nil)
127.0.0.1:6379> get k4
事务执行时报错,除了报错的命令,不影响其他事务命令执行,事务执行不存在原子性问题
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 2
QUEUED
127.0.0.1:6379(TX)> set k2 k
QUEUED
127.0.0.1:6379(TX)> incr k2
QUEUED
127.0.0.1:6379(TX)> incr k1
QUEUED
127.0.0.1:6379(TX)> get k1
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range
4) (integer) 3
5) "3"
6) "k"
监控 watch
悲观锁
? 很悲观,认为做什么都会出问题,所以做什么都加锁
乐观锁
? 很乐观,认为做什么都不会出问题,多以不会枷锁。在更新新数据的时候判断以下, 在此期间有没有人修改过数据。
获取version
更新的时候比较version
正常执行
27.0.0.1:6379> watch mony
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby mony 11
QUEUED
127.0.0.1:6379(TX)> incrby out 11
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 79
2) (integer) 21
未正常执行
OK
127.0.0.1:6379> watch mony
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby mony 11
QUEUED
127.0.0.1:6379(TX)> incrby out 11
QUEUED
127.0.0.1:6379(TX)> exec
(nil)
127.0.0.1:6379> unwatch
OK
127.0.0.1:6379> watch mony
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby mony 15
QUEUED
127.0.0.1:6379(TX)> incrby out 15
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 75
2) (integer) 16
Jedis
用jedis来操作redis,这是最开始的操作redis的方法,方法名和Linux命令类似。
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.80</version>
</dependency>
</dependencies>
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1",6379);
System.out.println(jedis.ping());
jedis.set("name","胡歌");
}
输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gj6sIACS-1659370129149)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220516222811334.png)]
所有的API命令就是上面的linux命令,就是这么操作。
事务:
{
Jedis jedis = new Jedis("127.0.0.1",6379);
jedis.flushDB();
Transaction transaction =jedis.multi();
try{
transaction.set("user1","user");
transaction.set("user2","user2");
int s =1/0;
transaction.exec();}
catch (Exception e){
transaction.discard();
}finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close();
}
}
SpringBoot 整合Redis
springBoot操作数据库:spring-data jpa jdbc mongodb redis
SpringData 也是和springBoot 齐名项目
说明:在spingBoot2.0后,原来的jedis被换成了,lettuce
jedis:采用的是直连,多个线程同时操作的话存在不安全,想要安全,需要引入jedis ,pool 连接池。
lettuce:采用netty,实例可以采用多线程共享,不存在安全问题
整合测试
@ConfigurationProperties(
prefix = "spring.redis"
)
public class RedisProperties {
private int database = 0;
private String url;
private String host = "localhost";
private String username;
private String password;
private int port = 6379;
private boolean ssl;
private Duration timeout;
private Duration connectTimeout;
private String clientName;
private RedisProperties.ClientType clientType;
private RedisProperties.Sentinel sentinel;
private RedisProperties.Cluster cluster;
private final RedisProperties.Jedis jedis = new RedisProperties.Jedis();
private final RedisProperties.Lettuce lettuce = new RedisProperties.Lettuce();
@Bean
@ConditionalOnMissingBean( name = {"redisTemplate"})
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
# prefix = "spring.redis" //表示配置文件以这个开头
spring.redis.host=127.0.0.1
spring.redis.port=6379
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
测试:s
redisTemplate.opsForValue().set("redis","关注狂神")
String str =(String) redisTemplate.opsForValue().get("redis");
System.out.println(str);
RedisConnection collection =redisTemplate.getConnectionFactory().getConnection();
collection.flushDb();
当我们需要在redis中传入一个对象的时候,虽然value值是一个Object,但是实际使用中,需要将对象进行序列化处理。
没有做序列化处理的时候,报错,没有进行序列化处理。
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int age;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GUNxGLEK-1659370129150)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220517233837824.png)]
需要一个序列化对象1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pQ3nWg50-1659370129151)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220517234046890.png)]
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User implements Serializable {
private String name;
private int age;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sENfAPM9-1659370129152)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220517234428789.png)]
自定义RedisTemplate待添加
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
RedisConf配置文件介绍
单位
启动的时候,通过配置文件,unit单位对大小写不敏感
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mpXUqoxl-1659370129153)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220518221015305.png)]
包含
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LuUnyzQB-1659370129154)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220518221331429.png)]
可以将多个配置文件包含到这一个配置文件中,就像学习spring中的xml文件使用import
网络
bind 127.0.0.1 -::1
protected-mode yes
port 6379
timeout 0
通用 GENERAL
daemonize yes
pidfile /var/run/redis_6379.pid
loglevel notice
logfile ""
databases 16
always-show-logo no
快照
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir ./
REPLICATION 主从复制,后面讲解
SECURITY 安全
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) ""
127.0.0.1:6379> config set requirepass "123456"
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379>
127.0.0.1:6379> auth "123456" 使用密码登陆
OK
127.0.0.1:6379> ping
PONG
限制CLIENTS
maxclients 10000
maxmemory <bytes>
maxmemory-policy noeviction
1、volatile-lru:只对设置了过期时间的key进行LRU算法进行删除
2、allkeys-lru : 对所有key执行LRU算法进行删除
3、volatile-lfu:只对设置了过期时间的key进行LFU算法进行删除
4、allkeys-lfu:对所有key执行LFU算法进行删除
5、volatile-random:随机删除设置有过期时间的key
6、allkeys-random:随机删除
7、volatile-ttl : 删除即将过期的
8、noeviction : 永不过期,返回错误
APPEND ONLY MODE 模式 aof配置
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
Redis持久化
RDB Redis DataBase
Redis是内存数据库,断电即失,需要将内存文件持久化到磁盘中,
RDB Redis DataBase
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DVZtrqE2-1659370129155)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220519000047535.png)]
在指定的时间间隔内,将内存中的数据集体的写入到磁盘中,也就是快照,在恢复的时候将快照文件直接读到内存中。
Redis会单独创建fork一个子进程来持久化,会将数据写到一个临时文件中,待持久化过程都结束了,就用这个临时文件替换上次的持久化文件。整个过程,主进程不会进行IO操作,这就确保了极高的性能。如果需要进行大规模的数据恢复,且对数据恢复的完整性不是很敏感,那么RDB方式比AOF方式更高效,RDB的缺点是最后一次持久化会丢失数据。 默认rdb文件配置,不用修改
rdb保存的文件是dump.rdb
触发机制
1.满足rdb的save规则,自动生成文件
2.执行flushall命令,也会触发生成文件
3.默认退出redis的时候,会生成文件
备份生成rdb文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UZTOOdTG-1659370129156)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220519232608983.png)]
如何恢复rdb文件
1.只要将rdb文件放在redis启动目录下面,启动redis的时候,就会自动检擦读取rdb文件,恢复其中的数据
2.查看rdb文件需要存放的位置
127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/bin"
127.0.0.1:6379>
正常情况下默认配置就够用了,但是我们需要学习其原理
优点:
1.适合大规模数据备份
2.对数据的完整性要求不是太高
缺点
1。保存备份需要时间间隔,如果此时宕机了,数据就会丢失
2.fork进程会占用一定的内容空间。
AOF append only file
将所有的命令都记录下来,恢复到适合将这些命令再执行一遍
? [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G1zoCnYf-1659370129156)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220519234019423.png)]
以日志的形式记录每一个写操作,将redis执行的所有写操作都记录下来(读操作不记录),只许追加文件不可以修改文件,reids启动的时候,会读取这个文件重新构造数据库,就是将日志中的所有命令都执行一遍以恢复数据
AOF 保存的文件:appendonly.aof
APPEND ONLY MODE
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rpA6dg2S-1659370129157)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220519234814321.png)]
默认是关闭的,修改成yes就可以
appendfsync everysec
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FD60t4Uy-1659370129158)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220520000839172.png)]
一个是生成的文件名,一个是再redis启动目录下的文件夹名。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2Zke9jA3-1659370129160)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220520001105072.png)]
保存文件后,重启reids就可以生效了
修改aof生效的配置文件,需要重启redis,保证aop文件的存在,才能保存命令
如果aof文件发生了损坏,则无法启动reids,需要通过文件redis-check-aof来修复
文件修复
redis-check-aof --fic **.aof
修复文件后,可以正常启动Redis
Redis发布订阅
Redis发布订阅(pub/sub)是一种消息通讯模式:发送者发送消息,订阅者接收消息。微信,微博,关注系统!
Redis客户端可以订阅任意数量的频道。
订阅/发布消息图:
第一个:消息发送者,第二个:频道 第三个:消息订阅者
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PrUCY6Tl-1659370129161)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220522231818364.png)]
下面是频道channel1和订阅这个频道的三个用户之间关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PB1JodW8-1659370129161)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220522233439607.png)]
当有消息通过命令publis发送到频道channel1时,就会发送给三个订阅者
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PjCYbQUL-1659370129162)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220522233551056.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sYERFyVl-1659370129163)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220522233849410.png)]
测试
127.0.0.1:6379> subscribe kuangshen
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "kuangshen"
3) (integer) 1
1) "message"
2) "kuangshen"
3) "hello \xbf\xf1\xc9\xf1"
1) "message"
2) "kuangshen"
3) "hello kuangshen"
1) "message"
2) "kuangshen"
3) "hello redis"
publish kuangshen 'hello redis'
(integer) 1
此处选用添加用java代码实现的频道订阅
知道了发布订阅功能的相关命令后,下面来看一下各个命令底层的实现原理。总体来说,发布和订阅功能相关的状态都保存在RedisServer中的pubsub_channels和pattern两个字段中:
struct redisServer{ // …
dict *pubsub_channels; // 保存所有订阅的频道关系
list *pattern; // 保存所有订阅的频道的匹配模式
// ....
其中dict类型的pubsub_channels保存所有订阅的频道关系,key就是对应的频道名,value就是所有订阅该频道的客户端。由于订阅频道的客户端可能有多个,这里采用了链表的形式进行保存。如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Op0W8ScV-1659370129164)(C:\Users\Administrator\Desktop\20201027101917468.png)]
当客户端订阅某个频道时,如果pubsub_channels中已经有了该频道名,说明该频道已经有订阅者,将其直接添加到订阅者链表的尾部即可。如果pubsub_channels并没有指定的频道名,需要先将频道添加到pubsub_channels中,然后该客户端作为链表的头节点存在。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xvSIdORY-1659370129164)(C:\Users\Administrator\Desktop\2020102710192453.png)]
退订频道和订阅频道的动作相反,如果某个客户端想要退订某个频道,服务器会遍历pubsub_channels,找到对应频道的订阅者链表,将其从链表中删除。此外,如果该客户端是这个频道的唯一订阅者,删除订阅者后,还需要将该频道从pubsub_channels中删除。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gu6kp7mZ-1659370129165)(C:\Users\Administrator\Desktop\20201027101931368.png)]
Redis主从复制
主从复制,是指将一台Redis服务器上的数据复制到其他服务器,前者称为主节点,后者成为从节点;数据的复制是单向的,只能从主节点复制到从节点。主节点以写为主,从节点以读为主。
默认情况下,每一个服务器都是主节点,且一个主节点可以有多个从节点,一个从节点只能有一个主节点。
主从复制的主要作用
1.数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
2.故障恢复:当主节点出现故障时,可以由从节点提供服务,实现故障的快速修复;实际也是一种服务的冗余。
3.负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,(写Reids数据时连接主节点,读Redis数据时连接从节点),分担服务器负载;尤其是在写少读多的情况下,通过多个节点分担读负载,可以大大提高Redis服务器的并发量。
4.高可用(集群)基石:除了上述作用,主从复制还是哨兵和集群的基石,因此说主从复制是Redis高可用的基石。
一般来说,要将Redis用到项目中,一台Redis是万万不够的。原因如下:
1。从结构上,一台服务器发生单点故障,并且一台服务器处理所有的请求负载,压力过大。
2。从容量上,单个Redis服务器容量有限,就算一台服务器256G,也不能都用作Redis内存。一般来说,一台Redis的最大使用内存不超过20G。
比如一般的电商网站,都是一次上传,无数次的浏览,就是多读写少。
对于这种场景,我们可以使用以下架构。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D4W1Zj8R-1659370129166)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220523225909575.png)]
主从复制,读写分离,80%都在进行读操作,减缓服务器压力,架构中经常使用,一主二从
环境配置
127.0.0.1:6379> info replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:240b4dca074dfd2a2d6976aa02dff6adc681478c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
复制三个配置文件,修改以下配置:
1.端口 2.pid文件名 3.日志文件名 4.rdb文件名
根据配置文件启动三个服务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pT1O62dw-1659370129167)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220524000046395.png)]
一主二从
默认情况下每一台机器都是主机 :我们著需要配置从机
配置从机的方式,认老大
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379
OK
127.0.0.1:6380> info replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_read_repl_offset:14
slave_repl_offset:14
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:c09d97db61c0e6812c4bb076277c5561f3277eb1
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:14
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:14
127.0.0.1:6380>
127.0.0.1:6379> info replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=224,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=224,lag=1
master_failover_state:no-failover
master_replid:c09d97db61c0e6812c4bb076277c5561f3277eb1
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:224
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:224
127.0.0.1:6379>
真实的主从配置都是在配置文件中完成,这样的配置是永久的,命令的配置是暂时的
细节
主机可以写,从机只能读取内容,主机的内容会被复制到从机。
在主机写入的数据,在从机可以读取。在从机写入则会报错:
127.0.0.1:6381> get k1
"v1"
127.0.0.1:6381> set k2 v2
(error) READONLY You can't write against a read only replica.
127.0.0.1:6381>
测试:主机断开后,从机依然是连接到主机的从机,只有读操作没有写操作,主机回来后继续可以读取主机内容。
如果是使用命令行来配置的主从,从机断开后,这个时候重启从机,那么会变回主机。(配置文件永久)
只要变成从机,立马可以读取主机数据。
全量复制:第一次连接主机后,会将全部数据同步到从机
增量复制:连接过程中的同步为增量复制。
层层主从
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yS2RQCMi-1659370129168)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220524230625465.png)]
上一个从节点是下一个主节点,这样也可以实现主从复制
如果主节点宕机了,这个时候怎么选择一个主节点? 手动
谋朝篡位
根据命令实现:SLAVEOF no one
127.0.0.1:6380> SLAVEOF no one
OK
127.0.0.1:6380> info replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:8ffaf32b5a5839e27120e4ee33e7ed80026700d0
master_replid2:c09d97db61c0e6812c4bb076277c5561f3277eb1
master_repl_offset:115286
second_repl_offset:115287
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_his
如果这个时候,上一个主节点回来了,已经是光杆司令了,需要从新执行命令才能层层主从。
哨兵模式
概述
主从切换的方法,当我们的主服务器宕机之后,需要我们手动选择一个从服务器切换成主服务器,费时费力,还有可能造成一段时间的的服务不可用。这个是不推荐的,在Redis2.8过后,就有了哨兵模式,自动监控切换。
哨兵模式,监控后台主机是否宕机,如果主机故障了,则通过投票的方式,选择一台从机设置为主机。
哨兵模式,是一种特殊的模式,首先Redis提供了命令,哨兵是一个独立的进程,作为进程会独立运行。
原理:哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zUEHjxsU-1659370129169)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220524234714568.png)]
这里哨兵的作用:
1。通过发送命令,让Redis服务器返回其监控其运行状态,包括主机和从机
2。当哨兵发现master宕机了,会自动将slave切换为master,然后通过 发布订阅模式通知其他从服务器,修改配置,更改主机.
然而一个哨兵进程对Redis服务器进行监控可能会出现问题,因此可以使用多个哨兵进行监控,各个哨兵之间也可以监控,形成多哨兵模式。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GhMXkOTF-1659370129170)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220524235255196.png)]
如果主服务器宕机,哨兵1先发现了这个结果,这个时候不会立刻进行failover过程,仅仅是哨兵1主观的认为服务器不可以用,这种现象称为主观下线。当后面的哨兵也检测到这种现象,数量达到一定值的时候,那么哨兵之间就会进行一次投票,投票结果由一个哨兵发起执行failover(故障转移)操作。切换成功后,就会通过订阅发布的模式,让哨兵把自己监控的服务器现实切换主机,这个过程叫客观下线。
|