Redis数据结构类型和持久化

介绍

Redis官网:http://redis.io

国内社区:http://www.redis.cn

http://nosql-database.org 列出了世界上目前正在使用的nosql产品

nosql产品特点:

1、一般不适用严格的表结构(由行和列组成的表)

2、数据查询一般都不再使用sql查询

常见nosql的产品 比较:

类型 主要产品 简介
KV存储 Redis/Memcached 使用key快速查到其value,Memcached支持string类型的value,Redis除string类型外还支持set、hash、sort set、list等类型
文档存储 MongoDB
/CouchDB
使用JSON或类JSON的BSON数据结构,存储内容为文档型,能实现部分关系数据库的功能
列存储 HBase
/Cassandra
按照列进行数据存储,便于存储结构化和半结构化数据,方便做数据压缩和针对某一列和某几列的数据查询
图存储 Neo4J
/FlockDB
图形关系的存储,能够很好弥补关系数据库在图形存储的不足
对象存储 Db4o
/Versant
通过类似面向对象语言的方式操作数据库,通过对象的方式存取数据
XML存储 Berkeley DB XML
/BaseX
高效存储XML数据,支持XML的内部查询语言,如XQuery、XPath

总结:redis是个key-value类型的nosql产品,数据在内存中获取,同时支持数据的持久化操作,保证了数据安全。注意:数据是无价的。

Redis安装

window系统下Redis的安装

redis安装包中文件介绍

在命令行工具窗口启动redis服务端

1
2
3
4
5
6
7
8
9
启动客户端
redis -cli.exe -h 192.168.1.11 -p 6379
测试:
>set name asion //设置个string类型数据,key=name,value=asion
>get name //获取name的value

-h redis服务器的IP地址
-p redis的端口 默认6379
按ctrl+c 关闭redis服务

Linux下redis的安装,需要redis源码包和php扩展包

xshell操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
make命令:
make是用来编译的,它从Makefile中读取指令,然后编译。大多数的源代码包都经过这一步进行编译(当然有些perl或python编写的软件需要调用perl或python来进行编译)

make all:编译程序、库、文档等(等同于make)
make install:安装已经编译好的程序(有些软件需要先运行 make check 或 make test来进行一些测试),这一步一般需要你有 root 权限(因为要向系统写入文件),复制文件树中到文件到指定的位置。
make unistall:卸载已经安装的程序。
make clean:删除由make命令产生的文件
make distclean:删除由./configure产生的文件
make check:测试刚刚编译的软件(某些程序可能不支持)
make installcheck:检查安装的库和程序(某些程序可能不支持)
make dist:重新打包成packname-version.tar.gz

//连接ftp(用FlashFXP软件),上传redis源码包和php扩展包到linux系统,cp包到 linux下 /user/local/src/
#service vsftpd status (查看ftp服务有没有开启)
#tar -zxvf redis-3.0.7.tar.gz 进入目录,并进行解压,进入源码包目录
#make(直接make,不需要使用configure过程,这是编译)
#make PREFIX=/user/local/redis install(指定安装目录)

#mkdir /etc/redis //安装redis到指定目录
#cp redis.conf /etc/redis/
# ./redis-server //cd 切换到redis/bin,启动服务,可以ctrl +c关闭服务
#vim /etc/redis/redis.conf //使redis服务在后台运行,修改配置文件redis.conf,将daemonize no 改为daemonize yes完成保存

在bin目录下
#./redis-server /etc/redis/redis.conf //使用配置文件启动服务

查看redis服务是否正常启动
#ps axu |grep redis-server //使用ps查看,看到./redis-server*:6379
#netstat -tunple | grep 6379 //使用netstat查看,看到LISTEN和redis-server
#pkill -9 redis-server //关闭后台redis服务,用pkill关闭

//在用ps或netstat查看
//使用客户端连接服务
#./redis-server /etc/redis/redis.conf(确保服务启动)
#./redis-cli -h 192.168.1.11 -p 6379

测试
>set name asion(set命令,key是name,value是asion )
>get name(获取value值)

redis数据结构类型

string
1
2
3
4
5
6
7
>set name asion //set设置,key是name,value是asion
>incr age //对key为age的value +1操作
>decr age //对key为age的value -1操作
>incrby age 23 //对key为age的value+23
>decrby age 23 //对key为age的value-23
>keys * //获取redis中所有key
>del age //对key为age的进行删除操作

redis中对key的设计

场景:

获得最新的10个登录用户信息:select * from user order by logintime desc limit 10;

mysql中it_user表

1
2
3
4
it_user:id:1:username 保存username,若保存email,则将username换成email
表名:主键字段:主键值:其他字段
>set it_user:id:1:username asion //设置key为it_user:id:1:username,value为asion
>keys it_user:id:1* //h获取id为1的用户的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1.Redis的数据是共享的:
redis集群,如果将用户信息存储在web服务的本地缓存,则每个web服务都会缓存一份,当用户修改昵称时,需要通知其它web服务更新用户缓存。
如果将用户信息存储在Redis,则只有一份缓存,所有的web访问的都是同一份缓存,当用户修改昵称时,所有web服务都能同时访问到最新的缓存。

2.Redis是单线程的
由于Redis的性能瓶颈在于内存读写速度,而不是CPU,设计者将Redis设计成了单线程模式,其所有操作都是原子性的,避免了多线程带来的复杂性。

应用场景
1.计数器
string类型的incr和decr命令的作用是将key中储存的数字值加一/减一,这两个操作具有原子性,总能安全地进行加减操作,因此可以用string类型进行计数,如微博的评论数、点赞数、分享数,抖音作品的收藏数,京东商品的销售量、评价数等。
2.分布式锁
string类型的setnx的作用是“当key不存在时,设值并返回1,当key已经存在时,不设值并返回0”,“判断key是否存在”和“设值”两个操作是原子性地执行的,因此可以用string类型作为分布式锁,返回1表示获得锁,返回0表示没有获得锁。
例如,为了保证定时任务的高可用,往往会同时部署多个具备相同定时任务的服务,但是业务上只希望其中的某一台服务执行定时任务,当定时任务的时间点触发时,多个服务同时竞争一个分布式锁,获取到锁的执行定时任务,没获取到的放弃执行定时任务。定时任务执行完时通过del命令删除key即释放锁,如果担心del命令操作失败而导致锁一直未释放,可以通过expire命令给锁设置一个合理的自动过期时间,确保即使del命令失败,锁也能被释放。不过expire命令同样存在失败的可能性,如果你用的是Java语言,建议使用JedisCommands接口提供的String set(String key, String value, String nxxx, String expx, long time)方法,这个方法可以将setnx和expire原子性地执行,其它语言的Redis客户端也应当提供了类似的方法。
3存储对象
利用JSON强大的兼容性、可读性和易用性,将对象转换为JSON字符串,再存储在string类型中,是个不错的选择,如用户信息、商品信息等。

string底层是简单动态字符串(simple dynamic string, SDS)

C字符串 SDS
获取字符串长度的复杂度为O(N) 获取字符串长度的复杂度为O(1)
API是不安全的,可能会造成缓冲区溢出 API是安全的,不会造成缓冲区溢出
修改字符串长度N次必然需要执行N次内存重分配 修改字符串长度N次最多需要执行N次内存重分配
只能保存文本数据 可以保存文本或者二进制数据
可以使用所有库中的函数 可以使用一部分库的函数

参考:https://blog.csdn.net/pugongying_95/article/details/99718749

hash
1
2
3
4
5
6
7
>hset userInfo name asion
//hsetd命令设置单个,key为userInfo,value为name asion
//类似于php中关联数组$userInfo=array('name'=>'asion')
>hget userInfo name //获取单个值value
>hmset userInfo age 12 email gogo@163.com
//hmset设置多个,获取单个值value是一样的操作,获取userInfo全部value值,用hgetall
>hgetall userInfo
1
2
3
4
1.购物车
以用户id为key,商品id为field,商品数量为value,恰好构成了购物车的3个要素。
2.存储对象
hash类型的(key, field, value)的结构与对象的(对象id, 属性, 值)的结构相似,也可以用来存储对象。

参考:https://www.cnblogs.com/pangzizhe/p/10657801.html

list链表

左侧头部,右侧尾部

1
2
3
4
5
lpush link1 a	//从左侧(头部)向链表link1压入a
rpush link2 A //从右侧(尾部)向链表link2压入A
lrang link1 0 -1//从左侧开始取link1数据,0是第一个单元,-1代表最后一个单元
lpop link1 //弹出link1左侧第一个元素
rpop link1 //弹出link1右侧第一个元素

1
2
3
4
5
6
1.消息队列
list类型的lpop和rpush(或者反过来,lpush和rpop)能实现队列的功能,故而可以用Redis的list类型实现简单的点对点的消息队列。不过我不推荐在实战中这么使用,因为现在已经有Kafka、NSQ、RabbitMQ等成熟的消息队列了,它们的功能已经很完善了,除非是为了更深入地理解消息队列,不然我觉得没必要去重复造轮子。
2.排行版
list类型的lrange命令可以分页查看队列中的数据。
3.最新列表
list类型的lpush命令和lrange命令能实现最新列表的功能,每次通过lpush命令往列表里插入新的元素,然后通过lrange命令读取最新的元素列表,如朋友圈的点赞列表、评论列表

https://www.cnblogs.com/pangzizhe/p/10674501.html
秒杀场景的使用,作为参考:
https://blog.csdn.net/a7442358/article/details/102463137

set集合

三特定:

1、数据彼此没有顺序之分

2、数据彼此不重复

3、数据的个数是确定的

常见运算:

交集、并集、差集

1
2
3
4
sadd set1 a //像集合set1中添加单元a
smembers set1 //获取集合set1中的所有单元
srem set1 a //移除集合set1中的a单元
spop set1 //随机弹出集合中的一个单元

社交类网站中用于推荐好友、共同好友等。

1
2
3
4
5
6
7
8
sinter set:user:id:3:friend set:user:id:45:friend
//3号用户和45号用户的共同好友(求交集)

sunion set:user:id:3:friend set:user:id:45:friend
//3号用户和45号用户的全部好友(并集)

sdiff set:user:id:3:friend set:user:id:45:friend
//让3号用户给45号用户推荐好友(差集)

1
2
3
4
5
6
7
8
9
10
1.好友/关注/粉丝/感兴趣的人
set类型唯一的特点使得其适合用于存储好友/关注/粉丝/感兴趣的人集合,集合中的元素数量可能很多,每次全部取出来成本不小,set类型提供了一些很实用的命令用于直接操作这些集合,如
a. sinter命令可以获得A和B两个用户的共同好友
b. sismember命令可以判断A是否是B的好友
c. scard命令可以获取好友数量
d. 关注时,smove命令可以将B从A的粉丝集合转移到A的好友集合
2.随机展示
通常,app首页的展示区域有限,但是又不能总是展示固定的内容,一种做法是先确定一批需要展示的内容,再从中随机获取
3.黑名单/白名单
经常有业务出于安全性方面的考虑,需要设置用户黑名单、ip黑名单、设备黑名单等,set类型适合存储这些黑名单数据,sismember命令可用于判断用户、ip、设备是否处于黑名单之中

参考:https://www.cnblogs.com/pangzizhe/p/10723019.html

zset(sort set)

三特点:

1、数据彼此有顺序之分(权重:如1-9阿拉伯数字或者 a-z字母顺序等)

2、数据彼此不重复

3、数据的个数是确定的

1
2
3
4
//给class:phpRank的集合中添加权重为5的mark
zadd class:phpRank 5 mark
//显示集合中的元素和对应权重(withscores按权重排好序了)
zrange class:phpRank 0 -1 withscores

应用场景

1
2
3
4
5
6
1.延时队列
score作为时间戳,自动按照时间最近的进行排序,启一个线程持续poll并设置park时间,完成延迟队列的设计
2.排行榜
score作为浏览次数,自动进行排序,但要注意冷数据
3.滑动窗口限流
score作为时间戳,可统计最近一段时间内内的成员数量,实现滑动窗口限流

参考:https://zhuanlan.zhihu.com/p/147912757

redis持久化功能

redis为了本身数据的安全和完整性,会将内存中的数据按照一定的方法同步到电脑的磁盘中,这个过程被称为持久化操作;下次再次启动redis服务时,会将磁盘上保存的数据重新的加载到内存中。

常见的持久化操作方式有两种:

a基于快照的方式:按照一定周期把将内存中的数据同步到磁盘文件中。

b基于日志文件的追加:把对redis数据造成更改的命令记录到日志文件里面,然后在再一次重启的时候,执行一下日志文件里面对redis写的操作,达到数据还原的目的。

持久化技术对于Redis服务性能来说是有损的

基于快照的方式

1、修改配置文件

1
2
3
#vim /etc/redis/redis.conf
:/save搜索save,按n下一个
找到内容->基于周期性持久化,只要任何一个条件达到,就会触发

save 900 1 含义,如果在900s内对redis的key进行过1次操作,则会把内存里面的数据同步到磁盘文件

save 300 10 含义,如果在300s内对redis的key进行过10次操作,则会把内存里面的数据同步到磁盘文件

save 60 10000 含义,如果在60s内对redis的key进行过10000次操作,则会把内存里面的数据同步到磁盘文件

保存磁盘的文件位置

实操:

1
2
>bgsave	//人为的触发基于快照的持久化操作
#pkill -9 redis -sever //人为的模拟服务宕机
基于文件追加的方式

1、修改redis.conf文件

1
#vim /etc/redis/redis.conf

备份文件的周期

appendfsync always 只要存在对redis数据造成更改的操作,都要记录到磁盘文件上

appendfsync everysec 每1s进行一次,把对redis数据造成更改的操作,都要记录到磁盘文件上

appendfsync no 完全交给操作系统来完成,即当操作系统不繁忙的时候,会把对redis数据造成更改的操作,都要记录到磁盘文件上,这种操作最不靠谱。

实操:基于文件追加方式的文件已经存在

基于快照和日志文件的持久化场景

RDB(快照)和AOF(日志)区别场景分析

RDB

优点:

  • RDB快照是一个压缩过的非常紧凑的文件,保存着某个时间点的数据集,适合做数据的备份,灾难恢复;
  • 可以最大化Redis的的性能,在保存RDB文件,服务器进程只需要fork一个子进程来完成RDB文件的创建,父进程不需要做IO操作;
  • 与AOF相比,恢复大数据集的时候会更快;

缺点:

  • RDB的数据安全性是不如AOF的,保存整个数据集的过程是比繁重的,根据配置可能要几分钟才快照一次,如果服务器宕机,那么就可能丢失几分钟的数据;
  • Redis数据集较大时,fork的子进程要完成快照会比较耗CPU、耗时;

AOF

优点:

  • 数据更完整,安全性更高,秒级数据丢失(取决fsync策略,如果是everysec,最多丢失1秒的数据);
  • AOF文件是一个只进行追加的日志文件,且写入操作是以Redis协议格式保存,内容是可读的,适合误删紧急恢复;

缺点:

  • 对于相同的数据集,AOF文件的体积要大于RDB文件,数据恢复也会比较慢;
  • 根据所使用的fsync策略,AOF的速度可能会慢于RDB(性能降低的影响)。 不过在一般情况下,每秒fsync的性能依然非常高;

RDB和AOF的选择

通常来说,应该同时使用两种持久化方案,以保证数据安全。

  • 如果数据不敏感,且可以从其他地方重新生成,可以关闭持久化。
  • 如果数据比较重要,且能够承受几分钟的数据丢失,比如缓存等,只需要使用RDB即可。如果不能承受数分钟以内得数据丢失,对业务数据非常敏感,选用AOF
  • 如果是用做内存数据,要使用Redis的持久化,建议是RDB和AOF都开启。
  • 如果只用AOF,优先使用everysec的配置选择,因为它在可靠性和性能之间取了一个平衡。

灾难恢复选用RDB双保险策略,当RDB与AOF两种方式都开启时,Redis会优先使用AOF恢复数据,因为AOF保存的文件比RDB文件更完整。

参考官网:https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/

持久化应用场景

1Redis应用于抢购、限购、限量发放优惠券、激活等业务的数据存储设计

2Redis应用于具有操作先后顺序的数据控制

3Redis应用于最新消息展示

4Redis应用于基于黑名单与白名单设定的服务控制

5Redis应用于计数器组合排序功能对应的排名

6Redis应用于按次结算的服务控制

参考:https://blog.csdn.net/weixin_35099248/article/details/111577925

mongodb的高并发处理https://www.cnblogs.com/hulianwangjiagoushi/p/12073244.html