Redis杂谈

目录

一、单线程的Redis为什么那么快?

二、Redis的持久化?

三、Redis的主从复制?

四、Redis集群?

五、Redis的Value有哪几种类型?

六、Redis有哪些应用场景?

七、内存爆了咋办?

Maxmemory配置指令

回收策略

回收进程如何工作

近似LRU算法


Redis【Remote Dictionary Service】,K-V数据库,属于NoSQL中的一员。推荐Redis中文官网

一、单线程的Redis为什么那么快?

全部数据操作都是在内存中完成的,这是其中一个原因。

另一个原因是,操作系统提供了epoll(早期是select/poll)函数形成多路复用的API,可以将接受连接、读、写的文件描述符绑定到selector上基于事件通知机制。Redis的工作线程进行轮询监视,一旦有任何事件到来就可以进行相关操作。

6.x版本增加了多线程io threads,但是工作线程worker还是只有一个。

二、Redis的持久化?

两种机制,全量备份的快照(RDB)和连续增量备份的AOF日志。

RDB:内存数据的二进制序列化形式,存储紧凑。fork子进程copy on write(COW)。

AOF:内存数据修改的指令记录文本(先缓存后执行,内核fsync函数将其刷到磁盘)。重启时加载重放,需要定期重写(首先遍历内存转换成一系列指令然后序列化到新的AOF日志文件,接着追加期间发生的增量AOF日志,最后替换旧的AOF日志文件)。

持久化操作主要在从节点进行,没有压力。

Redis 4.0之后增加混合持久化选项——将rdb文件的内容和增量的AOF日志文件(持久化过程发生的,不是全量的)存在一起。在重启时,首先加载rdb的内容,然后再重放增量AOF日志,提高效率(之前采用AOF日志重放太慢了)。

三、Redis的主从复制?

异步复制,不满足强一致性,因为是否成功不影响主节点对外提供写服务,所以保证可用性。

从节点会使用各种策略全力追赶主节点,所以最终一致。

Redis同步支持主从同步和从从同步(后续版本增加,以减轻主节点的同步负担)。

主节点会把写指令记录在本地的内存buffer中,然后异步复制到从节点,从节点一边重放一边向主节点反馈自己同步的偏移量。buffer有限且是定长的环形数组,如果内容满了会被从头覆盖,这时候需要执行快照同步。

主节点快照同步时把当前内存的全部数据快照到磁盘文件然后把它们传送到从节点,从节点接收完毕首先清空当前缓存执行全量加载然后继续增量同步。快照同步过程中如果主节点又发生覆盖buffer的情况,只能再次进行快照同步了。避免这种死循环可以配置合适的buffer大小参数。

新节点加入时,先快照同步,完成后增量同步。

Redis 2.8.18版本 之后支持快照同步的无盘复制,主节点遍历内存直接将序列化的内容发送到从节点。

Redis 3.0版本 之后使用wait指令可以让异步复制变身同步复制,这样就保证了强一致性。

哨兵模式Sentinel可以监控主节点状态实现自动主从切换,它的min-slaves-to-write和min-slaves-max-lag两个选项可以限制主从延迟过大,表示在指定时间内(第二个参数,秒)至少有指定数量(第一个参数)的从节点完成了正常复制,否则就对外停止写服务,丧失可用性。

四、Redis集群?

高可用方案:Redis Sentinel(哨兵模式),基于Zookeeper实现的Master-Slave架构。

集群方案一:Codis,转发代理中间件。首先将请求的key哈希,然后这个值对槽位数量(1024)取模得到对应key的槽位,最后根据Redis实例和槽位的映射关系将请求转发到对应的Redis实例。

集群方案二:RedisCluster,去中心化。将所有数据划分为16384的slots,每个节点负责一部分槽位。槽位的信息存储于每个节点。客户端连接时会得到槽位配置信息(需要缓存),可以直接定位(哈希取模)某个具体的key所在的节点。节点发现客户端发出指令的key不归自己管理时,它会发送携带目标地址的跳转指令让客户端去对应的节点操作。Redis迁移的单位是槽,迁移时处于中间过渡状态(原节点migrating,新节点importing),过程同步,从源节点获取内容->存到目标节点->从源节点删除内容。每个主节点可以设置若干个从节点保证HA(Height Availability)。

五、Redis的Value有哪几种类型?

1)string(字符串):动态字符串可修改。小于1M时加倍扩容,超过1M每次多扩1M。最大长度512M。

字符串操作、数值计算(限流、秒杀)、二进制bitmap(指定范围统计-统计用户任意时间窗口内登录天数、位与、位或)

2)list(列表):双向链表。

模拟栈结构、模拟队列结构、模拟数组结构

3)hash(哈希,字典):无序。底层是数组+链表二维结构。

聚合对象

4)set(集合):无序、唯一。

去重、随机数获取-抽奖、交集-共同好友、并集-朋友圈、差集-可能认识的人

5)zset(有序列表):有序集合,跳跃列表实现(随机翻硬币)。

动态排名、前几名

skiplist

附上Jedis客户端的方法列表

String set(String key, String value)
String set(String key, String value, String nxxx, String expx, long time)
String get(String key)
Long exists(String... keys)
Boolean exists(String key)
Long del(String... keys)
Long del(String key)
String type(String key)
Set<String> keys(String pattern)
String randomKey()
String rename(String oldkey, String newkey)
Long renamenx(String oldkey, String newkey)
Long expire(String key, int seconds)
Long expireAt(String key, long unixTime)
Long ttl(String key)
Long move(String key, int dbIndex)
String getSet(String key, String value)
List<String> mget(String... keys)
Long setnx(String key, String value)
String setex(String key, int seconds, String value)
String mset(String... keysvalues)
Long msetnx(String... keysvalues)
Long decrBy(String key, long integer)
Long decr(String key)
Long incrBy(String key, long integer)
Double incrByFloat(String key, double value)
Long incr(String key)
Long append(String key, String value)
String substr(String key, int start, int end)
Long hset(String key, String field, String value)
String hget(String key, String field)
Long hsetnx(String key, String field, String value)
String hmset(String key, Map<String, String> hash)
List<String> hmget(String key, String... fields)
Long hincrBy(String key, String field, long value)
Double hincrByFloat(String key, String field, double value)
Boolean hexists(String key, String field)
Long hdel(String key, String... fields)
Long hlen(String key)
Set<String> hkeys(String key)
List<String> hvals(String key)
Map<String, String> hgetAll(String key)
Long rpush(String key, String... strings)
Long lpush(String key, String... strings)
Long llen(String key)
List<String> lrange(String key, long start, long end)
String ltrim(String key, long start, long end)
String lindex(String key, long index)
String lset(String key, long index, String value)
Long lrem(String key, long count, String value)
String lpop(String key)
String rpop(String key)
String rpoplpush(String srckey, String dstkey)
Long sadd(String key, String... members)
Set<String> smembers(String key)
Long srem(String key, String... members)
String spop(String key)
Set<String> spop(String key, long count)
Long smove(String srckey, String dstkey, String member)
Long scard(String key)
Boolean sismember(String key, String member)
Set<String> sinter(String... keys)
Long sinterstore(String dstkey, String... keys)
Set<String> sunion(String... keys)
Long sunionstore(String dstkey, String... keys)
Set<String> sdiff(String... keys)
Long sdiffstore(String dstkey, String... keys)
String srandmember(String key)
List<String> srandmember(String key, int count)
Long zadd(String key, double score, String member)
Long zadd(String key, double score, String member, ZAddParams params)
Long zadd(String key, Map<String, Double> scoreMembers)
Long zadd(String key, Map<String, Double> scoreMembers, ZAddParams params)
Set<String> zrange(String key, long start, long end)
Long zrem(String key, String... members)
Double zincrby(String key, double score, String member)
Double zincrby(String key, double score, String member, ZIncrByParams params)
Long zrank(String key, String member)
Long zrevrank(String key, String member)
Set<String> zrevrange(String key, long start, long end)
Set<Tuple> zrangeWithScores(String key, long start, long end)
Set<Tuple> zrevrangeWithScores(String key, long start, long end)
Long zcard(String key)
Double zscore(String key, String member)
String watch(String... keys)
List<String> sort(String key)
List<String> sort(String key, SortingParams sortingParameters)
List<String> blpop(int timeout, String... keys)
List<String> blpop(String... args)
List<String> brpop(String... args)
List<String> blpop(String arg)
List<String> brpop(String arg)
Long sort(String key, SortingParams sortingParameters, String dstkey)
Long sort(String key, String dstkey)
List<String> brpop(int timeout, String... keys)
Long zcount(String key, double min, double max)
Long zcount(String key, String min, String max)
Set<String> zrangeByScore(String key, double min, double max)
Set<String> zrangeByScore(String key, String min, String max)
Set<String> zrangeByScore(String key, double min, double max, int offset, int count)
Set<String> zrangeByScore(String key, String min, String max, int offset, int count)
Set<Tuple> zrangeByScoreWithScores(String key, double min, double max)
Set<Tuple> zrangeByScoreWithScores(String key, String min, String max)
Set<Tuple> zrangeByScoreWithScores(String key, double min, double max, int offset, int count)
Set<Tuple> zrangeByScoreWithScores(String key, String min, String max, int offset, int count)
Set<String> zrevrangeByScore(String key, double max, double min)
Set<String> zrevrangeByScore(String key, String max, String min)
Set<String> zrevrangeByScore(String key, double max, double min, int offset, int count)
Set<Tuple> zrevrangeByScoreWithScores(String key, double max, double min)
Set<Tuple> zrevrangeByScoreWithScores(String key, double max, double min, int offset, int count)
Set<Tuple> zrevrangeByScoreWithScores(String key, String max, String min, int offset, int count)
Set<String> zrevrangeByScore(String key, String max, String min, int offset, int count)
Set<Tuple> zrevrangeByScoreWithScores(String key, String max, String min)
Long zremrangeByRank(String key, long start, long end)
Long zremrangeByScore(String key, double start, double end)
Long zremrangeByScore(String key, String start, String end)
Long zunionstore(String dstkey, String... sets)
Long zunionstore(String dstkey, ZParams params, String... sets)
Long zinterstore(String dstkey, String... sets)
Long zinterstore(String dstkey, ZParams params, String... sets)
Long zlexcount(String key, String min, String max)
Set<String> zrangeByLex(String key, String min, String max)
Set<String> zrangeByLex(String key, String min, String max, int offset, int count)
Set<String> zrevrangeByLex(String key, String max, String min)
Set<String> zrevrangeByLex(String key, String max, String min, int offset, int count)
Long zremrangeByLex(String key, String min, String max)
Long strlen(String key)
Long lpushx(String key, String... string)
Long persist(String key)
Long rpushx(String key, String... string)
String echo(String string)
Long linsert(String key, LIST_POSITION where, String pivot, String value)
String brpoplpush(String source, String destination, int timeout)
Boolean setbit(String key, long offset, boolean value)
Boolean setbit(String key, long offset, String value)
Boolean getbit(String key, long offset)
Long setrange(String key, long offset, String value)
String getrange(String key, long startOffset, long endOffset)
Long bitpos(String key, boolean value)
Long bitpos(String key, boolean value, BitPosParams params)
List<String> configGet(String pattern)
String configSet(String parameter, String value)
Object eval(String script, int keyCount, String... params)
void subscribe(JedisPubSub jedisPubSub, String... channels)
Long publish(String channel, String message)
void psubscribe(JedisPubSub jedisPubSub, String... patterns)
Object eval(String script, List<String> keys, List<String> args)
Object eval(String script)
Object evalsha(String script)
Object evalsha(String sha1, List<String> keys, List<String> args)
Object evalsha(String sha1, int keyCount, String... params)
Boolean scriptExists(String sha1)
List<Boolean> scriptExists(String... sha1)
String scriptLoad(String script)
List<Slowlog> slowlogGet()
List<Slowlog> slowlogGet(long entries)
Long objectRefcount(String string)
String objectEncoding(String string)
Long objectIdletime(String string)
Long bitcount(String key)
Long bitcount(String key, long start, long end)
Long bitop(BitOP op, String destKey, String... srcKeys)
List<Map<String, String>> sentinelMasters()
List<String> sentinelGetMasterAddrByName(String masterName)
Long sentinelReset(String pattern)
List<Map<String, String>> sentinelSlaves(String masterName)
String sentinelFailover(String masterName)
String sentinelMonitor(String masterName, String ip, int port, int quorum)
String sentinelRemove(String masterName)
String sentinelSet(String masterName, Map<String, String> parameterMap)
byte[] dump(String key)
String restore(String key, int ttl, byte[] serializedValue)
Long pexpire(String key, int milliseconds)
Long pexpire(String key, long milliseconds)
Long pexpireAt(String key, long millisecondsTimestamp)
Long pttl(String key)
String psetex(String key, int milliseconds, String value)
String psetex(String key, long milliseconds, String value)
String set(String key, String value, String nxxx)
String set(String key, String value, String nxxx, String expx, int time)
String clientKill(String client)
String clientSetname(String name)
String migrate(String host, int port, String key, int destinationDb, int timeout)
ScanResult<String> scan(int cursor)
ScanResult<String> scan(int cursor, ScanParams params)
ScanResult<Entry<String, String>> hscan(String key, int cursor)
ScanResult<Entry<String, String>> hscan(String key, int cursor, ScanParams params)
ScanResult<String> sscan(String key, int cursor)
ScanResult<String> sscan(String key, int cursor, ScanParams params)
ScanResult<Tuple> zscan(String key, int cursor)
ScanResult<Tuple> zscan(String key, int cursor, ScanParams params)
ScanResult<String> scan(String cursor)
ScanResult<String> scan(String cursor, ScanParams params)
ScanResult<Entry<String, String>> hscan(String key, String cursor)
ScanResult<Entry<String, String>> hscan(String key, String cursor, ScanParams params)
ScanResult<String> sscan(String key, String cursor)
ScanResult<String> sscan(String key, String cursor, ScanParams params)
ScanResult<Tuple> zscan(String key, String cursor)
ScanResult<Tuple> zscan(String key, String cursor, ScanParams params)
String clusterNodes()
String readonly()
String clusterMeet(String ip, int port)
String clusterReset(Reset resetType)
String clusterAddSlots(int... slots)
String clusterDelSlots(int... slots)
String clusterInfo()
List<String> clusterGetKeysInSlot(int slot, int count)
String clusterSetSlotNode(int slot, String nodeId)
String clusterSetSlotMigrating(int slot, String nodeId)
String clusterSetSlotImporting(int slot, String nodeId)
String clusterSetSlotStable(int slot)
String clusterForget(String nodeId)
String clusterFlushSlots()
Long clusterKeySlot(String key)
Long clusterCountKeysInSlot(int slot)
String clusterSaveConfig()
String clusterReplicate(String nodeId)
List<String> clusterSlaves(String nodeId)
String clusterFailover()
List<Object> clusterSlots()
String asking()
List<String> pubsubChannels(String pattern)
Long pubsubNumPat()
Map<String, String> pubsubNumSub(String... channels)
void close()
void setDataSource(Pool<Jedis> jedisPool)
Long pfadd(String key, String... elements)
long pfcount(String key)
long pfcount(String... keys)
String pfmerge(String destkey, String... sourcekeys)
List<String> blpop(int timeout, String key)
List<String> brpop(int timeout, String key)
Long geoadd(String key, double longitude, double latitude, String member)
Long geoadd(String key, Map<String, GeoCoordinate> memberCoordinateMap)
Double geodist(String key, String member1, String member2)
Double geodist(String key, String member1, String member2, GeoUnit unit)
List<String> geohash(String key, String... members)
List<GeoCoordinate> geopos(String key, String... members)
List<GeoRadiusResponse> georadius(String key, double longitude, double latitude, double radius, GeoUnit unit)
List<GeoRadiusResponse> georadius(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param)
List<GeoRadiusResponse> georadiusByMember(String key, String member, double radius, GeoUnit unit)
List<GeoRadiusResponse> georadiusByMember(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param)
List<Long> bitfield(String key, String... arguments)

六、Redis有哪些应用场景?

上面Value类型部分也有涉及。

  • 分布式锁:string的setnx命令,2.8版本之后可以同时设置过期时间
  • 消息队列:list,可以阻塞式弹出(如blpop)
  • 延时队列:zset,过期时间作为score并且排序
  • 缓存:string(对象json化)、hash(属性分开)
  • 布隆过滤器:引入插件,Jedis没有扩展命令支持
  • 限流:list漏桶,每次请求之前先做漏水操作
  • 地图最近位置找点:GeoHash
  • 用户访问(UV)统计:简单的直接用string的incr,同一用户多次算做一次用set去重,数量大的HyperLogLog(允许误差)
  • 用户排名:zset

Redis 的业务应用范围非常广泛,让我们以掘金技术社区(juejin.im)的帖子模块为实 例,梳理一下,Redis 可以用在哪些地方?

1记录帖子的点赞数评论数和点击数 (hash)。

2记录用户的帖子 ID 列表 (排序)便于快速显示用户的帖子列表 (zset)

3记录帖子的标题摘要作者和封面信息用于列表页展示 (hash)

4记录帖子的点赞用户 ID 列表评论 ID 列表用于显示和去重计数 (zset)

5缓存近期热帖内容 (帖子内容空间占用比较大)减少数据库压力 (hash)

6记录帖子的相关文章 ID根据内容推荐相关帖子 (list)

7如果帖子 ID 是整数自增的可以使用 Redis 来分配帖子 ID(计数器)

8收藏集和帖子之间的关系 (zset)

9记录热榜帖子 ID 列表总热榜和分类热榜 (zset)

10缓存用户行为历史进行恶意行为过滤 (zset,hash)

七、内存爆了咋办?

Maxmemory配置指令

maxmemory配置指令用于配置Redis存储数据时指定限制的内存大小。通过redis.conf可以设置该指令,或者之后使用CONFIG SET命令来进行运行时配置。

例如为了配置内存限制为100mb,以下的指令可以放在redis.conf文件中。

maxmemory 100mb

设置maxmemory为0代表没有内存限制。对于64位的系统这是个默认值,对于32位的系统默认内存限制为3GB。

当指定的内存限制大小达到时,需要选择不同的行为,也就是策略。 Redis可以仅仅对命令返回错误,这将使得内存被使用得更多,或者回收一些旧的数据来使得添加数据时可以避免内存限制。

回收策略

当maxmemory限制达到的时候Redis会使用的行为由 Redis的maxmemory-policy配置指令来进行配置。

以下的策略是可用的:

  • noeviction:返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但DEL和几个例外)
  • allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
  • volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。
  • allkeys-random: 回收随机的键使得新添加的数据有空间存放。
  • volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
  • volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。

如果没有键满足回收的前提条件的话,策略volatile-lruvolatile-random以及volatile-ttl就和noeviction 差不多了。

选择正确的回收策略是非常重要的,这取决于你的应用的访问模式,不过你可以在运行时进行相关的策略调整,并且监控缓存命中率和没命中的次数,通过RedisINFO命令输出以便调优。

一般的经验规则:

  • 使用allkeys-lru策略:当你希望你的请求符合一个幂定律分布,也就是说,你希望部分的子集元素将比其它其它元素被访问的更多。如果你不确定选择什么,这是个很好的选择。.
  • 使用allkeys-random:如果你是循环访问,所有的键被连续的扫描,或者你希望请求分布正常(所有元素被访问的概率都差不多)。
  • 使用volatile-ttl:如果你想要通过创建缓存对象时设置TTL值,来决定哪些对象应该被过期。

allkeys-lru 和 volatile-random策略对于当你想要单一的实例实现缓存及持久化一些键时很有用。不过一般运行两个实例是解决这个问题的更好方法。

为了键设置过期时间也是需要消耗内存的,所以使用allkeys-lru这种策略更加高效,因为没有必要为键取设置过期时间当内存有压力时。

回收进程如何工作

理解回收进程如何工作是非常重要的:

  • 一个客户端运行了新的命令,添加了新的数据。
  • Redi检查内存使用情况,如果大于maxmemory的限制, 则根据设定好的策略进行回收。
  • 一个新的命令被执行,等等。
  • 所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断地回收回到边界以下。

如果一个命令的结果导致大量内存被使用(例如很大的集合的交集保存到一个新的键),不用多久内存限制就会被这个内存使用量超越。

近似LRU算法

Redis的LRU算法并非完整的实现。这意味着Redis并没办法选择最佳候选来进行回收,也就是最久未被访问的键。相反它会尝试运行一个近似LRU的算法,通过对少量keys进行取样,然后回收其中一个最好的key(被访问时间较早的)。

不过从Redis 3.0算法已经改进为回收键的候选池子。这改善了算法的性能,使得更加近似真是的LRU算法的行为。

Redis LRU有个很重要的点,你通过调整每次回收时检查的采样数量,以实现调整算法的精度。这个参数可以通过以下的配置指令调整:

maxmemory-samples 5

Redis为什么不使用真实的LRU实现是因为这需要太多的内存。

※文章内容部分摘自《Redis深度历险:核心原理和应用实践》和Redis中文官网※

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值