当今互联网时代,用户访问量大幅提升,出现了高并发高内存问题,同时产生了大量的用户数据,为了缓解CPU及
内存压力,NoSQL这种缓存数据库的作用就体现出来了,它使得数据完全在内存中,访问数据速度极快,数据结
构也较简单,同时它还解决了IO压力,以往的关系型数据库当存储的内容较大时,会选择"水平切分,垂直切分,
读写分离等操作"但是这破坏了一定的业务逻辑依此来换取性能,代价还是很大的。总而言之,NoSQL的出现是解
决性能方面的。
NoSQL
(NoSQL = Not Only SQL ),意思“不仅仅是 SQL”,泛指非关系型的数据库。 NoSQL 不依赖业务逻辑方式存
储,而以简单的 key-value 模式存储。因此大大的增加了 数据库的扩展能力。
其特点:
总之:数据量庞大存在高并发且对数据可扩展的选择NoSQL
注:这里只讲Linux的安装过程
去官网,点击Download下载最新版本的zip文件
准备工作:以CentOS7为例
1、下载最新版本的gcc编译器
yum install centos-release-scl scl-utils-build
yum install -y ddevtoolset-8-toolchain
scl enable devtoolset-8-bash
2、测试gcc版本
gcc --version
3、采用xtfp工具将windows上下载的redis压缩包放到自己在linux这边的目录
4、进入linux找到刚刚放redis压缩包的目录,打开终端
5、解压命令:tar -zxvf redis-6.2.1.tar.gz
6、解压完进入目录 cd 解压目录
7、在该目录执行 make
(编译好)
8、如果在第一步时候没有准备C语言编译缓解,make会报错提示:-Jemalloc/jemalloc.h:没有那个文件
解决方法:make distclean
8.1、在刚才解压目录下再次执行make
命令
9、正式安装:make install
默认安装目录:/usr/local/bin
redis-benchmark:性能测试工具,可以在自己本子运行,看看自己本子性能如何
redis-check-aof:修复有问题的 AOF 文件
redis-check-dump:修复有问题的 dump.rdb 文件
redis-sentinel:Redis 集群使用
redis-server:Redis 服务器启动命令
redis-cli:客户端,操作入口
前台启动,命令行窗口不能关闭,一旦关闭,redis服务停止(ctrl + c 强制结束)
一般建议将刚刚我们解压目录中的redis.conf配置文件 复制到另一个位置上
cp /myredis/redis-6.2.1/redis.conf /etc/redis.conf
修改配置文件redis.conf
后台启动设置 daemonize no
改成 daemonize yes
后台启动redis服务
redis-server /etc/redis.conf
查看关于redis的后台进程
ps -ef | grep redis
这里我的redis的地址是*:表示任意一台IP地址都可以访问,这需要在配置文件中配置
你们一开始查看的时候都是本机地址,即 redis-server 127.0.0.1:6379
采用客户端访问redis
redis-cli
测试是否成功
在redis客户端输入ping
,如果出现 PONG ,恭喜你正式启动了Redis
Redis关闭
Linux命令行关闭 : redis-cli shutdown
redis客户端关闭 :shutdown
6379
select x
x: 0 ~ 15dbsize
flushdb
flushall
常用五大数据类型的操作命令:http://www.redis.cn/commands.html
keys *
查看当前库所有 key (匹配:keys *1)
exists key
判断某个 key 是否存在
type key
查看你的 key 是什么类型
del key
删除指定的 key 数据
unlink key
根据 value 选择非阻塞删除 仅将 keys 从 keyspace 元数据中删除,真正的删除会在后续异步操作。
expire key seconds
10 秒钟:为给定的 key 设置过期时间
tl key
查看还有多少秒过期,-1 表示永不过期,-2 表示已过期
select
命令切换数据库
dbsize
查看当前数据库的 key 的数量
flushdb
清空当前库
flushall
通杀全部库
String 是 Redis 最基本的类型 “一个 key 对应一个 value” String 类型是二进制安全的。意味着 Redis 的 string 可以包含任何数据。比如 jpg 图片 或者序列化的对象。
一个 Redis 中字符串 value 最多可以是 512M
127.0.0.1:6379> set name wcd
OK
*nx
:当数据库key不存在时,可以将k-v添加到数据库
127.0.0.1:6379> setnx age 18
(integer) 1 //库中不存在,可以添加,返回1
127.0.0.1:6379> setnx name hhh
(integer) 0 //库中已存在,不可以添加,返回0
*xx
(set):当数据库中 key 存在时,可以将 key-value 添加数据库,与 nx 参数互斥
*ex
:key 的超时秒数 setex
127.0.0.1:6379> setex day 20 day1
OK
127.0.0.1:6379> keys *
1) "day"
2) "age"
3) "name"
4) "k3"
5) "k1"
6) "k2"
......20s后
127.0.0.1:6379> keys *
1) "age"
2) "name"
3) "k3"
4) "k1"
5) "k2"
*px
:key 的超时毫秒数,与 ex 互斥
127.0.0.1:6379> get name
"wcd"
127.0.0.1:6379> append name 666
(integer) 6 //返回追加后的字符串长度
127.0.0.1:6379> get name
"wcd666"
strlen 获得值的长度
incr 将key中存储的数字增1,只能对数字值操作,如果为空,新增值为1
decr 将key中存储的数字减1,只能对数字值操作,如果为空,新增值为-1
incrby/decrby 将key中存储的数字值进行增减,自定义步长
127.0.0.1:6379> get age
"18"
127.0.0.1:6379> incrby age 20 //decrby同理
(integer) 38 //返回成功后的数字值
127.0.0.1:6379> get age
"38"
原子性
incr,decr,incrby,decrby具备原子性的操作叫做原子操作。所谓原子操作是指不会被线程调度机制打断的操作
这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另 一个线程)。
(1)在单线程中, 能够在单条指令中完成的操作都可以认为是"原子操作",因为中 断只能发生于指令之间。
(2)在多线程中,不能被其它进程(线程)打断的操作就叫原子操作。 Redis 单命令的原子性主要得益于 Redis 的单线程。
mset … 同时设置一个或多个k-v对
mget 同时获取一个或多个value
msetnx … 同时设置一个或多个k-v对,当且仅当所有给定的key都不存在,如果有一个存在,那么该指令操作失败(原子性:要么都成功,有一个失败就失败)
getrange 获得值的范围,类似java中的subString
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> getrange name 0 2
"wcd" //"wcd666" 从0开始到2截取字符串
setrange 用覆盖所存储的字符串值,从开始(索引从0开始)
getset 以旧换新,设置了新值的同时获得旧值
127.0.0.1:6379> getset name zpp
"wcd666" //设置新值"zpp",返回旧值"wcd666"
127.0.0.1:6379> get name
"zpp"
String 的数据结构为 简单动态字符串(Simple Dynamic String,缩写 SDS)。是可以 修改的字符串,内部结构实现上类似于 Java 的 ArrayList,采用预分配冗余空间的方式 来减少内存的频繁分配
如上图所示,内部为当前字符串实际分配的空间 capacity 一般要高于实际字符串长度 len。当字符串长度小于 1M 时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次 只会多扩 1M 的空间。需要注意的是字符串最大长度为 512M。
记住List:“单键多值"
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(Left)或者尾部(Right)
底层是一个双向链表,对双端的操作性都很高,如果通过索引下标的操作方式,中间的节点性能会较差。
List的命令基本都是L(List)开头
127.0.0.1:6379> lpush number 1 2 3
(integer) 3
127.0.0.1:6379> rpush number 4 5 6
(integer) 6
127.0.0.1:6379> lrange number 0 -1
1) "3"
2) "2"
3) "1"
4) "4"
5) "5"
6) "6"
--------------------- ---------------------
lpush(123) 3 2 1 | | | | --> 3 | 2 | 1
---------------------- ----------------------
--------------------- -------------------------
3 2 1 <---rpush(456) 3 2 1 4 5 6
---------------------- --------------------------
lrange key 0 -1 :从左边下标为0的位置到最右边第一个下标-1 之间的所有value
lpop number 1
1) "3"
127.0.0.1:6379> lpush size 10 20 30
(integer) 3
127.0.0.1:6379> rpoplpush size number
"10"
127.0.0.1:6379> lrange number 0 -1
1) "10"
2) "3"
3) "2"
4) "1"
5) "4"
6) "5"
7) "6"
lrange 按照索引下标获得元素(从左到右) lrange mylist 0 -1 0 左边第一个,-1 右边第一个,(0-1 表示获取所有)
lindex 按照索引下标获得元素(从左到右) 下标从0开始
llen 获得列表长度
linsert before/after 在的前面/后面插入
127.0.0.1:6379> lrange size 0 -1
1) "30"
2) "20"
3) "10"
127.0.0.1:6379> linsert size before 30 40
(integer) 4
127.0.0.1:6379> linsert size before 40 50
(integer) 5
127.0.0.1:6379> linsert size after 10 5
(integer) 6
127.0.0.1:6379> lrange size 0 -1
1) "50"
2) "40"
3) "30"
4) "20"
5) "10"
6) "5"
127.0.0.1:6379> lrem size 2 10 //从左到右删除2个value为10的,如果没有就不删除,不足2个也删除,作用就是从左到右批量删除一样的值
(integer) 1
127.0.0.1:6379> lrange size 0 -1
1) "50"
2) "40"
3) "30"
4) "20"
5) "5"
127.0.0.1:6379> lset size 0 100 //将下标0的值替换成100
OK
127.0.0.1:6379> lrange size 0 -1
1) "100"
2) "40"
3) "30"
4) "20"
5) "5"
List的数据结构为快速链表QuickList
首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是 ziplist,也即是 压缩列表它将所有的元素紧挨着一起存储,分配的是一块连续的内存。 当数据量比较多的时候才会改成 quicklist。
因为普通的链表需要的附加指针空间太大,会比较浪费空间。比如这个列表里存的只 是 int 类型的数据,结构上还需要两个额外的指针 prev 和 next。
Redis 将链表和 ziplist 结合起来组成了 quicklist。也就是将多个 ziplist 使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。
”可以看作是有去重功能的List“
Redis set 对外提供的功能与 list 类似是一个列表的功能,特殊之处在于 set 是可以自动排重(类似于MySql的去重distinct)的,当你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择.并且 set 提供了判断某个成员是否在一个 set 集合内的重要接口,这个也是 list 所不能提供的
Redis 的 Set 是 String 类型的无序集合(Zset与之相反)。它底层其实是一个 value 为 null 的 hash 表,所 以添加,删除,查找的复杂度都是 O(1)。
一个算法,随着数据的增加,执行时间的长短,如果是 O(1),数据增加,查找数据的时间不变
Set的命令都以"s”开头
127.0.0.1:6379> sadd s1 v1 v2 v3 v1 v4 //排重,v1重复,只添加1个
(integer) 4
127.0.0.1:6379> smembers s1
1) "v1"
2) "v4"
3) "v3"
4) "v2"
127.0.0.1:6379> sismember s1 v1
(integer) 1
127.0.0.1:6379> scard s1
(integer) 4
127.0.0.1:6379> srem s1 v1 v2
(integer) 2
127.0.0.1:6379> smembers s1
1) "v4"
2) "v3"
127.0.0.1:6379> spop s1 1
1) "v3"
smembers 取出该集合的所有值。
sismember 判断集合是否为含有该值,有 1,没有 0
scard 返回该集合的元素个数。
srem 删除集合中的某元素。
spop 随机从该集合中吐出n个值。
srandmember 随机从该集合中取出 n 个值。不会从集合中删除 。
127.0.0.1:6379> smembers s1
1) "v1"
2) "v2"
3) "v5"
4) "v3"
5) "v4"
127.0.0.1:6379> srandmember s1 2
1) "v1"
2) "v5"
127.0.0.1:6379> smove s1 s2 v1
(integer) 1
127.0.0.1:6379> smove s1 s2 v2
(integer) 1
127.0.0.1:6379> smembers s2
1) "v1"
2) "v2"
Set 数据结构是 dict 字典,字典是用哈希表实现的。 Java 中 HashSet 的内部实现使用的是 HashMap,只不过所有的 value 都指向同一个对象。 Redis 的 set 结构也是一样,它的内部也使用 hash 结构,所有的 value 都指向同一个内部值。
Redis hash 是一个键值对集合
Redis hash 是一个 String 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。 类似 Java 里面的 Map
假设要存储一个用户,用户 ID 为查找的 key,存储的 value用户对象包含姓名,年龄,生日等信息,如果用普通的 key/value 结构来存储,主要有两种方式
而Hash哈希就是上面两种方法的折中,通过key(UserID) + field(属性标签) 就可以操作对应的属性数据,即不需要重读存储数据,也不会带来序列化和并发修改控制的问题
和上面一样,Hash的命令都是"h"开头
hset … 给集合中的键赋值
hget 从集合取出 value
hmset … 批量设置 hash 的值
hmget… 查看hash中field对应的值
hexists查看哈希表 key 中,给定域 field 是否存在。返回1表示存在
hkeys 列出该 hash 集合的所有 field
hvals 列出该 hash 集合的所有 value
hincrby 为哈希表 key 中的域 field 的值加上增量 1 -1
hsetnx 将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在
127.0.0.1:6379> hset user:1001 name wcd age 18 sex male
(integer) 3
127.0.0.1:6379> hget user:1001 name
"wcd"
127.0.0.1:6379> hget user:1001 age
"18"
127.0.0.1:6379> hget user:1001 sex
"male"
127.0.0.1:6379> hmset user:1002 name zpp age 18 sex female
OK
127.0.0.1:6379> hmget user:1002 name age sex
1) "zpp"
2) "18"
3) "female"
127.0.0.1:6379> hkeys user:1001
1) "name"
2) "age"
3) "sex"
127.0.0.1:6379> hvals user:1001
1) "wcd"
2) "18"
3) "male"
127.0.0.1:6379> hincrby user:1001 age 2
(integer) 20
127.0.0.1:6379> hincrby user:1001 age -2
(integer) 18
127.0.0.1:6379> hsetnx user:1001 name wll //name存在,无法重新赋值,返回0
(integer) 0
127.0.0.1:6379> hsetnx user:1001 inters basketball
(integer) 1
Hash 类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当 field-value 长度较短且个数较少时,使用 ziplist,否则使用 hashtable。
Redis 有序集合 zset 与普通集合 set 非常相似,是一个没有重复元素的字符串集合。
不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用 来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复的 。
因为元素是有序的, 所以可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。 访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。
127.0.0.1:6379> zadd scores 100 math 90 english 95 chinese
(integer) 3
127.0.0.1:6379> zrange scores 0 -1 //从小到大
1) "english"
2) "chinese"
3) "math"
127.0.0.1:6379> zrevrange scores 0 -1 //从大到小
1) "math"
2) "chinese"
3) "english"
127.0.0.1:6379> zrange scores 0 -1 withscores //带分数
1) "english"
2) "90"
3) "chinese"
4) "95"
5) "math"
6) "100"
127.0.0.1:6379> zrevrange scores 0 -1 withscores
1) "math"
2) "100"
3) "chinese"
4) "95"
5) "english"
6) "90"
127.0.0.1:6379> zrangebyscore scores 95 100 withscores //根据分数由小到大排序,从95-100
1) "chinese"
2) "95"
3) "math"
4) "100"
127.0.0.1:6379> zrevrangebyscore scores 100 95 withscores //根据分数由大到小排序,从100-95
1) "math"
2) "100"
3) "chinese"
4) "95"
127.0.0.1:6379> zincrby scores 2 english
"92"
127.0.0.1:6379> zrem scores english
(integer) 1
127.0.0.1:6379> zrange scores 0 -1
1) "chinese"
2) "math"
采用zrevrange 根据分数排序
SortedSet(zset)是 Redis 提供的一个非常特别的数据结构,一方面它等价于 Java 的数据结构 Map,可以给每一个元素 value 赋予一个权重 score,另 一方面它又类似于 TreeSet,内部的元素会按照权重 score 进行排序,可以得到每个元 素的名次,还可以通过 score 的范围来获取元素的列表。
zset 底层使用了两个数据结构
(1)hash,hash 的作用就是关联元素 value 和权重 score,保障元素 value 的唯 一性,可以通过元素 value 找到相应的 score 值。
(2)跳跃表,跳跃表的目的在于给元素 value 排序,根据 score 的范围获取元素列表。
跳跃表又称跳表
有序集合在生活中比较常见,例如根据成绩对学生排名,根据得分对玩家排名等。对于有序集合的底层实现,可以用数组、平衡树、链表等。数组对于元素的插入、 删除比较繁琐;平衡树或红黑树虽然效率高但结构复杂;链表查询遍历所有元素时效率低。Redis 采用的是跳跃表。跳跃表效率堪比红黑树,实现远比红黑树简单。
例子:
对比有序链表和跳跃表,从链表中查询出 51
1)有序链表
要查找值为 51 的元素,需要从第一个元素开始依次查找、比较才能找到。共需要 6 次比较。
2)跳表
从第 2 层开始,1 节点比 51 节点小,向后比较。
21 节点比 51 节点小,继续向后比较,后面就是 NULL 了,所以从 21 节点向下到第1层
在第1层,41 节点比 51 节点小,继续向后,61 节点比 51 节点大,所以从 41 向下
在第0层,51 节点为要查找的节点,节点被找到,共查找 4 次。
从此可以看出跳跃表比有序链表效率要高
安装完的redis目录中的conf文件我复制到了 /etc/redis.conf
让我们从上到下看看配置文件
配置了大小单位,只支持bytes,不支持bit
大小写不敏感 (units are case insensitve so 1GB 1Gb 1gB are all the same)
类似于JSP中的include,多实例的情况可以把公用的配置文件提取出来,然后包含到其他配置文件
默认情况 bind=127.0.0.1 只能接受本机的访问请求
不写的情况下,无限制接受任何 ip 地址的访问
生产环境肯定要写你应用服务器的地址;服务器是需要远程访问的,所以需要将其注释掉 如果开启了 protected-mode,那么在没有设定 bind ip 且没有设密码的情况下,Redis 只允许接受本机的响应
保存配置,停止服务,重启启动查看进程,不再是本机访问了。
shutdown
/ linux终端 -->redis-cli shutdown
/ 强制杀死进程 kill- 9 pid
redis-server /etc/redis.conf
ps -ef | grep redis
将本机访问保护模式设置 no,不然就算只改了bind Id,远程ip也无法访问,后续可能还要关闭防火墙
端口号,默认 6379
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0epHybC1-1647841176634)(C:/Users/Lebr7Wcd/AppData/Roaming/Typora/typora-user-images/image-20220320210012502.png)]
四个级别根据使用阶段来选择,生产环境选择 notice 或者 warning
Redis中的(pub/sub)发布与订阅是一种消息通信模式;发送者(publish)发送消息,订阅者(subscriber)接收消息
Redis 客户端可以订阅任意数量的频道
subscriber channel1
publish channerl1 hello
返回的1是订阅者的数量
注:发布的消息没有持久化
ng" alt=“image-20220320212419405” style=“zoom:80%;” />
subscriber channel1
publish channerl1 hello
返回的1是订阅者的数量
注:发布的消息没有持久化