背景:在传统的Java Web项目中,使用数据库来进行存储数据,但这有一些来自性能方面的弊端。
为什么呢?因为数据库持久化数据主要是面向磁盘,而磁盘的读写比较慢,如果是存在高并发,有瞬间需要读写大量数据的要求的场景(比如即将到来的淘宝双十一,还有抢红包,还有商品抢购场景),一瞬间成千上万的请求发来,需要系统在极短的时间内完成成千上万次的读写操作,这时候就往往不是普通数据库能够承受的了,极其容易造成数据库系统瘫痪,最终导致服务器宕机的严重生产问题。
一、redis介绍
是一种NoSQL技术,NoSQL工具也是一种简易的数据库,主要是一种基于内存的数据库,并提供一定的持久化功能。(PS:MongoDB也是一种NoSQL技术)。redis的性能十分优越,可以支持每秒十几万次的读写操作,并且支持集群、分布式、主从同步等配置,原则上可以无限扩展,让更多的数据存储在内存中,还支持一定的事务能力,这在高并发访问的场景下保证数据安全和一致性特别有用。
从以下三方面体现redis的性能优越性
- 它是基于ANSI C语言写的,接近于汇编语言,运行十分快速。
- 它是基于内存的读写,速度自然比数据库的磁盘读写操作快得多。
- 它的数据库结构只有6中数据类型,数据结构比较简单易用。
redis的常用使用场景
- 缓存常用的数据,如一些命中率高(即使用频率高)的数据。
- 在需要高速读写的场合使用它快速读写,比如抢红包和商品抢购的场景。
redis的缓存应用
在数据库的读写操作中,往往是读操作次数远超写操作次数,当发送SQL去数据库进行读取时,数据库就回去磁盘把对应的数据索引取出来,而索引磁盘是一个相对缓慢的过程。如果把数据直接放在运行在内存的Redis服务器上,那么就不再需要去读写磁盘了,直接读取内存,速度就相应得快很多,并且也极大地减轻数据库的压力。
是否使用redis缓存数据的三方面考虑
- 业务数据常用吗?命中率高?若命中率低就没必要写入缓存。
- 该业务数据是读操作多还是写操作多,若是需要频繁写入数据库的操作也没必要使用缓存。
- 业务数据大小如何?如果存储几百兆的文件也没必要使用缓存,会给缓存带来巨大压力。
高速读写场合
如上面说的淘宝的双十一和春运抢票等场景,一瞬间成千上万的请求服务器,这时应对方法往往是考虑异步写入数据库。即先把高速读写的数据缓存到Redis中,在满足一定的条件下,触发这些缓存的数据写入数据库中。
(举例说明:如在商品秒杀的场景把业务数据先在redis中读写,当一个请求操作完redis的读写后,会去判断该高速读写的业务是否结束,比如这里的条件可以是秒杀商品的剩余数为0,此时判断条件成立则触发事件讲redis缓存的数据以批量的形式一次性写入数据库,从而完成持久化的工作。)
二、Redis的安装
①windows上安装
redis下载地址,download下来后解压缩即可,为了方便在解压后的文件里新建一个startup.cmd文件,在里面写上redis-server redis.windows,conf 然后保存,用于快速启动redis,启动的时候直接双击即可。
在启动redis后还可以使用解压缩文件里的redis-cli.exe,这是一个redis自带的客户端工具。
②linux上安装,这才是实际中最常用的,redis往往安装在服务器端系统。如下步骤:
①使用root用户登录linux系统 即su root "密码"
②在指定目录mkdir新建目录,在切换进入目录
③运行一下命令
wget http://download.redis.io/releases/redis-3.2.4.tar.gz
tar xzf redis-3.2.4.tar.gz //解压文件到当前文件夹的命令
cd redis-3.2.4
make //安装redis的命令
④等待片刻,之后在当前目录执行src/redis-server,就可以看到redis启动好了。
⑤在当前目录可以另外打开一个linux窗口,输入src/redis-cli启动redis自带的命令行窗口
三、Redis的6种数据类型
Redis是一种基于内存的数据库,并且提供一定的持久化功能,它是一种键值对数据库,使用key作为索引找到当前缓存的数据。分别有6种数据类型:字符串(String)、列表(List)、集合(set)、哈希结构(hash)、有序集合(zset)、基数(HyperLogLog)。redis定义的这6种数据类型除了可以存储外,还可以对存储的数据进行一些计算。
此外,Redis还支持一些事务、发布订阅消息模式、主从复制、持久化等作为Java程序猿需要知道的功能。
四、Redis的事务
和其他大多数NoSQL不同,Redis是存在事务的。互联网系统面向的是大众,很多用户同时访问,这时系统的性能就很重要的。在Redis中,也存在多个客户端同时向Redis系统发送命令的并发可能性,因此同一个数据,可能在不同的时刻被不同的线程所操纵,这样就出现了并发下的数据一致的问题。为了保证异性数据的安全性,redis提供了事务方案,而redis的事务是使用multi-exec的命令组合,使用它可以提供以下2个保证:
①事务是一个被隔离的操作,事务中方法都会被redis进行序列化并按顺序执行,事务在执行的过程中不会被其他客户端发生的命令打断
②事务是一个原子性的操作,要么全部执行,要么全不执行。
使用spring中会使用SessionCallback接口进行处理,在redis中使用事务会经过3个过程,分别为:①开启事务,②命令进入队列,③执行事务
常用的Redis事务命令:
1、multi(开启事务)
2、watch(监听某个键,当被监听的键在事务执行前被修改,则事务会被回滚)
3、unwatch(取消监听某个键)
4、exec(执行事务,若被监听的键在事务执行前被修改,则事务会被回滚,没有的话则执行命令)
5、discard(回滚进入队列的事务命令,之后不能用exec提交事务)
下图表明开启事务后输入的命令在没有exec执行的时候是一直都存在队列里的。
PS:注意一点:Redis在命令入队的时候,就会检查事务的命令是否正确,若不正确则之前之后的命令都会回滚,若事务命令正确则执行。可当命令格式正确,而因为数据结构错误,则该命令执行会报错,而其之前之后的命令都会正常执行,这点就和数据库很不一样。
多线程使用的CAS与乐观锁
当一条线程执行某个业务逻辑,但这条业务逻辑操作的数据被其他线程共享了,这样就会引发线程中数据不一致的情况。为了解决这问题,在读取这些多线程共享的数据时将其保存到当前线程的副本中,成为旧值,之后在执行更新前比较当前线程副本保存的旧值和当前的线程共享的值是否一致,若不一致则更新失败。
由CAS产生而来的ABA问题:即多个线程访问了同一数据,经过一阵修改,改去又改回,最后的值还是和旧值一致,这样redis事务就认为正常事务,但是数据已经被修改过了。
在redis执行事务的过程中,并不会阻塞其他连接的并发,而只是通过比较watch监控的键值对去保证数据的一致性,所以redis多个事务完全可以在非阻塞的多线程环境下并发执行,而且redis的机制是不会产生ABA问题的。
五、Redis总结
Redis和数据库一样可以存储数据,但不代表Redis会取代数据库。有以下原因:
① 虽然NoSQL数据结构比较简单,能处理很多问题,但是其功能毕竟有限,没有SQL语句强大,支持复杂的计算
②NoSQL并不完全安全稳定,由于它基于内存,一旦停电或者机器故障数据就很容易丢失了,而且持久化能力也是有限的
③其数据完整性、事务能力、安全性、可靠性、扩展性都不如数据库
所以,NoSQL和传统数据库各有优势,NoSQL的主要应用场景在于对性能有要求的地方,如果一个系统对性能要求不大,则也没什么必要使用NoSQL技术,传统数据库便可适用。