Redis专辑
文章平均质量分 84
Redis技术的开发实践、技术追踪
FeelTouch Labs
这个作者很懒,什么都没留下…
展开
-
《Redis核心技术与实战》学习总结(18)之使用规范
因此,建议将不同的业务数据放到不同的Redis实例,避免单实例的内存使用量过大。在实际应用 Redis 时,我们会更多地把它作为缓存保存热数据,这样既可以充分利用 Redis 的高性能特性,还可以把宝贵的内存资源用在服务热数据上,就是俗话说的“好钢用在刀刃上”。对于业务名或业务数据名,可以使用相应的英文单词的首字母表示,或者用缩写表示,减少key占用的内存空间。:按照键值对的 key 内容进行匹配,返回符合匹配条件的键值对,该命令需要对 Redis 的全局哈希表进行全表扫描,严重阻塞 Redis 主线程;转载 2024-02-20 17:54:50 · 116 阅读 · 1 评论 -
《Redis核心技术与实战》学习总结(17)之多线程
所谓区分编码,就是指直接通过不同的开头字符,区分不同的数据类型,这样一来,客户端就可以直接通过判断传递消息的开头字符,来实现数据转换操作了,提升了客户端的效率。Redis 6.0之前使用的 RESP 2 协议,客户端和服务器端的通信内容都是以字节数组形式进行编码的,客户端需要根据操作的命令或是数据类型自行对传输的数据进行解码,增加了客户端开发复杂度。Redis 6.0 是推出不久的,新的功能特性还需要在实际应用中进行部署和验证,所以,如果我们想试用 Redis 6.0,转载 2024-02-20 17:48:22 · 108 阅读 · 0 评论 -
《Redis核心技术与实战》学习总结(16)之集群方案
Codis集群保证高可靠性的架构:配置了 server group 的 Codis 集群架构,在 Codis 集群中,通过部署 server group 和哨兵集群,实现 codis server 的主从切换,提升集群可靠性。对于 codis dashboard 和 codis fe 来说,它们主要提供配置管理和管理员手工操作,负载压力不大,所以,它们的可靠性可以不用额外进行保证了。本文总结了Redis集群的两种方案:Codis 和 Redis Cluster 的基础内容,希望对你有帮助。转载 2024-02-20 17:44:46 · 84 阅读 · 0 评论 -
《Redis核心技术与实战》学习总结(15)之秒杀场景
这样可以将获取分布式锁 和 查询商品库存 的请求压力分摊开来,减轻保存库存信息的Redis实例的压力。此外,也建议不要将秒杀商品的库存信息和日常系统的数据保存在同一个Redis实例,避免干扰业务系统的正常运行。本文总结了Redis支撑秒杀场景的关键技术:原子操作 和 分布式锁 的具体应用,但是需要注意,对于秒杀,只有Redis是不够的,秒杀系统是一个系统性的工程,需要考虑各个环节的设计。在Redis中的应用场景,秒杀场景是一个经典的应用场景,相信大家或多或少都在使用Redis支撑不同量级的秒杀应用。转载 2024-02-20 17:41:19 · 90 阅读 · 0 评论 -
《Redis核心技术与实战》学习总结(14)之分布式锁保证原子性
对于分布式锁,我们需要考虑场景需要来做权衡,偏重于效率,我们可以使用单Redis实例来实现,而如果偏重于正确性,我们可以使用Redlock方案。客户端需要计算的是 锁的最初有效时间 减去 获取锁的总耗时,如果锁的有效时间已经来不及完成数据的操作了,这时就需要释放锁了,避免出现还没完成数据操作,锁就过期了的情况。在Redis 6.0之前,Redis是使用单线程串行处理请求操作的,当 Redis 执行某个命令操作时,其他命令是无法执行的,这相当于命令操作是互斥执行的。命令格式:SET + Lua脚本组合;转载 2024-02-20 17:39:37 · 178 阅读 · 0 评论 -
《Redis核心技术与实战》学习总结(13)之性能分析
问题原因:Redis使用内存大页,优点是在分配相同的内存量时可以减少分配次数,而缺点是在RDB持久化时为了继续接收客户端写请求会采取“写时复制”机制,采用内存大页会在客户端请求只修改较小数据(比如:100KB)的时候仍然需要拷贝2MB的大页,当大量的大页拷贝发生时,就会导致性能变慢。背景介绍:内存 swap 是操作系统里将内存数据在内存和磁盘间来回换入和换出的机制,涉及到磁盘的读写,所以,一旦触发 swap,无论是被换入数据的进程,还是被换出数据的进程,其性能都会受到慢速磁盘读写的影响。转载 2024-02-20 17:34:15 · 78 阅读 · 0 评论 -
《Redis核心技术与实战》学习总结(12)之淘汰策略
当需要再次淘汰时,Redis会再挑选一些lru字段比候选集合中最小lru字段还要小的键值对,将它们放入候选集,如果候选集的数据的个数达到了 maxmemory-sample 配置的个数,Redis就开始将lru字段值最小的数据淘汰(lru字段最小的可能不止一个)。在实际业务应用中,LRU 和 LFU 两个策略都有应用。然后,在需要选择淘汰的数据时,Redis首先会随机选择N个数据将它们作为一个候选集合,然后比较他们的lru字段,将lru字段最小的数据淘汰掉(lru字段最小的可能不止一个)。转载 2024-02-20 17:16:28 · 91 阅读 · 0 评论 -
《Redis核心技术与实战》学习总结(11)之读写一致性问题
下图展示了一个包含10个bit位的数字,使用3个哈希函数的布隆过滤器,当标记数据X时,首先会经历3次哈希计算,然后将哈希结果对应的bit位标记为1,代表假设数据X已经在数据库了。这时,如果后续应用想要查询数据X时,只需要查看对应的3个bit位是否都为1,是则允许继续查询,不是的话,就代表X不在数据库中。问题定义:缓存穿透是指要访问的数据既不在 Redis 缓存中,也不在数据库中,导致请求在访问缓存时,发生缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据。(例如使用 Kafka 消息队列)。转载 2024-02-20 16:31:13 · 121 阅读 · 0 评论 -
《Redis核心技术与实战》学习总结(10)之消息队列
最后,对于需求3-消息可靠性保证,List本身在读取一条消息后就不会再留存这条消息了,所以为了留存消息,List提供了BRPOPLPUSH命令,即让消费者程序从一个List中读取消息,同时再把这个消息插入到另一个List(可以理解为备份List)留存。此外,使用消费组的目的是让组内的多个消费者共同分担读取消息,所以,我们通常会让每个消费者读取部分消息,从而实现消息读取负载在多个消费者间是均衡分布的,比如我们可以使用count N命令来让各个消费者各自读取N条消息。转载 2024-02-20 16:24:16 · 97 阅读 · 0 评论 -
《Redis核心技术与实战》学习总结(9)之集合使用场景
例如,在统计 1 亿个用户连续 10 天的签到情况时,可以把每天的日期作为 key,每个 key 对应一个 1 亿位的 Bitmap,每一个 bit 对应一个用户当天的签到情况。最后,我们可以用 BITCOUNT 统计下 Bitmap 中的 1 的个数,这就是连续签到 10 天的用户总数了。Sorted Set 元素的权重分数是一个浮点数(float 类型),而一组经纬度包含的是经度和纬度两个值,是没法直接保存为一个浮点数的,因此需要进行GeoHash编码,将经纬度各自的编码组合成一个最终编码。转载 2024-02-20 16:05:25 · 72 阅读 · 0 评论 -
《Redis核心技术与实战》学习总结(8)之String内存使用分析
在使用Redis集合类型时,一个key就对应一个集合的数据,能保存的数据很多,但也只使用了一个dictEntry,就可以节省在string类型下每个数据都需要的dictEntry内存开销。压缩列表也类似于一个数组,但和数组不同的是,压缩列表在表头有三个字段,分别表示列表长度、列表尾的偏移量 和 列表中entry的个数。其实,Sorted Set类型也可以实现需求,还是按照二级编码设计思路,将ID的前7位作为Sorted Set的key,将ID的后三位作为member值,存储对象ID则作为score值。转载 2024-02-20 15:39:45 · 83 阅读 · 0 评论 -
《Redis核心技术与实战》学习总结(7)之实例扩展
换句话说,ASK命令只是让客户端给新实例发一次数据请求,ASK 命令并不会让客户端后续所有命令都发到新实例,当然,它更不能更新客户端本地缓存。当客户端把一个键值对的操作请求发给一个实例时,如果这个实例上并没有这个键值对映射的哈希槽,那么,这个实例就会给客户端返回下面的 MOVED 命令响应结果,这个结果中就包含了新实例的访问地址。不过,实际场景中,Redis实例 和 哈希槽 的对应关系并不是一成不变的,而是会随着实例个数的增减而变化,比如Redis Cluster会重新分配哈希槽以确保负载均衡。转载 2024-02-20 14:57:55 · 54 阅读 · 0 评论 -
《Redis核心技术与实战》学习总结(6)之哨兵机制
所述,从库的 slave_repl_offset 偏移量 一般会等于或接近于 主库的 master_repl_offset 偏移量,而如果某个从库的这个值最接近于主库的,那么就可以被选为新主库。简单说来,就是当一个哨兵和主库建立了连接(如下面所示的配置项)后,就可以向主库发布一个自己的连接信息(如IP和端口),也可以从主从订阅一个其他哨兵的连接信息。在确定好新主库后,多个哨兵实例会进行投票仲裁,选举一个 Leader 出来,由它负责实际的主从切换,即由它来完成新主库的选择以及通知从库与客户端。转载 2024-02-20 14:41:17 · 55 阅读 · 0 评论 -
《Redis核心技术与实战》学习总结(5)之数据同步
随着主库不断接收新的写操作,它在缓冲区中的写位置会逐步偏离起始位置。当网络闪断异常情况下,主库可能会接收到新的写操作命令,因此,可以看出,主库的偏移量master_repl_offset > 从库的偏移量slave_repl_offset。比如,在实际中通常会手动选择一个 内存配置较高的 从库 来作为同步源,其他新的从库加入后可以从这个从库中同步,建立主从关系。在主-从-从模式下,新增的从库可以设置从 集群中的某一个从库 中进行数据同步,从而避免每次都从主库进行同步,降低主库的资源消耗,保证系统的稳定性。转载 2024-02-20 14:30:21 · 51 阅读 · 0 评论 -
《Redis核心技术与实战》学习总结(4)之持久化机制
因此,如果采用先写日志方式,那么日志中就有可能会记录错误的命令,导致Redis在使用AOF恢复数据的时候,可能会出错。,即如果AOF需要一次记录的命令太多,Redis会进行重写:Redis根据现状创建一个新的AOF文件,它会读取所有键值对,然后对每一个键值对用一条命令记录它的写入。因为,Redis的单线程设计,日志中的命令只能一条一条按顺序执行,“重放”过程会变慢。但是,当数据量越来越大之后,做全量快照的过程中写入磁盘的时间会逐渐增大,全量数据越多,RDB文件就会越大,往磁盘上写数据的时间开销也就越大。转载 2024-02-20 14:16:07 · 44 阅读 · 0 评论 -
《Redis核心技术与实战》学习总结(3)之线程模型
但在Redis的使用场景下,简单地加锁可能并不能得到理想的效果,会导致大部分线程在等待获取互斥锁,并行变串行,从而降低吞吐率。比如服务端监听到一个客户端有连接请求,但是一直没有能够成功建立连接,就会阻塞在accept()函数中,导致其他客户端无法和服务端建立连接,这就可能会导致服务端的线程阻塞。正如刚刚的例子中提到,黄牛买到票后会通知老周去领票,为了在请求到达时能够通知到Redis线程,epoll提供了基于事件的回调机制,即针对不同事件的发生,调用响应的处理函数。转载 2024-02-20 14:05:10 · 35 阅读 · 0 评论 -
《Redis核心技术与实战》学习总结(2)之底层数据结构
对于软件设计来说,也需要在时间 vs 空间,新技术 vs 老技术,优雅 vs 效率,轻度设计 vs 重度设计等之间做权衡,一个问题总会有多种解决方案可以实现,在特定的时间段,永远没有最完美的设计,只有较合适的设计。由上一篇已经知道,List类型的底层实现包括了 双向链表 和 压缩列表,但这是在Redis的3.2版本之前的底层实现。两端节点一般不被压缩,因为当一个链表很长时,最频繁访问的就是两端的数据,根据“二八定律”,两端数据不压缩,而将中间数据压缩,从而节省空间,但又保证读取效率。转载 2024-02-19 17:42:49 · 41 阅读 · 1 评论 -
《Redis核心技术与实战》学习总结(1)之 架构设计
在之前多年的工作生涯中,我也只是关注了零散的技术点,没有对Redis建立起一套整体观,但只有建立了系统整体观,才能更好地定位问题和解决问题,更重要的是应付面试。当往哈希表中写入的数据变的很多时,哈希冲突问题就会出现。所谓rehash,就是增加现有的哈希桶的数量,让逐渐增多的entry元素能够在更多的桶之间分散保存,减少单个桶中的元素数量,从而减少单个桶中的冲突。在具体操作中,Redis会开辟一个新的哈希表(比如:大小为之前的两倍),然后把之前哈希表的数据重新映射到新的哈希表,最后释放之前的哈希表。转载 2024-02-19 17:21:56 · 83 阅读 · 0 评论 -
基于Redis的高可用分布式锁——RedLock
客户端在向每个节点尝试获取锁的时候,有一个超时时间限制,而且这个时间远小于锁的有效期,比如说几毫秒到几十毫秒之间,这样的机制是为了防止在向某一个节点获取锁的时候,等待的时间过长,从而导致获取锁的整体时间过长。client1和client2,对Redis节点A-E进行抢锁操作,如图,client1先抢到节点ABC,超过半数,因此持有分布式锁,在持有锁期间,client2抢锁都是失败的,当时序=6时,client1才处理完业务流程释放分布式锁,这时候client2才有可能抢锁成功。多数节点的锁(N/2+1)原创 2024-01-29 00:39:37 · 682 阅读 · 1 评论 -
Redis Hset使用中的小坑
Redis Hset 命令用于为哈希表中的字段赋值 。如果哈希表不存在,一个新的哈希表被创建并进行 HSET 操作。如果字段已经存在于哈希表中,旧值将被覆盖。如果字段是哈希表中的一个新建字段,并且值设置成功,返回 1 。 如果哈希表中域字段已经存在且旧值已被新值覆盖,返回 0 。注意⚠️:这个在纯粹的Redis的终端还可以,但是一旦用到了语言的高级API就很容易产生误解了。下面以Go语言举例详细说明。上面Go代码中,如果要判断是否插入成功,应该通过err == nil来判断,而不是通过flag是否为原创 2022-07-11 21:56:54 · 1800 阅读 · 1 评论 -
Redis重点特性DevOps
延迟因为Redis是个单线程模型,客户端过来的命令是按照顺序执行的。因此网络问题、慢命令会造成阻塞导致redis性能下降。如果发生命令阻塞就可以看到每秒命令处理数在明显下降。要分析解决这个性能问题,需要跟踪命令处理数的数量和延迟时间。降低延迟的几个技巧:使用多参数命令若是客户端在很短的时间内发送大量的命令过来,会发现响应时间明显变慢,这由于后面命令一直在等待队列中前面大量命令执行完...原创 2019-04-03 01:05:41 · 405 阅读 · 0 评论 -
Redis性能提高之批量和管道
批量的意义Redis协议采取的是客户端-服务器方式,即在一次round trip中,客户端发送一条指令,服务端解析指令并执行,然后向客户端返回结果。这是一种典型的tcp交互方式。粗略的分,客户端发起一次Redis请求主要有如下开销:socket IO导致的上下文切换开销熟悉OS/Linux的童鞋都知道,一次redis请求在客户端和服务端分别至少会存在一次read()和一次write...原创 2019-03-20 22:55:51 · 1484 阅读 · 0 评论 -
Redis集群实现方案选型分析
Redis集群方案1. 客户端分片实现集群2.中间代理层实现集群3.Server层面本身支持集群4.第三方:实际是上述三中的进一步增强尽管实现方案多样,其实质都是通过底层分片突破Redis单实例内存限制和增加可靠性客户端分片客户端分片是把分片的逻辑放在Redis客户端实现,通过Redis客户端预先定义好的路由规则,把对Key的访问转发到不同的Redis实例中,最后把返...原创 2019-03-03 22:25:59 · 1363 阅读 · 0 评论 -
Redis客户端连接数DevOps
Redis客户端连接redis通过监听一个TCP端口或socket的方式接收来自客户端的连接, 当与客户端建立连接后,redis内部会进行如下操作:(1)客户端socket会被设置为非阻塞模式,因为redis在网络时间处理上采用的是非阻塞多路复用模型;(2)然后为这个socket设置TCP_NODELAY属性,禁用Nagle算法;(3)然后创建一个可读的文件事件用于监听这个客户端soc...原创 2019-04-03 01:06:03 · 599 阅读 · 0 评论 -
Redis 集群到底支持不支持批处理指令MGET等
目前,这不是一个确定的答案,对于不同集群的实现方式其支持度也是不一样的原生Redis Cluster 3.* 和 4.*版本集群3.0 不支持,即使在某些客户端下返回了值,很可能仅仅只是某一个节点的值4.0 仅支持相同slot,key不能保证在相同slot还是没用参考:Redis阿里云Redis Cluster支持MGET MSET HMGET HMSET指令受限MG...原创 2019-03-04 00:26:20 · 10159 阅读 · 0 评论 -
ehcache memcache redis缓存特性区别
网站技术高速发展的今天,缓存技术已经成为大型网站的一个关键技术,缓存设计好坏直接关系的一个网站访问的速度,以及购置服务器的数量,甚至影响到用户的体验。网站缓存按照存放的地点不同,可以分为客户端缓存、服务端缓存。客户端缓存客户端缓存又可分为:浏览器缓存、网关或代理服务器缓存网关或代理服务器缓存是将网页缓存中网关服务器上,多用户访问同一个页面时,将直接从网关服务器把转载 2017-10-24 23:39:47 · 705 阅读 · 0 评论 -
Redis中使用Lua的原理讲解和实战
一、Redis使用Lua的优势减少网络开销: 不使用 Lua 的代码需要向 Redis 发送多次请求, 而脚本只需一次即可, 减少网络传输; 原子操作: Redis 将整个脚本作为一个原子执行, 无需担心并发, 也就无需事务; 复用: 脚本会永久保存 Redis 中, 其他客户端可继续使用.二、Lua是什么Lua is a powerful and fast programming...原创 2020-02-08 16:29:54 · 1329 阅读 · 0 评论 -
RedisClient 报出java.net.SocketException: Broken pipe异常
问题场景:读写数据量小时没有问题,当读写数据量大的时候偶尔会报出这个异常原因分析:大数据操作时间较长,被redis server强行close了,超过redis server的某个值。相关参数:minEvictableIdleTimeMillis 。线程中如果检测到当前连接的最后活跃时间和当前时间的差值大于minEvictableIdleTimeMillis,则关闭当前连接其他案...原创 2018-12-20 13:34:21 · 12128 阅读 · 0 评论 -
Redis client之Jedis在线程执行抛出异常无法恢复的情形和解决方案
环境概述1. SpringBoot 1.5.9 注解方式返回单例Jedis对象作为client2.JedisPool连接配置如下: max-total: 100 # 连接池最大连接数(使用负值表示没有限制) max-wait: 10 # 连接池最大阻塞等待时间(使用负值表示没有限制) min-idle: 10 # 连接池中的最小空闲连接...原创 2018-12-12 01:03:12 · 2443 阅读 · 0 评论 -
Redis报出JedisDataException: OOM command not allowed when used memory > 'maxmemory'解决方案
错误信息redis.clients.jedis.exceptions.JedisDataException: OOM command not allowed when used memory > 'maxmemory'. at redis.clients.jedis.Protocol.processError(Protocol.java:127) at ...原创 2020-01-13 11:42:19 · 5555 阅读 · 0 评论 -
Redis如何找出并快速删除亿级指定前缀的key
背景由于Redis的单线程服务模式,命令keys *会阻塞正常的业务请求,不建议使用keys * pattern的方法进行查询,可能会使服务器卡顿而出现事故。如何获取指定的 key?可以采用Redis提供的SCAN命令。SCAN 命令是一个基于游标的迭代器(cursor based iterator):SCAN 命令每次被调用之后都会向用户返回一个新的游标, 用户在下次迭代时会使用这个新游...转载 2019-10-14 00:20:54 · 1317 阅读 · 0 评论 -
Redis数据操作长延迟分析
1. 长耗时命令引起延迟Redis绝大多数读写命令的时间复杂度都在O(1)到O(N)之间。O(1)的命令是安全的,O(N)命令在使用时需要注意,如果N的数量级不可预知,则应避免使用。例如对一个field数未知的Hash数据执行HGETALL/HKEYS/HVALS命令,通常来说这些命令执行的很快,但如果这个Hash中的field数量极多,耗时就会成倍增长。针对长耗时的最佳实践:不要把...原创 2019-03-20 22:55:25 · 783 阅读 · 0 评论 -
Redis性能分析和运维工具
目录redis-benchmark工具monitor命令slowlog命令info命令Redis高阶用法进入Redis Deepen系列redis-benchmark工具作用: 可用于redis基准信息,服务器性能检测$ redis-benchmark -h localhost -p 6379 -c 100 -n 100000100个并发连接,10000...原创 2019-03-20 00:40:05 · 760 阅读 · 0 评论 -
Redis中Sorted-Set时间复杂度和实战
一、概述: Sorted-Sets和Sets类型极为相似,它们都是字符串的集合,都不允许重复的成员出现在一个Set中。它们之间的主要差别是Sorted-Sets中的每一个成员都会有一个分数(score)与之关联,Redis正是通过分数来为集合中的成员进行从小到大的排序。然而需要额外指出的是,尽管Sorted-Sets中的成员必须是唯一的,但是分数(score)却是可以重复的。 在S...转载 2019-04-03 23:43:51 · 6880 阅读 · 1 评论 -
最全的50道Redis面试题
1、什么是Redis?Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。 Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构...转载 2019-04-03 01:04:38 · 678 阅读 · 0 评论 -
阿里云Redis混合存储典型场景:如何轻松搭建视频直播间系统
本文主要介绍视频直播间系统,以及如何使用阿里云Redis混合存储实例方便快捷的构建大数据量,低延迟的视频直播间服务。背景视频直播间作为直播系统对外的表现形式,在整个系统中处于核心地位。通常除了视频直播窗口外,直播间还包含在线用户,礼物,评论,点赞,排行榜等信息。直播间消息,时效性高,互动性强,对系统时延有着非常高的要求,非常适合使用Redis等缓存服务来处理。直播信息实时排行信息...转载 2019-04-03 01:06:50 · 410 阅读 · 0 评论 -
连接到Redis server的两种方法
使用telent或者redis-cli链接远程redistelnet 192.168.1.100 6666redis-cli -h 192.168.1.100 -p 6666这里列出redis-cli的几个参数用法:redis-cli [OPTIONS] [cmd [arg [arg ...]]]-h <主机ip>,默认是127.0.0.1-p <端口...原创 2016-12-25 22:24:51 · 3706 阅读 · 1 评论 -
远程连接Redis问题集合
1:访问出现Could not connect to Redis at (ip地址):6379: Connection refused最有可能的原因就是没有关闭防火墙,1) 重启后生效 开启: chkconfig iptables on 关闭: chkconfig iptables off 2) 即时生效,重启后失效 开启: ...转载 2016-12-25 22:27:41 · 1365 阅读 · 0 评论 -
Redis提供的持久化机制
Redis提供的持久化机制 Redis是一种面向“key-value”类型数据的分布式NoSQL数据库系统,具有高性能、持久存储、适应高并发应用场景等优势。它虽然起步较晚,但发展却十分迅速。近日,Redis的作者在博客中写到,他看到的所有针对Redis的讨论中,对Redis持久化的误解是最大的,于是他写了一篇长文来对Redis的持久化进行了系统性的论述。文章主要包含三个方面:Red...转载 2019-04-19 00:36:30 · 297 阅读 · 0 评论 -
阿里云 Redis 报出You can't read against a non-read redis.解决方案
中午收到报警,所有请求阿里云redis的服务都报出了异常,立即上线排查查看log,发现log中有如下错误提示:[2019-04-29 11:47:35,339] [ERROR] [XNIO-2 task-307] [c.p.g.c.Controller] [Controller.java:38] Controller.query exception:NOREAD You can't read ...原创 2019-04-30 00:44:28 · 4817 阅读 · 0 评论