Redis应用

来源:互联网 发布:win的网络扫描仪 编辑:程序博客网 时间:2024/05/02 01:30

1        Redis总结

    缓存在电商项目乃至互联网项目使用之广,使用的点也非常之多,基本想到的地方就都有缓存。前面我们都是用户请求,我们从数据库查询数据,然后封装数据,封装成指定的数据结构。这个过程实际上是非常损耗性能的。那我们来想,像首页的广告内容,数据库中不常变化的字段数据,实际变化的并不很频繁,这就可以把这部分内容缓存起来,然后下次请求就可以直接从缓存中获取。

    缓存在品牌项目中是经常使用的,例如在我们执行全量导入的过程中经常会出现这样那样的问题导致不能全部将mysql数据库中数据全部导入进而影响工作。

    Redis在互联网企业中分布的极其广泛,而使用起来也特别简单,没什么难度。

1.1        Redis介绍:

1.1.1   Redis简介

Redis ~ REmote DIctionary Server,可以直接理解为远程字典服务。

官网:http://redis.io

Redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。

Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。

可以充当缓存、队列等作用。

1.1.2   Redis的特性

1)基本的5种数据类型存储

Ÿ   字符串类型

Ÿ   散列类型

Ÿ   列表类型

Ÿ   集合类型

Ÿ   有序集合类型

2)内存存储与持久化

Ÿ   内存的读写速度远快于硬盘

Ÿ   自身提供了持久化功能(RDB、AOF两种方式)

RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)。

AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF文件中的命令全部以Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。

AOF十分强大,正由于这个特性,它可以轻松实现主从复制。

Redis 还可以同时使用 AOF 持久化和 RDB 持久化。在这种情况下,当 Redis 重启时,它会优先使用 AOF 文件来还原数据集,因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。

snapshotting是默认的持久化方式,这种方式是将内存中数据以快照的方式写入二进制文件中,默认文件名为dump.rdb.

可以通过配置设置自动做快照持久化的方式。我们可以配置redis在n秒内如果超过m个key修改就自动做快照。

save 900 1 #900秒内如果超过1个键被修改,则发起快照保存。

save 300 10 #300秒内如果超过10个键被修改,则发起快照保存

save 60 10000

Redis默认会将快照文件存储在当前目录的dump.rdb文件中,通过配置dir和dbfilename两个参数分别指定快照文件的存储路径和文件名

甚至可以关闭持久化功能,让数据只在服务器运行时存在。(此时就类似于memcache)

3)功能丰富

Ÿ   可以用作缓存、队列、消息订阅/发布

Ÿ   支持键的生存时间

Ÿ   按照一定规则删除相应的键(内存的有限性)

4)简单稳定

Ÿ   相比SQL而言更加简洁

Ÿ   不同语言的客户端丰富

Ÿ   基于C语言开发,代码量只有3万多行

1.2       Redis安装和启动

1.2.1   *Linux下安装Redis

    (我使用的linux是centos 6.5版本)

//安装c支持

yum –y install cpp binutils glibc glibc-kernheadersglibc-common glibc-devel gcc make gcc-c++ libstdc++-devel tcl

    mkdir–p /usr/local/src/redis

    cd/usr/local/src/redis

    wgethttp://download.redis.io/releases/redis-3.0.2.tar.gz

    tar-xvf redis-3.0.2.tar.gz

    cdredis-3.0.2

    make   //编译过程比较长

       控制台显示“Hint:It’s a good idea to run ‘make test’:)”编译成功

    maketest #这个尽量不执行

    makeinstall

    mkdir/etc/redis

    cpredis.conf /etc/redis/6379.conf

    vi/etc/redis/6379.conf(redis.conf

    #修改如下,默认为no

    daemonizeyes //改成系统守护进程

    /deamonize搜索deamonize,下一个按n键,找到后按i,改成插入模式,进行修改。按ESC,输入:wq,保存退出。

1.2.2   *Linux启动和测试

#加上`&`号使redis以后台程序方式运行

    redis-server/etc/redis/redis.conf &

 

    ps–ef | grep redis  //服务正常启动的话,可以搜索到服务,端口为6379。

    kill9 进程号         //强制杀死进程

    #测试

redis-cli -p 6379

    ping/PONG

    参考前面打开6379端口。

1.2.3   Redis配置文件说明

在linux平台下默认的配置文件是:more/etc/redis/redis.conf。这里面配置了非常多的信息,一般配置保持默认,在一些特定的场景下可以自定义配置,常用到的配置项如下:

port 服务端口

bind 绑定ip其他ip不能访问(多个ip空格隔开)

databases 数据库数量,默认16个

daemonize设置为守护进程(linux平台)

maxmemory最大的内存大小(1MB、1GB、1m、1g)

maxmemory-policy达到内存限制后的处理策略(后面详细说明)

注意:修改后配置文件需要重启redis服务才能生效。

1.2.4   内存的清理策略Maxmemory-policy策略

Ÿ   Volatile-lru:使用LRU算法删除一个键(只对设置了生存时间的键)

Ÿ   Allkeys-lru:使用LRU算法删除一个键

Ÿ   Volatile-random:随机删除一个键(只对设置了生存时间的键)

Ÿ   Allkeys-random:随机删除一个键

Ÿ   Volatile-ttl:删除生存时间最近的一个键

Ÿ   Noeviction:不删除键,只返回错误

LUR(Least Recently Used)算法:最近最少使用

 

端口配置:

默认6379

配置数据库数量:

Redis默认开启16个数据库,不能像mysql自定义数据库名称,只能是数值,不能修改。

配置内存大小:

会生成一个和内存大小一样的文件,如果磁盘不够大小或者是文件系统是FAT32,修改最大内存。

maxmemory 200mb

1.3       Redis命令

1.3.1   redis-cli的使用之发送命令

默认连接:IP127.0.0.1 端口 6379

redis-cli

指定IP端口:

    redis-cli –h127.0.0.1 –p 6379

    使用ping命令测试与客户端和服务器链接是否正常

    redis-cliping

    或

    redis-cli

    redis127.0.0.1:6379>ping

    PONG

1.3.2   redis-cli的使用之命令返回值

状态回复(最简单的回复-redis提供的测试命令)

redis>PING

PONG

127.0.0.1:6379>SET test 123

OK

错误回复(以error开头,后面跟着错误信息)

127.0.0.1:6379>TEST

(error) ERR unknown command 'TEST'

整数回复

127.0.0.1:6379>INCR test_incr

(integer) 1

字符串回复(最长久的一种回复,双引号包裹)

127.0.0.1:6379>get test

“123”

多行字符串回复

127.0.0.1:6379>KEYS *

1) "test_incr"

2) "test"

1.3.3   redis的基本命令之HELP

127.0.0.1:6379> help

redis-cli 2.8.19

Type: "help @<group>" to get a listof commands in <group>

     "help <command>" for help on <command>

     "help <tab>" to get a list of possible help topics

     "quit" to exit

127.0.0.1:6379> help type

 

  TYPE key

  summary:Determine the type stored at key

  since:1.0.0

  group:generic

 

官网:http://www.redis.io帮助

1.3.4   redis多数据库

redis默认支持16个数据库,对外都是以一个从0开始的递增数字命名,可以通过参数database来修改默认数据库个数。客户端连接redis服务后会自动选择0号数据库,可以通过select命令更换数据库,例如选择1号数据库:

127.0.0.1:6379>SELECT 1

OK

127.0.0.1:6379>GET test

(nil)

说明:

Redis不支持自定义数据库名称。

Redis不支持为每个数据库设置访问密码。

Redis的多个数据库之间不是安全隔离的,FLUSHALL命令会清空所有数据库的数据。

1.3.5   redis的基本命令之KEYS

获取符合规则的建名列表。

KEYS *

keys test[_]*

keys t[a-d]

说明:

?  匹配一个字符

*   匹配任意个(包括0个)字符

[]  匹配括号间的任一字符,可以使用“-“表示范围。如a[a-d]匹配ab/ac/ad

\x  匹配字符x,用于转义符合,如果要匹配“?“就需要使用\?

1.3.6   redis的基本命令之EXISTS

判断一个键是否存在。

如果键存在则返回整数类型1,否则返回0。

127.0.0.1:6379> keys *

1) "test_incr"

2) "test"

127.0.0.1:6379> exists test

(integer) 1

127.0.0.1:6379> exists test1

(integer) 0

127.0.0.1:6379>

1.3.7   redis的基本命令之DEL

删除键,可以删除一个或者多个键,多个键用空格隔开,返回值是删除的键的个数。

127.0.0.1:6379> del test

(integer) 1

127.0.0.1:6379> del test

(integer) 0

127.0.0.1:6379> del test test_incr

(integer) 1

127.0.0.1:6379>

1.3.8   redis的基本命令之TYPE

获得键值的数据类型,返回值可能是string(字符串)、hash(散列类型)、list(列表类型)、set(集合类型)、zset(有序集合类型)。

127.0.0.1:6379> set java helloworld

OK

127.0.0.1:6379> type java

string

127.0.0.1:6379> hset user name chenchen

(integer) 1

127.0.0.1:6379> type user

hash

127.0.0.1:6379>

1.3.9   redis的基本命令之FLUSHALL

清空所有数据库(数据清空,没有校验,慎用)。

127.0.0.1:6379> FLUSHALL

OK

1.4       Redis数据类型之字符串

1.4.1   SET/GET基本命令

字符串类型是redis中最基本的数据类型,它能存储任何形式的字符串,包括二进制数据。可以存储JSON化的对象、字节数组等。一个字符串类型键允许存储的数据最大容量是512MB。

赋值与取值:

SET key value

GET key

127.0.0.1:6379> keys *

(empty list or set)

127.0.0.1:6379> set test 123

OK

127.0.0.1:6379> set test1 ab

OK

127.0.0.1:6379> keys *

1) "test1"

2) "test"

127.0.0.1:6379> get test

"123"

127.0.0.1:6379> get test1

"abc"

127.0.0.1:6379> get test2

(nil)

127.0.0.1:6379> set java 'sun company'

OK

127.0.0.1:6379> get java

"sun company"

127.0.0.1:6379> set java "oraclecompany"

OK

127.0.0.1:6379> get java

"oracle company"

127.0.0.1:6379>

注意:字符中间有空格时,用单撇或者双撇括起来。

1.4.2   INCR自增实现

递增数字INCR key

当存储的字符串是整数时,redis提供了一个实用的命令INCR,其作用是让当前键值递增,并返回递增后的值。

127.0.0.1:6379> keys *

1) "test1"

2) "test"

127.0.0.1:6379> get test

"123"

127.0.0.1:6379> get test1

"abc"

127.0.0.1:6379> get test2

(nil)

127.0.0.1:6379> incr num

(integer) 1

127.0.0.1:6379> keys *

1) "num"

2) "test1"

3) "test"

127.0.0.1:6379> incr num

(integer) 2

127.0.0.1:6379> incr num

(integer) 3

127.0.0.1:6379>

从上面例子可以看出,如果num不存在,则自动会创建,如果存在自动+1。

指定增长系数

语法:INCRBY keyincrement

127.0.0.1:6379> incr num

(integer) 2

127.0.0.1:6379> incr num

(integer) 3

127.0.0.1:6379> incrby num 2

(integer) 5

127.0.0.1:6379> incrby num 2

(integer) 7

127.0.0.1:6379> incrby num 2

(integer) 9

127.0.0.1:6379> incr num

(integer) 10

127.0.0.1:6379>

1.4.3   DECR自减

减少指定的整数

DECR key

DECRBY key decrement

127.0.0.1:6379> incr num

(integer) 10

127.0.0.1:6379> decr num

(integer) 9

127.0.0.1:6379> decrby num 3

1.4.4   Append追加

向尾部追加值。如果键不存在则创建该键,其值为写的value,即相当于SET keyvalue。返回值是追加后字符串的总长度。

语法:APPEND key value

127.0.0.1:6379> keys *

1) "num"

2) "test1"

3) "test"

127.0.0.1:6379> get test

"123"

127.0.0.1:6379> append test "abc"

(integer) 6

127.0.0.1:6379> get test

"123abc"

127.0.0.1:6379>

1.4.5   STRLEN获取字符串长度

字符串长度,返回数据的长度,如果键不存在则返回0。注意,如果键值为空串,返回也是0。

语法:STRLEN key

127.0.0.1:6379> get test

"123abc"

127.0.0.1:6379> strlen test

(integer) 6

127.0.0.1:6379> strlen tnt

(integer) 0

127.0.0.1:6379> set tnt ""

OK

127.0.0.1:6379> strlen tnt

(integer) 0

127.0.0.1:6379> exists tnt

(integer) 1

127.0.0.1:6379>

1.4.6   多个mset mget

同时设置/获取多个键值

语法:MSET keyvalue [key value …]

      MGET key [key …]

127.0.0.1:6379> flushall

OK

127.0.0.1:6379> keys *

(empty list or set)

127.0.0.1:6379> mset a 1 b 2 c 3

OK

127.0.0.1:6379> mget a b c

1) "1"

2) "2"

3) "3"

127.0.0.1:6379>

1.5       Redis生存时间

1.5.1   Redis之生存时间

Redis在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的,即到期后数据销毁。

语法:EXPIRE keyseconds

127.0.0.1:6379> flushall

OK

127.0.0.1:6379> set bomb t

OK

127.0.0.1:6379> expire bomb 10

(integer) 1

127.0.0.1:6379> ttl bomb

(integer) 5

127.0.0.1:6379> ttl bomb

(integer) 3

127.0.0.1:6379> ttl bomb

(integer) 3

127.0.0.1:6379> ttl bomb

(integer) 2

127.0.0.1:6379> ttl bomb

(integer) 1

127.0.0.1:6379> ttl bomb

(integer) -2

127.0.0.1:6379> ttl bomb

(integer) -2

127.0.0.1:6379>

TTL查看key的剩余时间,当返回值为-2时,表示键被删除。

1.5.2   清除生存时间

语法:PERSIST key

127.0.0.1:6379> set bomb tnt

OK

127.0.0.1:6379> expire bomb 60

(integer) 1

127.0.0.1:6379> ttl bomb

(integer) 49

127.0.0.1:6379> persist bomb

(integer) 1

127.0.0.1:6379> ttl bomb

(integer) -1

127.0.0.1:6379>

设置新的数据时需要重新设置该key的生存时间。重新设置值也会清除生存时间。

1.5.3   设置生存时间单位为毫秒

语法:PEXPIRE keymilliseconds

127.0.0.1:6379> set bomb tnt

OK

127.0.0.1:6379> pexpire bomb 10000

(integer) 1

127.0.0.1:6379> ttl bomb

(integer) 6

127.0.0.1:6379> ttl bomb

(integer) 3

127.0.0.1:6379> ttl bomb

(integer) -2

127.0.0.1:6379>

设置生存时间为毫秒,可以做到更精确的控制。

1.6       Jedis

访问redis的驱动包。

使用最为广泛的是Jedis和Redisson(官方推荐),在企业中采用最多的是Jedis,我们重点学习Jedis。

Jedis官网地址:https://github.com/xetorthio/jedis

1.6.1    创建MavenJava工程pom.xml

   <dependency>

        <groupId>redis.clients</groupId>

        <artifactId>jedis</artifactId>

        <version>2.6.0</version>

   </dependency>

1.6.2   第一个jedis示例

package cn.redis;

 

import redis.clients.jedis.Jedis;

 

public class JedisDemo {

    publicstatic void main(String[] args) {

        // 构造jedis对象,注意IP会随环境变化,使用ifconfig查询当前IP

        Jedis jedis = new Jedis("127.0.0.1",6379);

        // 向redis中添加数据

       jedis.set("bomb", "lihangchao");

        // 从redis中读取数据

       String value = jedis.get("bomb");

 

       System.out.println(value);

        // 关闭连接

       jedis.close();

    }

}

命令窗口:

127.0.0.1:6379> keys *

1) "bomb"

127.0.0.1:6379> get bomb

"lihangchao"

127.0.0.1:6379>

1.6.3   使用连接池创建jedis连接

package cn.redis;

 

import redis.clients.jedis.Jedis;

import redis.clients.jedis.JedisPool;

import redis.clients.jedis.JedisPoolConfig;

 

public class JedisPoolDemo {

 

    publicstatic void main(String[] args) {

        // 构建连接池配置信息

       JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();

        // 设置最大连接数

       jedisPoolConfig.setMaxTotal(50);

 

        // 构建连接池

       JedisPool jedisPool = new JedisPool(jedisPoolConfig,"127.0.0.1", 6379);

 

        // 从连接池中获取连接

        Jedisjedis = jedisPool.getResource();

 

        // 读取数据

       System.out.println(jedis.get("bomb"));

 

        // 将连接还回到连接池中

       jedisPool.returnResource(jedis);

 

        // 释放连接池

       jedisPool.close();

    }

}

1.7       Redis主从配置

1.7.1   同时启动多个RedisServer

通过主从复制可以允许多个slaveserver 拥有和masterserver相同的数据库副本

redis主从复制特点:

1、Master可以拥有多个slave;

2、多个slave可以连接到同一个master外,还可以连接到其它的slave;

3、主从复制不会阻塞master,在同步数据时,master继续可以处理client请求;

4、提高系统的伸缩性。

主从配置:

1、从机上配置slaveof masterip port 

2、如果主机中配置密码验证:则此时需要配置masterauth masterpasswd(主机密码)

 

redis无需安装,只需单独配置一个配置文件,再启一个进程即可。

1.8    Redis中的hash结构

在redis中用的最多的就是hash和string类型。

1.8.1   问题

假设有User对象以JSON序列化的形式存储到redis中,User对象有id、username、password、age、name等属性,存储的过程如下:

保存、更新:

User对象->json(string)->redis

如果在业务上只是更新age属性,其他的属性并不做更新应该怎么做呢?

Redis数据类型之散列类型hash可以很好的解决这个问题。

散列类型存储了字段(field)和字段值的映射,但字段值只能是字符串,不支持其他类型,也就是说,散列类型不能嵌套其他的数据类型。一个散列类型可以包含最多232-1个字段。

1.8.2   HSET和HGET赋值和取值

HSET key field value

HGET key field

HMSET key field value [field value…]

HMGET key field value [field value…]

HGETALL key

 

127.0.0.1:6379> hset user username chenchen

(integer) 1

127.0.0.1:6379> hget user username

"chenchen"

127.0.0.1:6379> hset user username chen

(integer) 0

127.0.0.1:6379> keys user

1) "user"

127.0.0.1:6379> hgetall user

1) "username"

2) "chen"

127.0.0.1:6379>

127.0.0.1:6379> hset user age 30

(integer) 1

127.0.0.1:6379> hgetall user

1) "username"

2) "chen"

3) "age"

4) "30"

127.0.0.1:6379>

HSET命令不区分插入和更新操作,当执行插入操作时HSET命令返回1,当执行更新操作时返回0。

1.8.3   HMSET和HMGET设置和获取对象属性

127.0.0.1:6379> hmset person username tony age18

OK

127.0.0.1:6379> hmget person age username

1) "18"

2) "tony"

127.0.0.1:6379> hgetall person

1) "username"

2) "tony"

3) "age"

4) "18"

127.0.0.1:6379>

注意:上面HMGET字段顺序可以自行定义

1.8.4   HEXISTS属性是否存在

127.0.0.1:6379> hexists killer

(error) ERR wrong number of arguments for 'hexists'command

127.0.0.1:6379> hexists killer a

(integer) 0

127.0.0.1:6379> hexists user username

(integer) 1

127.0.0.1:6379> hexists person age

(integer) 1

127.0.0.1:6379>

1.8.5   HDEL删除对象字段

127.0.0.1:6379> hdel user age

(integer) 1

127.0.0.1:6379> hgetall user

1) "username"

2) "chen"

127.0.0.1:6379> hgetall person

1) "username"

2) "tony"

3) "age"

4) "18"

127.0.0.1:6379>

1.8.6   只获取字段名HKEYS或字段值HVALS

127.0.0.1:6379> hkeys person

1) "username"

2) "age"

127.0.0.1:6379> hvals person

1) "tony"

2) "18"

1.8.7   获取字段数量HLEN

127.0.0.1:6379> hlen user

(integer) 1

127.0.0.1:6379> hlen person

(integer) 2

127.0.0.1:6379>

1.8.8   Jedis示例

package cn.redis;

 

import java.util.Map;

 

import redis.clients.jedis.Jedis;

import redis.clients.jedis.JedisPool;

import redis.clients.jedis.JedisPoolConfig;

 

public class JedisPoolDemoCMD {

 

    publicstatic void main(String[] args) {

        // 构建连接池配置信息

       JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();

        // 设置最大连接数

       jedisPoolConfig.setMaxTotal(50);

 

        // 构建连接池

       JedisPool jedisPool = new JedisPool(jedisPoolConfig,"127.0.0.1", 6379);

 

        // 从连接池中获取连接

        Jedisjedis = jedisPool.getResource();

 

       jedis.hset("USER_1", "username", "chenchen");

       jedis.hset("USER_1", "password","123456");

       

       Map<String, String> val = jedis.hgetAll("USER_1");

        for(Map.Entry<String, String> entry : val.entrySet()) {

           System.out.println(entry.getKey() + "  " + entry.getValue());

        }

 

        // 将连接还回到连接池中

       jedisPool.returnResource(jedis);

 

        // 释放连接池

       jedisPool.close();

 

    }

 

}

Redis中测试:

127.0.0.1:6379> hgetall USER_1

1) "username"

2) "zhangsan"

3) "password"

4) "123456"

127.0.0.1:6379> hgetall user_1

(empty list or set)

127.0.0.1:6379>

注意:key值的大小写是区分的。

1.9    Redis应用举例

1) 缓存(数据查询、短连接、新闻内容、商品内容等等)。(最多使用)

2)分布式集群架构中的session分离。

3)聊天室的在线好友列表。

4)任务队列。(秒杀、抢购、12306等等)(先进先出)

5)应用排行榜。(可以给每个元素设置一个打分,这样就可以排序)

6)网站访问统计。

7)数据过期处理(可以精确到毫秒)。

1.9.1   短连接

百度短网址:http://dwz.cn/

这样就可以通过一个比较短的URL来访问相同的地址。它是将连接地址通过一个算法进行简化变短。http://dwz.cn/NJfGa最后面这实际就是一个key,在它的数据库中这个key就对应一个URL。当访问http://dwz.cn/NJfGa这个地址时dwz.cn中的服务对这个后面的ID进行解析,从数据库查询出真正的地址。然后重定向访问真正的链接。

1.9.2   聊天室好友在线列表

假设A存放你的好友,B存放所有在线的人,它们的交集就是你的在线好友。

1.9.3   应用排行

1.9.4   缓存的异常处理

问题:缓存异常时应该自己处理还是应该抛出?

缓存在项目中或者系统中,分担底层数据库的压力。缓存是不能影响业务逻辑的。比如说缓存服务器宕机了,能说因为缓存服务器宕机了,业务走不下去了。这种理由当然不行。缓存一定不能影响正常的业务逻辑的执行。(记录日志,然后自己处理异常

0 0