1. NoSQL简介
1.1 数据库应用的演变历史
单机数据库时代、Memcached时代、读写分离时代、分表分库时代(集群)、nosql时代。
1.2 NoSQL数据库
NoSQL = Not Only SQL(不仅仅是SQL) ,泛指non-relational(非关系型数据库)。NoSQL数据库是为了解决大规模数据集合多重数据种类带来的挑战,特别是超大规模数据的存储。NoSQL数据库的一个显著特点就是去掉了关系数据库的关系型特性,数据之间一旦没有关系,使得扩展性、读写性能都大大提高。
1.3 NoSQL的应用
NoSQL和传统的关系型数据库不是排斥和取代的关系,在一个分布式应用中往往是结合使用的。
复杂的互联网应用通常都是多数据源、多数据类型,应该根据数据的使用情况和特点,存放在合适的数据库中。
1.4 NoSQL数据模型
-
传统关系型数据库:表
t_student、t_address、t_course -
NoSql数据库:聚合结构。
{
"student":{
"id":1001,
"name":"zhangsan",
"addresses":{"province":"beijing","city":"daxingqu","street":"liangshuihe"},
"courses":[
{
"id":01,
"name":"java"
},
{
"id":02,
"name":"mybatis"
},
{
"id":03,
"name":"spring"
}
]
}
}
2. Redis
2.1 Redis简介
Remote Dictionary Server(远程字典服务器),是一个用C语言编写的、开源的、基于内存运行并支持持久化的、高性能的NoSQL数据库.也是当前热门的NoSQL数据库之一。
2.2 Redis的特点
1、支持数据持久化
Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
2、支持多种数据结构
Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
3、支持数据备份
Redis支持数据的备份,即master-slave模式的数据备份。
2.3 Redis基本知识
- 测试Redis性能 redis-benchmark
- Redis沟通命令,查看状态 ping 正常返回PONG
- 查看redis服务器的统计信息:info
- redis默认使用16个库
- 切换库命令:select db
- 查看当前数据库中key的数目:dbsize
- 查看当前数据库中有哪些key:keys *
- config get * 获得redis的所有配置值
2.4 Redis的5种数据结构
-
字符串类型 String
字符串类型是Redis中最基本的数据结构,它能存储任何类型的数据,包括二进制数据,序列化后的数据,JSON化的对象甚至是一张图片。最大512M。
-
列表类型 List
Redis列表是简单的字符串列表,按照插入顺序排序,元素可以重复。你可以添加一个元素到列表的头部(左边)或者尾部(右边),底层是个链表结构。
-
集合类型 Set
Redis的Set是string类型的无序无重复集合
-
哈希类型 Hash
Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象
-
有序集合类型 zset
Redis 有序集合zset和集合set一样也是string类型元素的集合,且不允许重复的成员。不同的是zset的每个元素都会关联一个分数(分数可以重复),redis通过分数来为集合中的成员进行从小到大的排序
3. Redis常用操作命令
3.1 keys
语法:keys pattern
作用:查找所有符合模式pattern的key. pattern可以使用通配符
- *:表示0或多个字符,例如:keys * 查询所有的key
- ?:表示单个字符,例如:wo?d , 匹配 word , wood
- [] :表示选择[]内的一个字符,例如wo[or]d, 匹配word, wood, 不匹配wold、woord
3.2 exists
语法:exists key [key…]
作用:判断key是否存在
返回值:整数,存在key返回1,其他返回0。使用多个key,返回存在的key的数量
3.3 move
语法:move key db
作用:移动key到指定的数据库,移动的key在原库被删除。
返回值:移动成功返回1,失败返回0.
3.4 ttl
语法:ttl key
作用:查看key的剩余生存时间(ttl: time to live),以秒为单位。
返回值:
- -1 :没有设置key的生存时间, key永不过期
- -2:key不存在
3.5 expire
语法:expire key seconds
作用:设置key的生存时间,超过时间,key自动删除。单位是秒。
返回值:设置成功返回数字 1,其他情况是 0
3.6 type
语法:type key
作用:查看key所存储值的数据类型
返回值:字符串表示的数据类型
- none (key不存在)
- string
- list
- hash
- set
- zset
3.7 rename
语法:rename key newkey
作用:将key改为名newkey。当 key 和 newkey 相同,或者 key 不存在时,返回一个错误。
当 newkey 已经存在时, RENAME 命令将覆盖旧值。
3.8 del
语法:del key [key…]
作用:删除存在的key,不存在的key忽略。
返回值:数字,删除的key的数量
3.9 set
语法:set key value
功能:将字符串值 value 设置到 key 中,如果key已存在,后放的值会把前放的值覆盖掉。
返回值:OK表示成功
3.10 get
语法:get key
功能:获取 key 中设置的字符串值
返回值:
key存在,返回key对应的value;
key不存在,返回nil
3.11 append
语法:append key value
功能:如果 key 存在,则将 value 追加到 key 原来旧值的末尾
如果 key 不存在,则将key 设置值为 value
返回值:追加字符串之后的总长度(字符个数)
4. redis配置文件
-
存放位置
Redis的安装根目录下(/opt/redis-5.0.2),Redis在启动时会加载这个配置文件,在运行时按照配置进行工作。 这个文件有时候我们会拿出来,单独存放在某一个位置,启动的时候必须明确指定使用哪个配置文件,此文件才会生效。 -
redis网络相关配置
1、 bind:绑定IP地址,其它机器可以通过此IP访问Redis,默认绑定127.0.0.1,也可以修改为本机的IP地址
2、port:配置Redis占用的端口,默认是6379
3、tcp-keepalive:TCP连接保活策略,可以通过tcp-keepalive配置项来进行设置,单位为秒,假如设置为60秒,则server端会每60秒向连接空闲的客户端发起一次ACK请求,以检查客户端是否已经挂掉,对于无响应的客户端则会关闭其连接。如果设置为0,则不会进行保活检测。 -
redis常规配置
1、loglevel:日志级别,开发阶段可以设置成debug,生产阶段通常设置为notice或者warning.
2、logfile:指定日志文件名,如果不指定,Redis只进行标准输出。要保证日志文件所在的目录必须存在,文件可以不存在。还要在redis启动时指定所使用的配置文件,否则配置不起作用
3、databases:配置Redis数据库的个数,默认是16个 -
Redis安全设置
requirepass:配置Redis的访问密码。默认不配置密码,即访问不需要密码验证。此配置项需要在protected-mode=yes时起作用。使用密码登录客户端:redis-cli -h ip -p 6379 -a pwd -
Redis的RDB配置
1、save :配置复合的快照触发条件,即Redis 在seconds秒内key改变changes次,Redis把快照内的数据保存到磁盘中一次。默认的策略是:
1分钟内改变了1万次
或者5分钟内改变了10次
或者15分钟内改变了1次
如果要禁用Redis的持久化功能,则把所有的save配置都注释掉。
2、stop-writes-on-bgsave-error:当bgsave快照操作出错时停止写数据到磁盘,这样能保证内存数据和磁盘数据的一致性,但如果不在乎这种一致性,要在bgsave快照操作出错时继续写操作,这里需要配置为no。
3、rdbcompression:设置对于存储到磁盘中的快照是否进行压缩,设置为yes时,Redis会采用LZF算法进行压缩;如果不想消耗CPU进行压缩的话,可以设置为no,关闭此功能。
4、rdbchecksum:在存储快照以后,还可以让Redis使用CRC64算法来进行数据校验,但这样会消耗一定的性能,如果系统比较在意性能的提升,可以设置为no,关闭此功能。
5、dbfilename:Redis持久化数据生成的文件名,默认是dump.rdb,也可以自己配置。
6、dir:Redis持久化数据生成文件保存的目录,默认是./即redis的启动目录,也可以自己配置。 -
Redis AOF配置
1、appendonly:配置是否开启AOF,yes表示开启,no表示关闭。默认是no。
2、appendfilename:AOF保存文件名
3、appendfsync:AOF异步持久化策略
always:同步持久化,每次发生数据变化会立刻写入到磁盘中。性能较差但数据完整性比较好(慢,安全)
everysec:出厂默认推荐,每秒异步记录一次(默认值)
no:不即时同步,由操作系统决定何时同步。
4、no-appendfsync-on-rewrite:重写时是否可以运用appendsync,默认no,可以保证数据的安全性。
5、auto-aof-rewrite-percentage:设置重写的基准百分比
6、auto-aof-rewrite-min-size:设置重写的基准值
5. Redis的持久化
redis是内存数据库,它把数据存储在内存中,这样在加快读取速度的同时也对数据安全性产生了新的问题,即当redis所在服务器发生宕机后,redis数据库里的所有数据将会全部丢失。为了解决这个问题,redis提供了持久化功能——RDB和AOF(Append Only File)
5.1 RDB
RDB(Redis DataBase)是 Redis 默认的持久化方案。在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis重启会通过加载dump.rdb文件来恢复数据。
- RDB原理
Redis会复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程,来进行持久化。
整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。
如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失 - RDB保存的文件
RDB保存的文件是dump.rdb文件 ,位置保存在Redis的启动目录。Redis每次同步数据到磁盘都会生成一个dump.rdb文件,新的dump.rdb会覆盖旧的dump.rdb文件 - 配置RDB 持久化策略
在redis.conf的快照配置中,配置RDB保存的策略。
在客户端执行FLUSHDB或者FLUSHALL或者SHUTDOWN时,也会把快照中的数据保存到dump.rdb,只不过这种操作已经把数据清空了,保存的也是空文件,没有意义 - 手动保存RDB快照
save命令执行一个同步保存操作,将当前 Redis 实例的所有数据快照(snapshot)以 RDB 文件的形式保存到硬盘。
由于save指令会阻塞所有客户端,所以保存数据库的任务通常由 BGSAVE 命令异步地执行,而save作为保存数据的最后手段来使用,当负责保存数据的后台子进程不幸出现问题时使用 - RDB数据恢复
通过脚本将Redis产生的dump.rdb文件备份(cp dump.rdb dump_bak.rdb),每次启动Redis前,把备份的dump.rdb文件替换到Redis相应的目录(在redis.conf中配的的dir目录)下,Redis启动时会加载dump.rdb文件,并且把数据读到内存中
5.2 AOF
AOF(Append Only File),Redis 默认不开启。它的出现是为了弥补RDB的不足(数据的不一致性),所以它采用日志的形式来记录每个写操作,并追加到文件中。Redis 重启会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
- AOF原理
Redis以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),
只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作 - AOF保存的文件
AOF保存的文件是appendonly.aof文件 ,位置保存在Redis的启动目录。如果开启了AOF,Redis每次记录写操作都会往appendonly.aof文件追加新的日志内容 - 配置AOF持久化策略
在redis.conf的“APPEND ONLY MODE”配置模块中,配置AOF保存策略 - AOF数据恢复
通过脚本将Redis产生的appendonly.aof文件备份(cp appendonly.aof appendonly_bak.aof),每次启动Redis前,把备份的appendonly.aof文件替换到Redis相应的目录(在redis.conf中配的的dir目录)下,只要开启AOF的功能,Redis每次启动,会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
但在实际开发中,可能因为某些原因导致appendonly.aof 文件格式异常,从而导致数据还原失败,可以通过命令redis-check-aof --fix appendonly.aof 进行修复 。会把出现异常的部分往后所有写操作日志去掉 - AOF重写
AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。
AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename),遍历新进程的内存中数据,每条记录有一条的Set语句。重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。
Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发。当然,也可以在配置文件中进行配置 - AOF小结
Redis 需要手动开启AOF持久化方式,AOF 的数据完整性比RDB高,但记录内容多了,会影响数据恢复的效率。
关于Redis持久化的使用:若只打算用Redis 做缓存,可以关闭持久化。若打算使用Redis 的持久化,建议RDB和AOF都开启。其实RDB更适合做数据的备份,留一后手。AOF出问题了,还有RDB。
AOF与RDB模式可以同时启用,这并不冲突。如果AOF是可用的,那Redis启动时将自动加载AOF,这个文件能够提供更好的持久性保障。
6. Redis事务
Redis的事务允许在一次单独的步骤中执行一组命令,并且能够保证将一个事务中的所有命令序列化,然后按顺序执行;在一个Redis事务中,Redis要么执行其中的所有命令,要么什么都不执行。即Redis的事务要能够保证序列化和原子性。
6.1 Redis事务常用命令
- multi
语法:multi
功能:用于标记事务块的开始。Redis会将后续的命令逐个放入队列中,然后才能使用EXEC命令原子化地执行这个命令序列。
返回值:开启成功返回OK - exec
语法:exec
功能:在一个事务中执行所有先前放入队列的命令,然后恢复正常的连接状态。
如果在把命令压入队列的过程中报错,则整个队列中的命令都不会执行,执行结果报错;
如果在压队列的过程中正常,在执行队列中某一个命令报错,则只会影响本条命令的执行结果,其它命令正常运行;
当使用WATCH命令时,只有当受监控的键没有被修改时,EXEC命令才会执行事务中的命令;而一旦执行了exec命令,之前加的所有watch监控全部取消。
返回值:这个命令的返回值是一个数组,其中的每个元素分别是原子化事务中的每个命令的返回值。 当使用WATCH命令时,如果事务执行中止,那么EXEC命令就会返回一个Null值。 - discard
语法:discard
功能:清除所有先前在一个事务中放入队列的命令,并且结束事务。
如果使用了WATCH命令,那么DISCARD命令就会将当前连接监控的所有键取消监控。
返回值:清除成功,返回OK。 - watch
语法:watch key [key …]
功能:当某个事务需要按条件执行时,就要使用这个命令将给定的键设置为受监控的。如果被监控的key值在本事务外有修改时,则本事务所有指令都不会被执行。Watch命令相当于关系型数据库中的乐观锁。
返回值:监控成功,返回OK。 - unwatch
语法:unwatch
功能:清除所有先前为一个事务监控的键。
如果在watch命令之后你调用了EXEC或DISCARD命令,那么就不需要手动调用UNWATCH命令。
返回值:清除成功,返回OK。
6.2 Redis事务总结
- 单独的隔离操作:事务中的所有命令都会序列化、顺序地执行。事务在执行过程中,不会被其它客户端发来的命令请求所打断,除非使用watch命令监控某些键
- 不保证事务的原子性:redis同一个事务中如果一条命令执行失败,其后的命令仍然可能会被执行,redis的事务没有回滚。Redis已经在系统内部进行功能简化,这样可以确保更快的运行速度,因为Redis不需要事务回滚的能力
7. Redis典型应用场景
- 缓存热点数据
热点数据(经常会被查询,但是不经常被修改或者删除的数据),首选是使用redis缓存,redis的性能非常优秀 - 计数器
诸如统计点击数、访问数、点赞数、评论数、浏览数等应用,由于单线程,可以避免并发问题,保证数据的正确性,并且100%毫秒级性能,同时开启Redis持久化,以便于持久化数据。 - 单线程机制
验证前端的重复请求,可以自由扩展类似情况),可以通过redis进行过滤,比如,每次请求将Request IP、参数、接口等hash作为key存储redis(幂等性请求),设置多长时间有效期,然后下次请求过来的时候先在redis中检索有没有这个key,进而验证是不是一定时间内过来的重复提交;再比如,限制用户登录的次数,比如一天错误登录次数10次等。
秒杀系统,基于redis是单线程特征,防止出现数据库超卖;
全局增量ID生成等; - 排行榜
谁得分高谁排名在前,比如点击率最高、活跃度最高、销售数量最高、投票最高的前10名排行等等; - 分布式锁
使用redis可以实现分布式锁,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:
互斥性,在任意时刻,只有一个客户端能持有锁。
不会发生死锁,即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
具有容错性,只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
解铃还须系铃人,加锁和解锁必须是同一个客户端,客户端不能解他人加的锁。 - Session存储
使用Redis的进行会话缓存(session cache)是非常常见的一种场景。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化,目前大量的方案均采用了redis作为session的存储方案。