Redis理解和使用
摘抄并用于自查笔记
1. Redis简介
我们日常Java Web开发,一般使用数据库进行存储,在数据量较大的情况下,单一使用数据库保存数据的系统会因为面向磁盘,磁盘读写速度比较慢而存在严重的性能弊端,一瞬间成千上万的请求到来,需要系统在极短时间内完成千万次的读写操作,这个时候数据库承受不了,易造成数据库瘫痪。为克服此类问题,Java Web项目通常引入Nosql技术,这是一种基于内存的数据库,并且提供一定的持久化功能。
Redis是一个key-value存储系统,可支持五种数据类型:字符串、哈希、链表、集合和有序集合。其性能十分优越,可支持每秒十几万次的读写操作,性能远超数据库,且支持集群,分布式,主从同步等配置,原则上可以无限扩展,让更多人数据存储在内存中,支持一定的事务能力,保证高并发下数据的安全和一致性。
2. Redis在Java Web中的应用
Redis在Java Web主要有两个应用场景:
1)存储缓存用的数据
2)需要高速读写的场合使用他快速读写
缓存
根据业务需求,在Redis中存储一些常用和主要的数据,一般从以下几个方面考虑:
业务数据常用吗?如果是命中率低,没有必要写入缓存。
命中率如何?如果命中率低,就没有必要写入缓存。
读操作多还是写操作多?如果写操作多,频繁写入数据库,也没有必要使用缓存。
业务数据大小如何?如果要存储几百M字节的文件,会给缓存带来很大压力,这样也没必要。
高速读写的场合
在高并发的情况下,例如抢演唱会门票,仅用数据库会比较慢或数据库瘫痪,所以需要使用Redis来应对这样的高并发需求的场合。
i)当一个请求到达服务器时,只是把业务数据在Redis上进行读写,而没有对数据进行任何的操作,这样就能大大提高读写速度,从而满足高速响应的需求
ii)但是这些缓存的数据仍需要持久化,也就是存入数据库之中,所以在一个请求操作完Redis的读写后,会去判断该高速读写的业务是否结束,这个判断如果是成功了,则触发事件将Redis缓存的数据以批量的形式一次性写入数据库,从而完成持久化的工作。
3. Redis常用数据类型操作和其他操作
1)String
String是redis最基本的数据类型。String类型是二进制安全的,意思是,redis的String可以包含任何数据,比如,jpg或者序列化的对象,其最大能存储512MB
2)Hash(哈希)
Redis Hash是一个键值对集合。hash特别适合用于存储对象。
HMSET runoob field1 "Hello" field2 "World"
HGET runoob field1
HGET runoob field2
3) List(列表)
Redis列表是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部或者尾部。
4)Set(集合)
Redis的Set是String类型的无需集合,集合是通过哈希表实现的,所以添加、删除、查找的复杂度都是o(1)
5)zset(sorted set:有序集合)
zset和set一样是String类型元素的集合,且不允许出现重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序,zset的成员是唯一的,但分数却是可以重复的。
6)Redis HyperLogLog
Redis HyperLogLog是用来做基数统计的算法,HyperLogLog的优点是在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的,且是很小。
7)Redis 发布订阅
Redis发布订阅是一种消息通信模式。
8)Redis事务
MULTI, EXEC, DISCARD和WATCH命令是Redis事务操作的基础。他们可以让Redis在一个步骤里面执行一组命令,且能做到如下2个保证:
i)事务中所有的命令都是序列化且都是按顺序执行的,在一个客户端执行Redis事务的过程中,不会接收其他任何客户端对他发出的请求。这保证了这些命令是作为一个单独的独立操作执行的。
ii)所有的命令要么都被一起处理,要么全部都没有被处理,所以Redis事务是原子的。EXEC会命令触发事务中所有命令的执行。
使用MULTI命令进入事务模式,这个命令只会返回OK,这个时候,用户发出多个要执行的命令,Redis暂时不会执行这些命令,二十把他们放进队列,当EXEC被调用时,所有的命令才会被一次性执行。
在其中可能会有2种错误:
i)命令可能排队失败。如,命令的语法可能错误,或者重要的环境问题,内存不足。
ii)EXEC调用后,一些命令可能执行失败,如,在一个字符串上进行列表命令的操作。
第一种错误,发生在EXEC命令前,可以通过检查命令的返回值,QUEUED,redis对于在排队期间发生的错误,会拒绝执行EXEC,并放弃这个事务。
执行EXEC后,所有的命令都会被执行,甚至是错误的命令。就算其中有失败的命令,队列中的其他命令也会被执行。
为什么Redis不支持回滚
EXEC命令调用后的错误(这个问题在命令队列时无法检测到),Redis命令执行失败,都是程序性错误,这类错误在开发过程中就能够发现并解决,几乎不会出现在生产环境。
由于不需要回滚,这使得redis内部更加简单,而且运行速度更快
乐观锁(check-and-set)
WATCH命令为事务提供一个check-and-set(CAS)行为。
WATCH命令可以用来监听事务中的队列的命令,在EXEC之前,一旦发现有参数是被修改了的,那么整个事务就会终止,EXEC返回一个NULL,提示用户事务失败了。这种形式的锁称为乐观锁,是一种非常强大的锁。
WATCH命令使得EXEC命令的执行必须满足一个条件:如果被WATCH的keys没有一个被更改,则执行事务;不然就不执行这个事务。
WATCH可以被多次调用,所有的WATCH调用都会在EXEC调用之前起作用,WATCH可以接收任意多的key。
当EXEC被调用后,所有的keys都将UNWATCH,不管这个事务会不会终止。同样,当一个客户端链接关闭后,一切都将UNWATCH。
可以用UNWATCH命令来刷新所有被WATCH的keys。
这个常用于高并发的情况,例如,抢红包或者商品。
9)Redis持久化
Redis支持RDB和AOF两种持久化机制。持久化能有效避免因进程退出造成的数据丢失问题,下一次重启时,利用之前持久化的文件,即可实现数据恢复。
1. RDB:这种是把当前进程数据生成快照保存到硬盘的过程,触发RDB持久化过程分为手动触发和自动触发。
手动触发:save和bgsave。save命令阻塞当前Redis服务(主进程),bgsave命令Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束,阻塞只发生在fork阶段,时间短。(fork解释 : 一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。)
自动触发:有以下几种场景会触发
a)使用save相关配置,如 save m n , 表示m秒内数据集存在n次修改时,自动触发bgsave
b)如果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点。
c)执行debug reload命令重新加载Redis时,也会自动触发save操作。
d)默认情况下执行shutdown命令时,如果没有开启AOF持久化功能则自动执行bgsave
保存:RDB文件保存在dir配置指定的目录下,文件名通过dbfilename配置指定。
优点:非常适用备份,全量赋值等场景,恢复数据远快于AOF方法。
缺点:没有办法做到实时持久化/秒级持久化。其文件格式存在多版本格式不兼容情况。
2. AOF
开启AOF功能需要设置配置:appendonly yes。默认不开启。文件名通过appendfilename配置,默认appendonly.aof。
a)所有写入命令会追加到aof_buf(缓冲区)中。
b)AOF缓冲区根据对应的策略向硬盘做同步操作
c)随着AOF文件越来越大,需要定去对AOF文件进行重写,达到压缩目的
d)当Redis服务器重启时,可以加载AOF文件进行数据恢复
如果打算使用 redis 的持久化,建议 RDB 和 AOF 都开启。其实,RDB 更适合做数据的备份,留一后手,AOF 出问题了,还有 RDB。
10)管道技术
Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。这意味着通常情况下一个请求会遵循以下步骤:
客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务器端响应,服务端处理命令,并将结果返回客户单。
但是Redis管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次读取所有服务端的响应。
管道技术最显著的优势是提高了redis服务的性能。开启管道操作后,往返延时被改善的相当低。