关于redis:聊聊claudb的pubsub-command

序本文次要钻研一下claudb的pubsub command PublishCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/pubsub/PublishCommand.java @Command("publish")@ParamLength(2)public class PublishCommand implements DBCommand, SubscriptionSupport, PatternSubscriptionSupport { @Override public RedisToken execute(Database db, Request request) { String channel = request.getParam(0).toString(); SafeString message = request.getParam(1); return integer(publishAll(getClauDB(request.getServerContext()), channel, message)); } private int publishAll(DBServerContext server, String channel, SafeString message) { int count = publish(server, channel, message); int pcount = patternPublish(server, channel, message); return count + pcount; }}PublishCommand实现了DBCommand, SubscriptionSupport, PatternSubscriptionSupport接口,其execute办法执行publishAll(getClauDB(request.getServerContext()), channel, message)SubscribeCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/pubsub/SubscribeCommand.java @ReadOnly@Command("subscribe")@ParamLength(1)@PubSubAllowedpublic class SubscribeCommand implements DBCommand, SubscriptionSupport { private static final String SUBSCRIBE = "subscribe"; @Override public RedisToken execute(Database db, Request request) { Database admin = getAdminDatabase(request.getServerContext()); String sessionId = getSessionId(request); Sequence<SafeString> channels = getChannels(request); int i = channels.size(); List<Object> result = new LinkedList<>(); for (SafeString channel : request.getParams()) { addSubscription(admin, sessionId, channel); getSessionState(request.getSession()).addSubscription(channel); result.addAll(asList(SUBSCRIBE, channel, ++i)); } return convert(result); } private String getSessionId(Request request) { return request.getSession().getId(); } private Sequence<SafeString> getChannels(Request request) { return getSessionState(request.getSession()).getSubscriptions(); }}SubscribeCommand实现了DBCommand, SubscriptionSupport接口,其execute办法遍历channel挨个执行addSubscription及getSessionState(request.getSession()).addSubscription(channel)UnsubscribeCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/pubsub/UnsubscribeCommand.java ...

August 29, 2020 · 2 min · jiezi

关于redis:聊聊claudb的zset-command

序本文次要钻研一下zset command SortedSetAddCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/zset/SortedSetAddCommand.java @Command("zadd")@ParamLength(3)@ParamType(DataType.ZSET)public class SortedSetAddCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { try { DatabaseValue initial = db.getOrDefault(safeKey(request.getParam(0)), DatabaseValue.EMPTY_ZSET); DatabaseValue result = db.merge(safeKey(request.getParam(0)), parseInput(request), (oldValue, newValue) -> { Set<Entry<Double, SafeString>> merge = new SortedSet(); merge.addAll(oldValue.getSortedSet()); merge.addAll(newValue.getSortedSet()); return zset(merge); }); return integer(changed(initial.getSortedSet(), result.getSortedSet())); } catch (NumberFormatException e) { return error("ERR value is not a valid float"); } } private int changed(Set<Entry<Double, SafeString>> input, Set<Entry<Double, SafeString>> result) { return result.size() - input.size(); } private DatabaseValue parseInput(Request request) { Set<Entry<Double, SafeString>> set = new SortedSet(); SafeString score = null; for (SafeString string : request.getParams().stream().skip(1).collect(toList())) { if (score != null) { set.add(score(parseFloat(score.toString()), string)); score = null; } else { score = string; } } return zset(set); }}SortedSetAddCommand实现了DBCommand接口,其execute办法先获取initial,而后执行db.merge办法,它先增加oldValue.getSortedSet()、再增加newValue.getSortedSet()SortedSetCardinalityCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/zset/SortedSetCardinalityCommand.java ...

August 28, 2020 · 4 min · jiezi

关于redis:聊聊claudb的set-command

序本文次要钻研一下claudb的set command SetAddCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/set/SetAddCommand.java @Command("sadd")@ParamLength(2)@ParamType(DataType.SET)public class SetAddCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { List<SafeString> values = request.getParams().stream().skip(1).collect(toList()); DatabaseValue value = db.merge(safeKey(request.getParam(0)), set(values), (oldValue, newValue) -> set(oldValue.getSet().appendAll(newValue.getSet()))); return integer(value.size()); }}SetAddCommand实现了DBCommand接口,其execute办法先从request参数提取values,而后执行db.merge,应用oldValue.getSet().appendAll(newValue.getSet())SetMembersCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/set/SetMembersCommand.java @ReadOnly@Command("smembers")@ParamLength(1)@ParamType(DataType.SET)public class SetMembersCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { DatabaseValue value = db.getOrDefault(safeKey(request.getParam(0)), DatabaseValue.EMPTY_SET); return convert(value); }}SetMembersCommand实现了DBCommand接口,其execute办法通过db.getOrDefault获取DatabaseValueSetCardinalityCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/set/SetCardinalityCommand.java @ReadOnly@Command("scard")@ParamLength(1)@ParamType(DataType.SET)public class SetCardinalityCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { ImmutableSet<SafeString> set = db.getSet(request.getParam(0)); return integer(set.size()); }}SetCardinalityCommand实现了DBCommand接口,其execute办法通过db.getSet(request.getParam(0))获取ImmutableSet,而后返回set.size()SetIsMemberCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/set/SetIsMemberCommand.java ...

August 27, 2020 · 2 min · jiezi

关于redis:聊聊claudb的list-command

序本文次要钻研一下claudb的list command LeftPushCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/list/LeftPushCommand.java @Command("lpush")@ParamLength(2)@ParamType(DataType.LIST)public class LeftPushCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { ImmutableList<SafeString> values = request.getParams().asList().tail().reverse(); DatabaseValue result = db.merge(safeKey(request.getParam(0)), list(values), (oldValue, newValue) -> list(newValue.getList().appendAll(oldValue.getList()))); return RedisToken.integer(result.size()); }}LeftPushCommand实现了DBCommand接口,其execute办法提取values,而后执行db.merge,在newValue.getList()之后追加上oldValue.getList()LeftPopCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/list/LeftPopCommand.java @Command("lpop")@ParamLength(1)@ParamType(DataType.LIST)public class LeftPopCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { List<SafeString> removed = new LinkedList<>(); db.merge(safeKey(request.getParam(0)), DatabaseValue.EMPTY_LIST, (oldValue, newValue) -> { ImmutableList<SafeString> list = oldValue.getList(); list.head().stream().forEach(removed::add); return list(list.tail()); }); if (removed.isEmpty()) { return nullString(); } else { return string(removed.remove(0)); } }}LeftPopCommand实现了DBCommand接口,其execute办法执行db.merge,它先获取oldValue.getList(),而后取出head增加到removed中,而后再通过list.tail()抛弃head作为新的后果RightPushCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/list/RightPushCommand.java ...

August 26, 2020 · 3 min · jiezi

关于redis:聊聊claudb的keys-command

序本文次要钻研一下claudb的keys command KeysCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/key/KeysCommand.java @ReadOnly@Command("keys")@ParamLength(1)public class KeysCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { GlobPattern pattern = createPattern(request.getParam(0)); ImmutableSet<SafeString> keys = db.entrySet() .filter(matchPattern(pattern)) .filter(filterExpired(Instant.now()).negate()) .map(Tuple2::get1) .map(DatabaseKey::getValue); return convert(keys); } private GlobPattern createPattern(SafeString param) { return new GlobPattern(param.toString()); } private Matcher1<Tuple2<DatabaseKey, DatabaseValue>> filterExpired(Instant now) { return entry -> entry.get2().isExpired(now); } private Matcher1<Tuple2<DatabaseKey, DatabaseValue>> matchPattern(GlobPattern pattern) { return entry -> pattern.match(entry.get1().toString()); }}KeysCommand实现了DBCommand接口,其execute办法先通过createPattern创立GlobPattern,之后遍历db.entrySet(),过滤出matchPattern的,再过滤出非expired的,最初返回DeleteCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/key/DeleteCommand.java @Command("del")@ParamLength(1)public class DeleteCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { int removed = 0; for (SafeString key : request.getParams()) { DatabaseValue value = db.remove(safeKey(key)); if (value != null) { removed += 1; } } return integer(removed); }}DeleteCommand实现了DBCommand接口,其execute办法遍历request.getParams()执行db.remove(safeKey(key)),最初返回integer(removed)ExistsCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/key/ExistsCommand.java ...

August 25, 2020 · 2 min · jiezi

关于redis:搞定面试官系列Redis基础

引言在互联网电商热火朝天倒退的大背景下,传统的关系型数据库(MySQL、Oracle)未然无奈满足高并发、限时秒杀等简单的场景。此时NoSQL(非关系型数据库)应运而生,而Redis就是NoSQL大军里的一颗璀璨新星,在大厂面试中也是相对绕不开的话题。 面试开始> 同学你好,先简略做个主动介绍吧?面试官您好,我叫少侠露飞,……,纯熟使用Redis、RocketMQ等中间件。很期待退出贵部门。 > 好的,我看你自我介绍时提到了Redis,你们我的项目里为何采纳Redis,或者说你们是基于什么场景利用Redis的呢?什么?心里忍不住暗骂,这叫啥问题,大家都在用,我就是为了用而用。然而咱们必定不能把实在想法说进去,做人当然要有点外延嘛。 于是认真答道:“闪闪发光”的面试官您好,因为传统的MySQL数据库曾经不能实用所有的场景了,比方限时秒杀,大流量削峰等,这些场景刹时流量可达到数万甚至数十万级,这些服务全部打到MySQL,数据库必然扛不住,很容易被打崩造成服务宕机,所以引入了缓存中间件。在缓存中间件畛域有 Redis 和 Memcached 两个领头羊,然而Redis反对更多的数据类型,所以在综合思考之后抉择了Redis。 > 嗯,答得不错,你刚刚提到了数据类型,那介绍一下Redis有哪些数据类型及相应的应用场景吧?string(字符串),hash(哈希),list(列表),set(汇合)及zset(sorted set:有序汇合) 数据类型特点应用场景stringkey-value1.缓存性能:Redis作为缓存层,MySQL作为存储层,减速读写并升高后端压力;2.计数器性能,如视频点击量、文章浏览量等hashkey-field-value假如要存储的对象有很多属性,那么为了示意该对象,必然有很多Key来形容该对象,然而接下来如果我批改某个值,还要把这个对象取出来再批改,比拟麻烦,所以就有了Hashlistkey-value,在一个list首端或末端增加元素在朋友圈、微博等交友平台的发帖、回复、点赞、查看某一帖子点赞数等set数据无序且不能反复set能够做并集、交加运算,所以在独特好友、二度好友举荐场景下使用较多zset数据有序,给每个member一个分数值,可反对按分数排序微博等网站的排行榜阐明了这五种根本数据类型并详细分析了各自的利用场景,千万不要洋洋自得甚至都要被本人折服了,因为这仅仅是合格。你想从数千个竞争者中怀才不遇,必定要有些货色,你还须要补充说到这三种数据结构:<font color="#E96900">HyperLogLog、Geo、Pub/Sub</font> 这里我用一个理论的场景阐明一下HyperLogLog。在电商畛域,常常要统计某一页面的PV/UV(别离是浏览次数、浏览人数)。统计UV时必定要思考到去重,即同一个用户一天内无论点击多少次,都只算作一次。这个时候HyperLogLog就能够一展能为了。HyperLogLog是基于基数统计,通过hash碰撞实现,用户Id通过给定的hash算法每次都会失去同样的值,在哈希桶的同一索引地位。当然统计UV不必HyperLogLog也是能够的,就用set或者string(须要用到setnx)类型一样可行,这个不急,待会面试官就会问到。须要留神的是,统计每天的UV,相干的键须要设置当天过期,不然你会发现之后每天的UV数据都是异样的。 > 嗯,小伙子对Redis的数据结构把握的还不错(心田曾经给你比起了大拇指),刚刚你有提到setnx,能够简略说说这个原理及用途么?<font color="#E96900">面试相对不要让本人处于齐全被动的位置。不知聪慧的你们发现了没有,面试官问的Redis、setnx等问题都是我先在答复中提到的,也就是说面试官能够自在提问,我也能够被动的在答复中提到一些知识点,疏导面试官询问相干,化被动为被动。这样就尽可能的把面试管制在咱们的节奏中。</font> 答复面试官的问题:setnx经常用作分布式锁,setnx核心思想在于争抢锁,抢到之后执行相干逻辑,留神执行完之后不要遗记用expire给锁设置一个过期工夫。 > 接着面试官开始下一轮的守势,问到如果在setnx后再执行expire期间过程意外crash或者服务重启保护了,那怎么办?这个时候你要立刻做出回应:当这种状况setnx的锁就永远不会被开释了,这是个危险的操作。而后稍加思考并给出解决方案:Redis的set蕴含了丰盛的指令参数,这两个命令能够合并成一条命令变为原子操作来执行。 > 面试官曾经晓得背后的小伙不简略,于是决定加大难度:如果Redis外面有十亿个key,其中有10w是以固定已知前缀结尾的,如何将它们找进去?应用keys指令能够扫出指定模式的key列表。到这里你还能够更秀一点。你接着补充到,然而因为Redis是单线程模型,如果Redis正在给线上提供服务,keys指令会导致线程阻塞一段时间,线上服务会进展。这个时候能够应用scan指令,scan指令能够无阻塞的提取出指定模式的key列表,然而会有肯定的反复概率,将返回后果手动去重就行了。 > 这个时候面试官曾经克制不住冲动的情绪了,都学会抢答了,这个小伙子挺有料啊。既然刚刚提到了Redis的线程模型,能够说下么?这个时候不要慌,稳稳的答道:Redis是单线程模型,底层是I/O多路复用(对于此知识点我将会在Nginx的事件驱动模块做具体介绍)。 > 此时面试官曾经感到背后的小伙不个别了,于是持续开怼:Redis是一种内存数据库,数据如何长久化呢?有两种形式:RDB做镜像全量长久化,AOF做增量长久化。因为RDB会消耗较长时间,难以做到实时长久化,在停机的时候会导致大量失落数据,所以须要AOF来配合应用。在Redis实例重启时,会应用RDB长久化文件从新构建内存,再应用AOF重放近期的操作指令来实现残缺复原重启之前的状态。这里很好了解,把RDB了解为一整个表全量的数据,AOF了解为每次操作的日志就好了,服务器重启的时候先把表的数据全副塞进内存,然而他可能不残缺,你再回访一下日志,数据不就残缺了嘛。不过Redis自身的机制是 AOF长久化开启且存在AOF文件时,优先加载AOF文件;AOF敞开或者AOF文件不存在时,加载RDB文件;加载AOF/RDB文件之后,Redis启动胜利; AOF/RDB文件存在谬误时,Redis启动失败并打印错误信息。 > 长久化的时候忽然断电了会怎么?会失落数据,但这取决于AOF日志sync属性的配置,如果不要求性能,在每条写指令时都sync一下磁盘,就不会失落数据。然而在高性能的要求下每次都sync是不事实的,个别都应用定时sync,比方5s1次,这个时候最多就会失落5s的数据。 > 面试官此时决定跟你卯上了,接着诘问:RDB的原理呢?这里有两个要害的步骤:fork和COW。fork是指Redis通过创立子过程来进行RDB操作,COW指的是copy on write,子过程创立后,父子过程共享数据段,父过程持续提供读写服务,写脏的页面数据会逐步和子过程拆散开来。注:答复这个问题的时候,如果你还能说出AOF和RDB的优缺点,我感觉面试官都会为你点赞的,对于此点我会在之后的博客持续补充。 > 既然在这类问题难不倒你,聪慧的面试官决定换个方向问你:应用过Redis做异步队列么,怎么用的?面试官千方百计的想问倒咱们,咱们就凭借满腹经纶化解一次次的危机,所谓振长策而于宇内,奥利给…认真答道:Redis中个别用list数据结构作为异步队列,用rpush生产音讯,lpop生产音讯。当消费者lpop发现没有音讯的时候要睡眠sleep一会再重试。此时你再补充说道:除了应用sleep,list还有个指令叫blpop,在没有音讯的时候,它会阻塞直到有新的音讯到来。 > 这个时候面试官会感觉你这小伙子对Redis的见解很独到,在心里曾经录用你100次了。然而,外表上还是要沉稳,毕竟是久经沙场的老技术人了。问答持续:像刚刚那种模式,数据blpop之后别的消费者就生产不了了,如何实现生产一次生产屡次呢?应用pub/sub主题订阅者模式,能够实现 1:N 的音讯队列。 > 那么pub/sub主题订阅者模式的毛病呢?该模式有个问题在于消费者下线的时候,生产的音讯会失落,得应用业余的音讯队列RocketMQ或Kafka。 > 如果面试官还不罢休,Redis如何实现延时队列?这一系列的连环提问,预计有急躁如你,也想把面试官打一顿:还有完没完了。疫情这么重大都不敢在里面吃,我得回家本人做饭,人家今天还要下班呢!然而,为了大厂的offer,小伙子还是克服一下,而后神态自若的答复道:应用有序汇合sortedset(zset),以音讯内容作为key,用工夫戳作为score来调用zadd命令生产音讯,消费者用zrangebyscore指令获取N秒之前的数据轮询进行生产。 > 答复到这里,面试官曾经为你竖起了大拇指,并且心里默默的给了你A+,然而他还不肯进行,筛不筛选人才的无所谓,次要就想问倒你。于是吹响了下一轮防御的号角:晓得pipeline吗?能够将屡次IO往返的工夫缩减为一次,不过有个前提是pipeline执行的指令之间没有因果相关性。 注:实际上应用redis-benchmark进行压测的时候能够发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。 > 理解过Redis的同步机制么?Redis能够应用主从同步,从从同步。第一次同步时,主节点做一次bgsave,并同时将后续批改操作记录到内存buffer,待实现后将RDB文件全量同步到复制节点,复制节点承受实现后将RDB镜像加载到内存。加载实现后,再告诉主节点将期间批改的操作记录同步到复制节点进行重放就实现了同步过程。后续的增量数据通过AOF日志同步即可,有点相似数据库的binlog。 > 应用过Redis的集群么?如何保障集群的高可用?Redis Sentinal (Redis哨兵,这个我之后会独自写一篇博客介绍)着眼于高可用,在master宕机时会主动将slave晋升为master,持续提供服务。 Redis Cluster 着眼于扩展性,在单个redis内存不足时,应用Cluster进行分片存储。 面试完结小伙子能够啊,我很称心,咱们部门就缺你这种人才,要不明天就把入职手续办了吧?这个时候,你千万要稳住,按捺一下冲动无比的心田:这么急啊,疫情这么重大,房子不好找啊,要不下周一吧。面试官一听,哎呀,这小伙子预计在手的offer不少啊,这种人才怎么能放过,不行,人事经理何在,加钱!!!当这些问题你都一一答复上来了,你是不是都感觉本人很棒呢? 总结在技术面试的时候,不论是Redis还是什么问题,如果你能举出理论开发过程的问题和播种会给面试官的印象分会加很多,答复逻辑性也要强一点,不要东一榔头西一棒子的,容易把本人都绕晕了。并且面试不应该简略的一问一答,若能在问题之外扩散一些知识点,面试官会感觉你不只是一个会写代码的人,你逻辑清晰,你对技术选型,对中间件、对我的项目都有本人的了解和思考,心里天然会给你点赞的。 点点关注,不会迷路

August 25, 2020 · 1 min · jiezi

关于redis:redis学习笔记之6zset集合排行榜topN

上一篇: redis学习笔记之-(5)-list(栈/队列/阻塞队列) 排行榜/topN 加入成员: zadd key score member查问成员: zrange key start stop [withscores] 注: start = 0 end = -1 注: start/end都蕴含删除成员: zrem key member查看某个成员的 score: zscore key member减少某个成员的score: zincrby key increment member查看联合的size: zcard key查看topN/顺叙: zrevrange key start stop [withscores] 注: start/end都蕴含# 增加成员12个127.0.0.1:6379> zadd books 1 Python编程1127.0.0.1:6379> zadd books 2 数学之美1127.0.0.1:6379> zadd books 3 浪潮之巅1127.0.0.1:6379> zadd books 4 机器学习1127.0.0.1:6379> zadd books 5 深刻了解Java虚拟机1127.0.0.1:6379> zadd books 6 鸟哥的Linux私房菜1127.0.0.1:6379> zadd books 7 算法(第4版)1127.0.0.1:6379> zadd books 8 'C Primer Plus'1127.0.0.1:6379> zadd books 9 "Head First Java(中文版)"1127.0.0.1:6379> zadd books 10 Java编程思维(第4版)1127.0.0.1:6379> zadd books 11 "C++ Primer中文版(第5版)"1127.0.0.1:6379> zadd books 12 计算机网络:自顶向下办法(原书第7版)1127.0.0.1:6379> zrange books 0 -1 withscores ## 查问所有成员=>带分数Python编程1数学之美2浪潮之巅3机器学习4深刻了解Java虚拟机5鸟哥的Linux私房菜6算法(第4版)7C Primer Plus8Head First Java(中文版)9Java编程思维(第4版)10C++ Primer中文版(第5版)11计算机网络:自顶向下办法(原书第7版)12127.0.0.1:6379> zrem books 'C Primer Plus' ## 删除 'C Primer Plus'1127.0.0.1:6379> zrange books 0 -1 withscores ## 查问所有成员=>带分数Python编程1数学之美2浪潮之巅3机器学习4深刻了解Java虚拟机5鸟哥的Linux私房菜6算法(第4版)7Head First Java(中文版)9Java编程思维(第4版)10C++ Primer中文版(第5版)11计算机网络:自顶向下办法(原书第7版)12127.0.0.1:6379> zscore books 数学之美 ## 查看'数学之美'成员的 score2127.0.0.1:6379> zincrby books 20 数学之美 # 22127.0.0.1:6379> zscore books 数学之美22127.0.0.1:6379> zcard books # 查看汇合size11127.0.0.1:6379> zrevrange books 0 4 withscores # 逆序查看 topN => start=0 stop=4数学之美22计算机网络:自顶向下办法(原书第7版)12C++ Primer中文版(第5版)11Java编程思维(第4版)10Head First Java(中文版)9127.0.0.1:6379>

August 25, 2020 · 1 min · jiezi

关于redis:redis学习笔记之5list栈队列阻塞队列

上一篇: redis学习笔记之-(4)-set(无序不反复汇合) list无关的命令: lpush/rpush/lpop/rpop/brpop/blpop 5.1 栈(stack)=lpush+lpop(出入同一端)lpush+lpop127.0.0.1:6379> lpush juc synchronized volatile aqs thread(integer) 4127.0.0.1:6379> lpop juc # 最初进入的最先出列"thread"127.0.0.1:6379> lpop juc # 倒数第二个"aqs"127.0.0.1:6379> lpop juc # 倒数第三个"volatile"127.0.0.1:6379> 5.2 队列(queue)=lpush+rpop(出入两端)lpush+rpop127.0.0.1:6379> del juc (integer) 1127.0.0.1:6379> lpush juc synchronized volatile aqs thread(integer) 4127.0.0.1:6379> rpop juc # 先进先出"synchronized"127.0.0.1:6379> rpop juc # 先进先出"volatile"127.0.0.1:6379> rpop juc # 先进先出"aqs"127.0.0.1:6379> 5.3 阻塞队列=lpush+brpop(pop阻塞)lpush+brpopbrpop语法: brpop key [key...] timout timeout = 0 示意如果没有数据插入就始终阻塞; timeout = 5 示意阻塞 5秒 如果工夫到还没有值就返回null; ...

August 25, 2020 · 1 min · jiezi

关于redis:聊聊claudb的server-command

序本文次要钻研一下claudb的server command SelectCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/server/SelectCommand.java @ReadOnly@Command("select")@ParamLength(1)public class SelectCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { try { getSessionState(request.getSession()).setCurrentDB(parseCurrentDB(request)); return responseOk(); } catch (NumberFormatException e) { return error("ERR invalid DB index"); } } private int parseCurrentDB(Request request) { return parseInt(request.getParam(0).toString()); }}SelectCommand实现了DBCommand接口,其execute办法执行getSessionState(request.getSession()).setCurrentDB(parseCurrentDB(request))SyncCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/server/SyncCommand.java @ReadOnly@Command("sync")public class SyncCommand implements DBCommand { private MasterReplication master; @Override public RedisToken execute(Database db, Request request) { try { DBServerContext server = getClauDB(request.getServerContext()); ByteBufferOutputStream output = new ByteBufferOutputStream(); server.exportRDB(output); if (master == null) { master = new MasterReplication(server); master.start(); } master.addSlave(request.getSession().getId()); return string(new SafeString(output.toByteArray())); } catch (IOException e) { return error("ERROR replication error"); } }}SyncCommand实现了DBCommand接口,其execute办法先通过getClauDB获取server,而后执行server.exportRDB(output)、master.addSlave(request.getSession().getId()),而后返回string(new SafeString(output.toByteArray()))SlaveOfCommandclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/server/SlaveOfCommand.java ...

August 24, 2020 · 2 min · jiezi

关于redis:Redis基础

Redis介绍概述Redis是Remote Dictionary Server(近程数据服务)的缩写.由意大利人antirez(Salvatore Sanfilippo)开发的一款内存高速缓存数据库,诞生于08年。依据月度排行网站DB-Engines.com的数据显示,Redis是最风行的键值对存储数据库。 该软件应用C语言编写,它的数据模型为key-value,开源的,构建与内存的数据结构数据库,罕用作数据存储,缓存解决和音讯队列解决反对多种数据结构类型,包含string(字符串)、hash(哈希)、list(链表)、set(汇合)、Zset(有序汇合)。为了保障效率数据都是缓存在内存中,它也能够周期性的把更新的数据写入磁盘或者把批改操作写入追加的记录文件。极高的读写性能,原子性操作Redis能读的速度是110000次/s,写的速度是81000次/s 。Redis所有单个命令的执行都是原子性的,这与它的单线程机制无关; Redis命令的原子性使得咱们不必思考并发问题,能够不便的利用原子性自增操作 实现简略计数器性能; 应用缓存加重数据库的负载。 在开发网站的时候如果有一些数据在短时间之内不会发生变化,而它们还要被频繁拜访,为了进步用户的申请速度和升高网站的负载,就把这些数据放到一个读取速度更快的介质上(或者是通过较少的计算量就能够取得该数据) ,该行为就称作对该数据的缓存。 该介质能够是:文件、数据库、内存,内存介子常常用于数据缓存。 缓存的两种模式: 页面缓存(磁盘缓存):常常用在CMS(content manage system)内存管理系统里边(Smarty缓存)index.php ==== >index.html 就间接拜访index.html页面; 数据缓存:常常会用在页面的具体数据里边 应用场合及其劣势高性能缓存,最常见的利用场景多类型数据结构,适宜各种类型数据,Redis分布式存储数据有生命周期,Redis的键能够设置过期时长,一段时间当前主动删除。高并发和海量数据的解决数据长久化,数据存储到硬盘外面,服务器断电不失落。redis与memcache比拟 1、数据类型:memcache反对的数据类型就是字符串, redis反对的数据类型有字符串,哈希,链表,汇合,有序汇合。 2、长久化: memcache数据是存储到内存外面,一旦断电,或重启,则数据失落。 redis数据也是存储到内存外面的,然而能够长久化,周期性的把数据给保留到硬盘外面,导致重启,或断电不会失落数据。 3、数据量: memcahce一个键存储的数据最大是1M, 而redis的一个键值,存储的最大数据量是1G的数据量。 装置redis#下载wget http://download.redis.io/releases/redis-6.0.6.tar.gz#解压tar zxvf redis-6.0.6.tar.gz#进入解压目录cd redis-6.0.6#无需配置,间接编译make若呈现如下提醒,则阐明未装置gcc /bin/sh: cc: command not foundmake[1]: * [adlist.o] Error 127make[1]: Leaving directory `/root/redis-6.0.6/src'make: * [all] Error 2解决:yum -y install gcc 若呈现如下提醒 make[1]: * [server.o] Error 1make[1]: Leaving directory `/root/redis-6.0.1/src' make: * [all] Error 2 ...

August 24, 2020 · 7 min · jiezi

关于redis:带你redis从入门到不会系列了解跳跃表只看这篇大概不够

前言oh 肉丝 oh 杰克you jump! i jump!yeah 一起沉迷在jump的喜悦里明天跟大家来聊聊redis的跳跃表 everybody 都须要理解的跳跃表 不仅仅是概念 还有代码 强烈建议浏览《Redis5 设计与源码剖析》陈雷编著 有序汇合咱们先看看咱们平时罕用的对于redis的有序汇合大家罕用的命令如下 127.0.0.1:6379> zadd jump 1 jack(integer) 1127.0.0.1:6379> zadd jump 2 rose(integer) 1127.0.0.1:6379> zrange jump 0 11) "jack"2) "rose"redis的有序汇合的复杂度ZADD O(M*log(N)), N 是有序集的基数, M 为胜利增加的新成员的数量。ZRANGE O(log(N)+M), N 为有序集的基数,而 M 为后果集的基数ZRANK O(log(N))ZSCORE O(1) 咱们无妨能够思考下,什么样的数据结构能满足上述,来这时候拿出咱们的数据结构常识储备了。线性表、链表、栈与队列、串、树它全家系列想想哪种能合乎上述 也是常见问题 为什么应用跳表当数据结构线性表 查找合乎 增加和删除 是O(n) 不能够链表 增加和删除合乎 查找是O(n) 不能够树 (不论是什么二叉、红黑、B它全家)如同复杂度能够满足 然而咱们看看zscore是O(1) 如同不是内味而且树的实现有多蛋疼,能够常常听到以下对话你能够手撕 ** 树吗我能够爬 ** 树 你看能够吗? zscore 返回有序集 key 中,成员 member 的 score 值。如果是O(1)的话 有链表那味那么我猜跳表就是链表的它私生子,相对不是树的私生子。 ...

August 23, 2020 · 3 min · jiezi

关于redis:如何提高redis缓存命中率

redis缓存在redis的理论利用场景中,用做缓存居多mysql关系型数据库查问数据较慢(受架构、老本等方面的因素),而redis纯内存操作,做缓存能够大大提高数据访问速度 redis缓存命中率的几个概念命中(keyspace_hits):间接从Redis缓存获取数据,而不是从数据库不命中(keyspace_misses):缓存中没有数据,须要去数据库加载命中率:能够通过命令info查看命中与未命中状况keyspace_hits:1741626534keyspace_misses:36020166命中率算法:keyspace_hits/(keyspace_hits+keyspace_misses) 进步redis缓存命中率1.业务场景及时效性缓存适宜读多写少的场景,尽可能的聚焦在高频拜访且时效性要求不高的热点业务上(如字典数据、session、token)。拜访频率越高,命中率天然也越高;时效性越低,在雷同key和雷同申请数的状况下,缓存工夫越长,命中率会越高。 注:如果是高并发,就算时效性短,也一样能够有很高的缓存命中率 2.缓存粒度缓存的粒度越小,缓存命中率越高。也就是说,单个key缓存的数据单位越小,越不容易产生更改,比方一个key A只缓存用户地址,另外一个key B缓存用户信息的汇合(比方蕴含用户名、明码、地址、手机号码等),那么只有批改用户的手机号码,key B就生效了 3.缓存过期策略此处的缓存过期策略并非redis自带定期删除和惰性删除策略,而是依据业务场景优化key的过期工夫,如用户的key信息,如果同时过期,那么多个用户同时查问时,就会都落到数据库去,也就是说要防止缓存同时生效 注:redis缓存自带的过期策略在理论利用中,可能有大量曾经生效的key仍然存在于redis中 4.缓存预加载-预热redis的缓存必定都是从数据库加载的,那么第一次应用数据的时候,redis须要从数据库去加载数据,所以在对用户前,能够提前加载须要的数据到缓存,这样用户在第一次拜访的时候就能够间接走缓存而不是去查询数据库 5.避免缓存击穿和穿透缓存击穿和穿透也会影响缓存命中率,当然如果产生,大概率呈现利用故障了。缓存击穿:次要是缓存过期时的高并发拜访,能够通过加锁,同一key只容许一个连贯拜访到DB数据库解决 缓存穿透:个别是拜访不存在的key,导致落到数据库(可能数据库也没有),这样会升高缓存命中率(1)利用拜访缓存,如果数据存在,则间接返回数据(2)数据在redis不存在,则去拜访数据库,数据库查问到了间接返回利用,同时把后果写回redis(3)数据在redis不存在,数据库也不存在,返回空,一般来说空值是不会写入redis的,如果重复申请同一条数据,那么则会产生缓存穿透解决:能够应用布隆过滤器先判断key是否存在,存在才去redis缓存读取(redis缓存里可能key也不存在,这时候就去数据库读取,没有则返回空) 6.缓存容量要留神缓存容量,太小会触发redis的内存淘汰机制,线上redis个别配置maxmemory-policy allkeys-lru算法来进行内存淘汰,这样有一部分key会被删除,导致缓存穿透,从而升高缓存命中率,因而合理配置缓存容量很有必有

August 23, 2020 · 1 min · jiezi

关于redis:聊聊claudb的Database

序本文次要钻研一下claudb的Database Databaseclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/data/Database.java public interface Database { int size(); boolean isEmpty(); boolean containsKey(DatabaseKey key); DatabaseValue get(DatabaseKey key); DatabaseValue put(DatabaseKey key, DatabaseValue value); DatabaseValue remove(DatabaseKey key); void clear(); ImmutableSet<DatabaseKey> keySet(); Sequence<DatabaseValue> values(); ImmutableSet<Tuple2<DatabaseKey, DatabaseValue>> entrySet(); default SafeString getString(SafeString key) { return getOrDefault(safeKey(key), DatabaseValue.EMPTY_STRING).getString(); } default ImmutableList<SafeString> getList(SafeString key) { return getOrDefault(safeKey(key), DatabaseValue.EMPTY_LIST).getList(); } default ImmutableSet<SafeString> getSet(SafeString key) { return getOrDefault(safeKey(key), DatabaseValue.EMPTY_SET).getSet(); } default NavigableSet<Entry<Double, SafeString>> getSortedSet(SafeString key) { return getOrDefault(safeKey(key), DatabaseValue.EMPTY_ZSET).getSortedSet(); } default ImmutableMap<SafeString, SafeString> getHash(SafeString key) { return getOrDefault(safeKey(key), DatabaseValue.EMPTY_HASH).getHash(); } default void putAll(ImmutableMap<? extends DatabaseKey, ? extends DatabaseValue> map) { map.forEach(this::put); } default DatabaseValue putIfAbsent(DatabaseKey key, DatabaseValue value) { DatabaseValue oldValue = get(key); if (oldValue == null) { oldValue = put(key, value); } return oldValue; } default DatabaseValue merge(DatabaseKey key, DatabaseValue value, BiFunction<DatabaseValue, DatabaseValue, DatabaseValue> remappingFunction) { DatabaseValue oldValue = get(key); DatabaseValue newValue = oldValue == null ? value : remappingFunction.apply(oldValue, value); if(newValue == null) { remove(key); } else { put(key, newValue); } return newValue; } default DatabaseValue getOrDefault(DatabaseKey key, DatabaseValue defaultValue) { DatabaseValue value = get(key); return (value != null || containsKey(key)) ? value : defaultValue; } default boolean isType(DatabaseKey key, DataType type) { DatabaseValue value = get(key); return value != null ? value.getType() == type : true; } default boolean rename(DatabaseKey from, DatabaseKey to) { DatabaseValue value = remove(from); if (value != null) { put(to, value); return true; } return false; } default void overrideAll(ImmutableMap<DatabaseKey, DatabaseValue> value) { clear(); putAll(value); } default ImmutableSet<DatabaseKey> evictableKeys(Instant now) { return entrySet() .filter(entry -> entry.get2().isExpired(now)) .map(Tuple2::get1); }}Database接口定义了size、isEmpty、containsKey、get、put、remove、clear、keySet、values、entrySet办法;同时还提供了getString、getList、getSet、getSortedSet、getHash、putAll、putIfAbsent、merge、getOrDefault、isType、rename、overrideAll、evictableKeys这几个default办法OnHeapDatabaseclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/data/OnHeapDatabase.java ...

August 23, 2020 · 4 min · jiezi

关于redis:聊聊claudb的SlaveReplication

序本文次要钻研一下claudb的SlaveReplication SlaveReplicationclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/replication/SlaveReplication.java public class SlaveReplication implements RespCallback { private static final DatabaseKey MASTER_KEY = safeKey("master"); private static final Logger LOGGER = LoggerFactory.getLogger(SlaveReplication.class); private static final String SYNC_COMMAND = "SYNC"; private final RespClient client; private final DBServerContext server; private final DBCommandProcessor processor; private final String host; private final int port; public SlaveReplication(DBServerContext server, Session session, String host, int port) { this.server = server; this.host = host; this.port = port; this.client = new RespClient(host, port, this); this.processor = new DBCommandProcessor(server, session); } public void start() { client.start(); server.setMaster(false); server.getAdminDatabase().put(MASTER_KEY, createState(false)); } public void stop() { client.stop(); server.setMaster(true); } @Override public void onConnect() { LOGGER.info("Connected with master"); client.send(array(string(SYNC_COMMAND))); server.getAdminDatabase().put(MASTER_KEY, createState(true)); } @Override public void onDisconnect() { LOGGER.info("Disconnected from master"); server.getAdminDatabase().put(MASTER_KEY, createState(false)); } @Override public void onMessage(RedisToken token) { token.accept(RedisTokenVisitor.builder() .onString(string -> { processRDB(string); return null; }) .onArray(array -> { processor.processCommand(array); return null; }).build()); } private void processRDB(StringRedisToken token) { try { SafeString value = token.getValue(); server.importRDB(toStream(value)); LOGGER.info("loaded RDB file from master"); } catch (IOException e) { LOGGER.error("error importing RDB file", e); } } private InputStream toStream(SafeString value) { return new ByteBufferInputStream(value.getBytes()); } private DatabaseValue createState(boolean connected) { return hash(entry(safeString("host"), safeString(host)), entry(safeString("port"), safeString(valueOf(port))), entry(safeString("state"), safeString(connected ? "connected" : "disconnected"))); }}SlaveReplication实现了RespCallback接口,其结构器创立RespClient、DBCommandProcessor;其start办法执行client.start(),标记server为slave及master connect为false;其stop办法执行client.stop()及标记server为master;其onConnect办法执行client.send(array(string(SYNC_COMMAND)))并标记master connect为true;其onDisconnect标记master connect为false;其onMessage办法在onString时执行processRDB,在onArray时执行processor.processCommand(array);processRDB办法执行server.importRDB(toStream(value))DBCommandProcessorclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/DBCommandProcessor.java ...

August 22, 2020 · 2 min · jiezi

关于redis:redis学习笔记之4set无序不重复集合

上一篇: redis学习笔记之-(3)-HyperLogLogs(HLL)的应用 4.1 set汇合: 抽奖sadd key member [member...] 增加元素到汇合 smembers key 列出所有member srandmember key count 随机从key的汇合中抉择count个成员, 然而不移除它们; spop key count 随机从key的汇合中抉择count个成员,并移除它们; 127.0.0.1:6379> sadd choujiang 1000(integer) 1127.0.0.1:6379> sadd choujiang 1001(integer) 1127.0.0.1:6379> sadd choujiang 1002(integer) 1127.0.0.1:6379> sadd choujiang 1003(integer) 1127.0.0.1:6379> sadd choujiang 2000(integer) 1127.0.0.1:6379> sadd choujiang 20003(integer) 1127.0.0.1:6379> sadd choujiang 20003(integer) 0127.0.0.1:6379> smembers choujiang1) "1000"2) "1001"3) "1002"4) "1003"5) "2000"6) "20003"127.0.0.1:6379> srandmember choujiang 31) "1002"2) "1001"3) "20003"127.0.0.1:6379> smembers choujiang1) "1000"2) "1001"3) "1002"4) "1003"5) "2000"6) "20003"127.0.0.1:6379> spop choujiang 21) "2000"2) "1001"127.0.0.1:6379> spop choujiang 21) "1002"2) "20003"127.0.0.1:6379> smembers choujiang1) "1000"2) "1003"127.0.0.1:6379> sadd choujiang 3001 3002 3004(integer) 3127.0.0.1:6379> smembers choujiang1) "1000"2) "1003"3) "3001"4) "3002"5) "3004"127.0.0.1:6379>4.2 关注/点赞/珍藏/转发/标签key=like:niewj存储的是关注过niewj的人 ...

August 21, 2020 · 2 min · jiezi

关于redis:聊聊claudb的DatabaseCleaner

序本文次要钻研一下claudb的DatabaseCleaner DatabaseCleanerclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/data/DatabaseCleaner.java public class DatabaseCleaner { private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseCleaner.class); private final DBServerContext server; private final DBConfig config; private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); public DatabaseCleaner(DBServerContext server, DBConfig config) { this.server = server; this.config = config; } public void start() { executor.scheduleWithFixedDelay(this::clean, config.getCleanPeriod(), config.getCleanPeriod(), TimeUnit.SECONDS); } public void stop() { executor.shutdown(); } private void clean() { LOGGER.debug("cleaning database: running"); server.clean(Instant.now()); LOGGER.debug("cleaning database: done"); }}DatabaseCleaner的start办法每隔config.getCleanPeriod()(默认30)秒调度执行clean办法;clear办法执行server.clean(Instant.now())cleanclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/ClauDB.java ...

August 20, 2020 · 1 min · jiezi

关于redis:redis学习笔记之2bitmap用法之2上亿用户1周连续活跃用户数统计

上一篇: redis学习笔记之-(1)-bitmap用法之1-统计所有用户1年的登录天数 2. bitmap应用2:上亿个用户的1周内间断沉闷用户数2.1 思路需要2: 上亿个用户,统计一周内间断沉闷用户100000000/8/1024/1024 = 11.9M 一个bitmap占用 不到12M 好在一周只有7天, 咱们用7个key的bitmap来存储状态即可, 加上最初的一个后果res的bitmap: 12*8=100M 内存即可! 遵循上面步骤即可: 用户编号是前提, 每个用户的编号从1到n(n=就是说的那个上亿的最大值);申明7个bitmap, 从周一到周日: mon tue wed thur fri sat sun;每个用户编号所在的offset在周1如果登录了, 就是1, 没登录就是0;7个bitmap都设值记录;用bitop 对7个bitmap进行位AND操作, 7天都登录的当天的位上才是1最初的后果进行bitcount操作, 就是上亿用户一周内间断流动的人数!如果想晓得间断沉闷的用户都有哪些人, 遍历 getbit 每一天的key(mon/tue/...) id即可!示例: 咱们模仿5个用户吧: 用户IDmontuewedthurfrisatsun001111111100200000110031111111004111111100501010112.2 前4步: 初始化7个bitmap周一的key: mon, 周一所有用户的登录状态记录: 127.0.0.1:6379> setbit mon 1 1(integer) 0127.0.0.1:6379> setbit mon 2 0(integer) 0127.0.0.1:6379> setbit mon 3 1(integer) 0127.0.0.1:6379> setbit mon 4 1(integer) 0127.0.0.1:6379> setbit mon 5 0(integer) 0周二的key: tue, 所有用户的登录状态记录: ...

August 20, 2020 · 2 min · jiezi

关于redis:redis学习笔记之bitmap用法之统计用户全年登录天数

1. bitmap应用案例需要: 电商网站统计所有用户一年的登录天数, 比方用户id为, 咱们想要统计用户每年的登录天数, 比方如下 用户名用户id本年登录天数张三001100李四002200王五003365应用redis的bitmap来实现的话, 能够这么弄: setbit key offset value setbit ulogin:001 20200101 1 ulogin:001是bitmap的key20200101是offset, 记录某一天的登录值的key标识当天登录了记为1, 没有记为0;查问某天:getbit key offset 统计所有该key的1的值的个数(也就是登录天数)bitcount key 1.1 记录某天登录操作-setbit127.0.0.1:6379> setbit ulogin:001 20200101 1(integer) 0127.0.0.1:6379> setbit ulogin:001 20200102 1(integer) 0127.0.0.1:6379> setbit ulogin:001 20200103 1(integer) 0127.0.0.1:6379> setbit ulogin:001 20200104 0(integer) 01.2 查问某天登录了没有-getbit127.0.0.1:6379> getbit ulogin:001 20200102(integer) 11.3 查问用户总共登录了几天-bitcount127.0.0.1:6379> bitcount ulogin:001(integer) 3如果要统计所有的用户的登录天数, 咱们平时每天记录: 127.0.0.1:6379> setbit ulogin:002 20200101 1(integer) 0127.0.0.1:6379> setbit ulogin:003 20200102 1(integer) 01.4 统计所有用户登录天数如果要统计所有用户, 在java中利用前缀的命名规定ulogin: 在一个for循环中遍历所有的id列表即可, 把调用redis的后果放入一个汇合中即可!! ...

August 20, 2020 · 1 min · jiezi

关于redis:聊聊claudb的importRDB

序本文次要钻研一下claudb的importRDB importRDBclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/DBServerState.java public class DBServerState { //...... public void importRDB(InputStream input) throws IOException { RDBInputStream rdb = new RDBInputStream(input); Map<Integer, Map<DatabaseKey, DatabaseValue>> load = rdb.parse(); for (Map.Entry<Integer, Map<DatabaseKey, DatabaseValue>> entry : load.entrySet()) { databases.get(entry.getKey()).overrideAll(ImmutableMap.from(entry.getValue())); } } //......}importRDB办法创立RDBInputStream,而后执行其parse办法进行解析,之后遍历解析后果,挨个执行databases.get(entry.getKey()).overrideAll(ImmutableMap.from(entry.getValue()))RDBInputStreamclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/persistence/RDBInputStream.java public class RDBInputStream { private static final SafeString REDIS_PREAMBLE = safeString("REDIS"); private static final long TO_MILLIS = 1000L; private static final int HASH = 0x04; private static final int SORTED_SET = 0x03; private static final int SET = 0x02; private static final int LIST = 0x01; private static final int STRING = 0x00; private static final int TTL_MILLISECONDS = 0xFC; private static final int TTL_SECONDS = 0xFD; private static final int SELECT = 0xFE; private static final int END_OF_STREAM = 0xFF; private static final int REDIS_VERSION = 6; private static final int VERSION_LENGTH = 4; private static final int REDIS_LENGTH = 5; private final CheckedInputStream in; public RDBInputStream(InputStream in) { this.in = new CheckedInputStream(in, new CRC64()); } public Map<Integer, Map<DatabaseKey, DatabaseValue>> parse() throws IOException { Map<Integer, Map<DatabaseKey, DatabaseValue>> databases = new HashMap<>(); int version = version(); if (version > REDIS_VERSION) { throw new IOException("invalid version: " + version); } Long expireTime = null; HashMap<DatabaseKey, DatabaseValue> db = null; for (boolean end = false; !end;) { int read = in.read(); switch (read) { case SELECT: db = new HashMap<>(); databases.put(readLength(), db); break; case TTL_SECONDS: expireTime = parseTimeSeconds(); break; case TTL_MILLISECONDS: expireTime = parseTimeMillis(); break; case STRING: ensure(db, readKey(), readString(expireTime)); expireTime = null; break; case LIST: ensure(db, readKey(), readList(expireTime)); expireTime = null; break; case SET: ensure(db, readKey(), readSet(expireTime)); expireTime = null; break; case SORTED_SET: ensure(db, readKey(), readSortedSet(expireTime)); expireTime = null; break; case HASH: ensure(db, readKey(), readHash(expireTime)); expireTime = null; break; case END_OF_STREAM: // end of stream end = true; db = null; expireTime = null; break; default: throw new IOException("not supported: " + read); } } verifyChecksum(); return databases; } private int version() throws IOException { SafeString redis = new SafeString(read(REDIS_LENGTH)); if (!redis.equals(REDIS_PREAMBLE)) { throw new IOException("not valid stream"); } return parseVersion(read(VERSION_LENGTH)); } private int parseVersion(byte[] version) { StringBuilder sb = new StringBuilder(); for (byte b : version) { sb.append((char) b); } return Integer.parseInt(sb.toString()); } private void verifyChecksum() throws IOException { long calculated = in.getChecksum().getValue(); long readed = parseChecksum(); if (calculated != readed) { throw new IOException("invalid checksum: " + readed); } } private long parseChecksum() throws IOException { return ByteUtils.byteArrayToLong(read(Long.BYTES)); } //......}RDBInputStream的结构器应用CheckedInputStream对InputStream进行包装;其parse办法先解析version进行校验,之后循环进行in.read(),别离解析SELECT、TTL_SECONDS、TTL_MILLISECONDS、STRING、LIST、SET、SORTED_SET、HASH、END_OF_STREAM;最初执行verifyChecksum校验checksumreadKeyclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/persistence/RDBInputStream.java ...

August 19, 2020 · 4 min · jiezi

关于redis:聊聊claudb的exportRDB

序本文次要钻研一下claudb的exportRDB exportRDBclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/DBServerState.java public class DBServerState { //...... public void exportRDB(OutputStream output) throws IOException { RDBOutputStream rdb = new RDBOutputStream(output); rdb.preamble(RDB_VERSION); for (int i = 0; i < databases.size(); i++) { Database db = databases.get(i); if (!db.isEmpty()) { rdb.select(i); rdb.dabatase(db); } } rdb.end(); } //......}exportRDB办法先通过rdb.preamble(RDB_VERSION)写入redis魔数及版本;而后遍历databases,挨个执行rdb.select(i)写入SELECT及db的长度,在执行rdb.dabatase(db),遍历entry,挨个按expiredAt、type、key、value写入数据;end办法写入END_OF_STREAM,而后再写入checksumRDBOutputStreamclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/persistence/RDBOutputStream.java public class RDBOutputStream { private static final byte[] REDIS = safeString("REDIS").getBytes(); private static final int TTL_MILLISECONDS = 0xFC; private static final int END_OF_STREAM = 0xFF; private static final int SELECT = 0xFE; private final CheckedOutputStream out; public RDBOutputStream(OutputStream out) { super(); this.out = new CheckedOutputStream(out, new CRC64()); } public void preamble(int version) throws IOException { out.write(REDIS); out.write(version(version)); } private byte[] version(int version) { StringBuilder sb = new StringBuilder(String.valueOf(version)); for (int i = sb.length(); i < Integer.BYTES; i++) { sb.insert(0, '0'); } return sb.toString().getBytes(StandardCharsets.UTF_8); } public void select(int db) throws IOException { out.write(SELECT); length(db); } public void dabatase(Database db) throws IOException { for (Tuple2<DatabaseKey, DatabaseValue> entry : db.entrySet()) { value(entry.get1(), entry.get2()); } } private void value(DatabaseKey key, DatabaseValue value) throws IOException { expiredAt(value.getExpiredAt()); type(value.getType()); key(key); value(value); } private void expiredAt(Instant expiredAt) throws IOException { if (expiredAt != null) { out.write(TTL_MILLISECONDS); out.write(ByteUtils.toByteArray(expiredAt.toEpochMilli())); } } private void type(DataType type) throws IOException { out.write(type.ordinal()); } private void key(DatabaseKey key) throws IOException { string(key.getValue()); } private void value(DatabaseValue value) throws IOException { switch (value.getType()) { case STRING: string(value.getString()); break; case LIST: list(value.getList()); break; case HASH: hash(value.getHash()); break; case SET: set(value.getSet()); break; case ZSET: zset(value.getSortedSet()); break; default: break; } } private void length(int length) throws IOException { if (length < 0x40) { // 1 byte: 00XXXXXX out.write(length); } else if (length < 0x4000) { // 2 bytes: 01XXXXXX XXXXXXXX int b1 = length >> 8; int b2 = length & 0xFF; out.write(0x40 | b1); out.write(b2); } else { // 5 bytes: 10...... XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX out.write(0x80); out.write(toByteArray(length)); } } private void string(String value) throws IOException { string(safeString(value)); } private void string(SafeString value) throws IOException { byte[] bytes = value.getBytes(); length(bytes.length); out.write(bytes); } private void string(double value) throws IOException { string(String.valueOf(value)); } private void list(ImmutableList<SafeString> value) throws IOException { length(value.size()); for (SafeString item : value) { string(item); } } private void hash(ImmutableMap<SafeString, SafeString> value) throws IOException { length(value.size()); for (Tuple2<SafeString, SafeString> entry : value.entries()) { string(entry.get1()); string(entry.get2()); } } private void set(ImmutableSet<SafeString> value) throws IOException { length(value.size()); for (SafeString item : value) { string(item); } } private void zset(NavigableSet<Entry<Double, SafeString>> value) throws IOException { length(value.size()); for (Entry<Double, SafeString> item : value) { string(item.getValue()); string(item.getKey()); } } public void end() throws IOException { out.write(END_OF_STREAM); out.write(toByteArray(out.getChecksum().getValue())); out.flush(); }}RDBOutputStream的结构器用CheckedOutputStream包装了OutputStream;其dabatase办法遍历db.entrySet(),挨个执行value办法;value办法先别离执行expiredAt、type、key、value办法;value办法针对STRING、LIST、HASH、SET、ZSET这几种value类型做了不同的解决;string办法间接写入string;list办法先写入list大小,再挨个写入list元素;hash办法先写入hash大小,再挨个写入key和value;set先写入set大小,在挨个写入set元素;zset先写入zset大小,再挨个写入value和score;end办法写入END_OF_STREAM,而后再写入checksumCheckedOutputStreamjava.base/java/util/zip/CheckedOutputStream.java ...

August 18, 2020 · 4 min · jiezi

关于redis:聊聊claudb的exportRDB

序本文次要钻研一下claudb的exportRDB exportRDBclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/DBServerState.java public class DBServerState { //...... public void exportRDB(OutputStream output) throws IOException { RDBOutputStream rdb = new RDBOutputStream(output); rdb.preamble(RDB_VERSION); for (int i = 0; i < databases.size(); i++) { Database db = databases.get(i); if (!db.isEmpty()) { rdb.select(i); rdb.dabatase(db); } } rdb.end(); } //......}exportRDB办法先通过rdb.preamble(RDB_VERSION)写入redis魔数及版本;而后遍历databases,挨个执行rdb.select(i)写入SELECT及db的长度,在执行rdb.dabatase(db),遍历entry,挨个按expiredAt、type、key、value写入数据;end办法写入END_OF_STREAM,而后再写入checksumRDBOutputStreamclaudb-1.7.1/src/main/java/com/github/tonivade/claudb/persistence/RDBOutputStream.java public class RDBOutputStream { private static final byte[] REDIS = safeString("REDIS").getBytes(); private static final int TTL_MILLISECONDS = 0xFC; private static final int END_OF_STREAM = 0xFF; private static final int SELECT = 0xFE; private final CheckedOutputStream out; public RDBOutputStream(OutputStream out) { super(); this.out = new CheckedOutputStream(out, new CRC64()); } public void preamble(int version) throws IOException { out.write(REDIS); out.write(version(version)); } private byte[] version(int version) { StringBuilder sb = new StringBuilder(String.valueOf(version)); for (int i = sb.length(); i < Integer.BYTES; i++) { sb.insert(0, '0'); } return sb.toString().getBytes(StandardCharsets.UTF_8); } public void select(int db) throws IOException { out.write(SELECT); length(db); } public void dabatase(Database db) throws IOException { for (Tuple2<DatabaseKey, DatabaseValue> entry : db.entrySet()) { value(entry.get1(), entry.get2()); } } private void value(DatabaseKey key, DatabaseValue value) throws IOException { expiredAt(value.getExpiredAt()); type(value.getType()); key(key); value(value); } private void expiredAt(Instant expiredAt) throws IOException { if (expiredAt != null) { out.write(TTL_MILLISECONDS); out.write(ByteUtils.toByteArray(expiredAt.toEpochMilli())); } } private void type(DataType type) throws IOException { out.write(type.ordinal()); } private void key(DatabaseKey key) throws IOException { string(key.getValue()); } private void value(DatabaseValue value) throws IOException { switch (value.getType()) { case STRING: string(value.getString()); break; case LIST: list(value.getList()); break; case HASH: hash(value.getHash()); break; case SET: set(value.getSet()); break; case ZSET: zset(value.getSortedSet()); break; default: break; } } private void length(int length) throws IOException { if (length < 0x40) { // 1 byte: 00XXXXXX out.write(length); } else if (length < 0x4000) { // 2 bytes: 01XXXXXX XXXXXXXX int b1 = length >> 8; int b2 = length & 0xFF; out.write(0x40 | b1); out.write(b2); } else { // 5 bytes: 10...... XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX out.write(0x80); out.write(toByteArray(length)); } } private void string(String value) throws IOException { string(safeString(value)); } private void string(SafeString value) throws IOException { byte[] bytes = value.getBytes(); length(bytes.length); out.write(bytes); } private void string(double value) throws IOException { string(String.valueOf(value)); } private void list(ImmutableList<SafeString> value) throws IOException { length(value.size()); for (SafeString item : value) { string(item); } } private void hash(ImmutableMap<SafeString, SafeString> value) throws IOException { length(value.size()); for (Tuple2<SafeString, SafeString> entry : value.entries()) { string(entry.get1()); string(entry.get2()); } } private void set(ImmutableSet<SafeString> value) throws IOException { length(value.size()); for (SafeString item : value) { string(item); } } private void zset(NavigableSet<Entry<Double, SafeString>> value) throws IOException { length(value.size()); for (Entry<Double, SafeString> item : value) { string(item.getValue()); string(item.getKey()); } } public void end() throws IOException { out.write(END_OF_STREAM); out.write(toByteArray(out.getChecksum().getValue())); out.flush(); }}RDBOutputStream的结构器用CheckedOutputStream包装了OutputStream;其dabatase办法遍历db.entrySet(),挨个执行value办法;value办法先别离执行expiredAt、type、key、value办法;value办法针对STRING、LIST、HASH、SET、ZSET这几种value类型做了不同的解决;string办法间接写入string;list办法先写入list大小,再挨个写入list元素;hash办法先写入hash大小,再挨个写入key和value;set先写入set大小,在挨个写入set元素;zset先写入zset大小,再挨个写入value和score;end办法写入END_OF_STREAM,而后再写入checksumCheckedOutputStreamjava.base/java/util/zip/CheckedOutputStream.java ...

August 18, 2020 · 4 min · jiezi

关于redis:聊聊RedisTokenVisitor

序本文次要钻研一下RedisTokenVisitor RedisTokenVisitorresp-server-0.16.0/src/main/java/com/github/tonivade/resp/protocol/RedisTokenVisitor.java public interface RedisTokenVisitor<T> { T array(ArrayRedisToken token); T status(StatusRedisToken token); T string(StringRedisToken token); T integer(IntegerRedisToken token); T error(ErrorRedisToken token); T unknown(UnknownRedisToken token); static <T> LambdaRedisTokenVisitor.Builder<T> builder() { return new LambdaRedisTokenVisitor.Builder<>(); }}RedisTokenVisitor接口定义了array、status、string、integer、error、unknown办法,并提供了动态builder办法用于创立LambdaRedisTokenVisitor.BuilderAbstractRedisTokenVisitorresp-server-0.16.0/src/main/java/com/github/tonivade/resp/protocol/AbstractRedisTokenVisitor.java public abstract class AbstractRedisTokenVisitor<T> implements RedisTokenVisitor<T> { @Override public T array(ArrayRedisToken token) { return null; } @Override public T status(StatusRedisToken token) { return null; } @Override public T string(StringRedisToken token) { return null; } @Override public T error(ErrorRedisToken token) { return null; } @Override public T unknown(UnknownRedisToken token) { return null; } @Override public T integer(IntegerRedisToken token) { return null; }}AbstractRedisTokenVisitor实现了RedisTokenVisitor接口,默认返回nullLambdaRedisTokenVisitorresp-server-0.16.0/src/main/java/com/github/tonivade/resp/protocol/LambdaRedisTokenVisitor.java ...

August 17, 2020 · 4 min · jiezi

关于redis:聊聊RedisTokenVisitor

序本文次要钻研一下RedisTokenVisitor RedisTokenVisitorresp-server-0.16.0/src/main/java/com/github/tonivade/resp/protocol/RedisTokenVisitor.java public interface RedisTokenVisitor<T> { T array(ArrayRedisToken token); T status(StatusRedisToken token); T string(StringRedisToken token); T integer(IntegerRedisToken token); T error(ErrorRedisToken token); T unknown(UnknownRedisToken token); static <T> LambdaRedisTokenVisitor.Builder<T> builder() { return new LambdaRedisTokenVisitor.Builder<>(); }}RedisTokenVisitor接口定义了array、status、string、integer、error、unknown办法,并提供了动态builder办法用于创立LambdaRedisTokenVisitor.BuilderAbstractRedisTokenVisitorresp-server-0.16.0/src/main/java/com/github/tonivade/resp/protocol/AbstractRedisTokenVisitor.java public abstract class AbstractRedisTokenVisitor<T> implements RedisTokenVisitor<T> { @Override public T array(ArrayRedisToken token) { return null; } @Override public T status(StatusRedisToken token) { return null; } @Override public T string(StringRedisToken token) { return null; } @Override public T error(ErrorRedisToken token) { return null; } @Override public T unknown(UnknownRedisToken token) { return null; } @Override public T integer(IntegerRedisToken token) { return null; }}AbstractRedisTokenVisitor实现了RedisTokenVisitor接口,默认返回nullLambdaRedisTokenVisitorresp-server-0.16.0/src/main/java/com/github/tonivade/resp/protocol/LambdaRedisTokenVisitor.java ...

August 17, 2020 · 4 min · jiezi

关于redis:聊聊RedisToken

序本文次要钻研一下RedisToken RedisTokenresp-server-0.16.0/src/main/java/com/github/tonivade/resp/protocol/RedisToken.java public interface RedisToken { RedisToken NULL_STRING = string((SafeString) null); RedisToken RESPONSE_OK = status("OK"); RedisTokenType getType(); <T> T accept(RedisTokenVisitor<T> visitor); static RedisToken nullString() { return NULL_STRING; } static RedisToken responseOk() { return RESPONSE_OK; } static RedisToken string(SafeString str) { return new StringRedisToken(str); } static RedisToken string(String str) { return new StringRedisToken(safeString(str)); } static RedisToken status(String str) { return new StatusRedisToken(str); } static RedisToken integer(boolean b) { return new IntegerRedisToken(b ? 1 : 0); } static RedisToken integer(int i) { return new IntegerRedisToken(i); } static RedisToken error(String str) { return new ErrorRedisToken(str); } static RedisToken array(RedisToken... redisTokens) { return new ArrayRedisToken(ImmutableList.of(redisTokens)); } static RedisToken array(Collection<RedisToken> redisTokens) { return new ArrayRedisToken(ImmutableList.from(redisTokens)); } static RedisToken array(Sequence<RedisToken> redisTokens) { return new ArrayRedisToken(redisTokens.asArray()); } static <T> Stream<T> visit(Stream<RedisToken> tokens, RedisTokenVisitor<T> visitor) { return tokens.map(token -> token.accept(visitor)); }}RedisToken接口定义了getType、accept办法;它还提供了nullString、responseOk、string、status、integer、error、array、visit静态方法AbstractRedisTokenresp-server-0.16.0/src/main/java/com/github/tonivade/resp/protocol/AbstractRedisToken.java ...

August 16, 2020 · 2 min · jiezi

关于redis:一-Redis数据结构SDS和链表

最近在看《Redis设计与构造》这本书。整顿了一些笔记。 SDSRedis中的字符串基本上用的都是本人定义的sdshdr构造。 struct sdshdr { int len; int free; char buf[];};len属性记录了以后字符串的长度,值得注意的是,这个长度并不包含\0free为0时,代表以后SDS没有空余的空间。buf保留了一个c类型的字符串。并且以\0作为结尾。 这样子的设计有这些益处。 记录了字符串的长度,能够让咱们在O(1)工夫内获取长度。咱们能够给buf调配更大的空间,这样子在后续对字符串进行操作时,就不必频繁地分配内存了。SDS是二进制平安的。因为C类型的字符串永远以\0作为结尾,如果咱们须要保留的数据中蕴含了\0这样的字符,会导致拜访不到后续的字符。SDS则能够通过len元素来判断是否达到字符串的起点。依然能够应用C语言的字符串函数库。链表Redis应用了双向链表。 struct listNode { struct listNode *prev; struct listNode *next; void *value;};并且链表构造中保留了表头结点,表尾节点,链表长度以及一些函数。 struct list { listNode *head; listNode *tail; unsigned long len; ...}

August 16, 2020 · 1 min · jiezi

关于redis:一-Redis数据结构SDS和链表

最近在看《Redis设计与构造》这本书。整顿了一些笔记。 SDSRedis中的字符串基本上用的都是本人定义的sdshdr构造。 struct sdshdr { int len; int free; char buf[];};len属性记录了以后字符串的长度,值得注意的是,这个长度并不包含\0free为0时,代表以后SDS没有空余的空间。buf保留了一个c类型的字符串。并且以\0作为结尾。 这样子的设计有这些益处。 记录了字符串的长度,能够让咱们在O(1)工夫内获取长度。咱们能够给buf调配更大的空间,这样子在后续对字符串进行操作时,就不必频繁地分配内存了。SDS是二进制平安的。因为C类型的字符串永远以\0作为结尾,如果咱们须要保留的数据中蕴含了\0这样的字符,会导致拜访不到后续的字符。SDS则能够通过len元素来判断是否达到字符串的起点。依然能够应用C语言的字符串函数库。链表Redis应用了双向链表。 struct listNode { struct listNode *prev; struct listNode *next; void *value;};并且链表构造中保留了表头结点,表尾节点,链表长度以及一些函数。 struct list { listNode *head; listNode *tail; unsigned long len; ...}

August 16, 2020 · 1 min · jiezi

关于redis:Redis之object-idletime为什么是不可靠的

问题引入 Redis是一款基于内存的key-value数据库,内存使用率是咱们十分关注的一个指标。 通常咱们都能够通过『EXPIRE key seconds』等形式,为指定键设置生存工夫;待过期后,Redis会主动删除该键值对,以此避免内存应用持续增长。 然而,生产环境中,往往存在很多键值对都没有设置正当的生存工夫;随着业务的继续倒退,内存使用量也持续增长。 DBA或者研发人员,在监控到Redis占用内存过高时,能够手动清理某些长期不拜访的键值对。如何判断一个键值对多久没有被拜访了呢?能够通过命令object idletime实现,该命令返回的是以后键从上一次拜访到当初通过的工夫(单位,秒): 127.0.0.1:6379> object idletime key(integer) 2 在一次统计过程中,发现有一批key曾经很长时间没有拜访了(数月),然而object idletime命令却表明最近刚被拜访过(甚至数秒内)。 为什么会不准呢?为此钻研了下object idletime的实现原理。 object idletime实现 Redis外部对象应用构造体redisObject示意,其定义为: typedef struct redisObject { //数据类型,string,list,hash等 unsigned type:4; //编码,即底层采纳哪种数据结构 unsigned encoding:4; //缓存淘汰应用;会存储上一次该对象的拜访工夫 unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or * LFU data (least significant 8 bits frequency * and most significant 16 bits access time). */ //援用计数 int refcount; //指向对应的数据结构 void *ptr;} robj; 能够看到,正因为redisObject.lru字段的存在,咱们才能够获取到任何一个对象的闲暇工夫,以后工夫 - 上次访问工夫lru,即可。 ...

August 15, 2020 · 1 min · jiezi

关于redis:Redis之object-idletime为什么是不可靠的

问题引入 Redis是一款基于内存的key-value数据库,内存使用率是咱们十分关注的一个指标。 通常咱们都能够通过『EXPIRE key seconds』等形式,为指定键设置生存工夫;待过期后,Redis会主动删除该键值对,以此避免内存应用持续增长。 然而,生产环境中,往往存在很多键值对都没有设置正当的生存工夫;随着业务的继续倒退,内存使用量也持续增长。 DBA或者研发人员,在监控到Redis占用内存过高时,能够手动清理某些长期不拜访的键值对。如何判断一个键值对多久没有被拜访了呢?能够通过命令object idletime实现,该命令返回的是以后键从上一次拜访到当初通过的工夫(单位,秒): 127.0.0.1:6379> object idletime key(integer) 2 在一次统计过程中,发现有一批key曾经很长时间没有拜访了(数月),然而object idletime命令却表明最近刚被拜访过(甚至数秒内)。 为什么会不准呢?为此钻研了下object idletime的实现原理。 object idletime实现 Redis外部对象应用构造体redisObject示意,其定义为: typedef struct redisObject { //数据类型,string,list,hash等 unsigned type:4; //编码,即底层采纳哪种数据结构 unsigned encoding:4; //缓存淘汰应用;会存储上一次该对象的拜访工夫 unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or * LFU data (least significant 8 bits frequency * and most significant 16 bits access time). */ //援用计数 int refcount; //指向对应的数据结构 void *ptr;} robj; 能够看到,正因为redisObject.lru字段的存在,咱们才能够获取到任何一个对象的闲暇工夫,以后工夫 - 上次访问工夫lru,即可。 ...

August 15, 2020 · 1 min · jiezi

关于redis:聊聊RespCommand

序本文次要钻研一下RespCommand RespCommandresp-server-0.16.0/src/main/java/com/github/tonivade/resp/command/RespCommand.java @FunctionalInterfacepublic interface RespCommand { RedisToken execute(Request request);}RespCommand定义了execute办法,接管Request参数,返回RedisTokenRequestresp-server-0.16.0/src/main/java/com/github/tonivade/resp/command/Request.java public interface Request { String getCommand(); ImmutableArray<SafeString> getParams(); SafeString getParam(int i); Option<SafeString> getOptionalParam(int i); int getLength(); boolean isEmpty(); Session getSession(); ServerContext getServerContext(); boolean isExit();}Request接口定义了getCommand、getParams、getParam、getOptionalParam、getLength、isEmpty、getSession、getServerContext、isExit办法DefaultRequestresp-server-0.16.0/src/main/java/com/github/tonivade/resp/command/DefaultRequest.java public class DefaultRequest implements Request { private final SafeString command; private final ImmutableArray<SafeString> params; private final Session session; private final ServerContext server; public DefaultRequest(ServerContext server, Session session, SafeString command, ImmutableArray<SafeString> params) { this.server = server; this.session = session; this.command = requireNonNull(command); this.params = requireNonNull(params); } @Override public String getCommand() { return command.toString(); } @Override public ImmutableArray<SafeString> getParams() { return params; } @Override public SafeString getParam(int i) { if (i < params.size()) { return params.get(i); } return null; } @Override public Option<SafeString> getOptionalParam(int i) { return Option.of(() -> getParam(i)); } @Override public int getLength() { return params.size(); } @Override public boolean isEmpty() { return params.isEmpty(); } @Override public boolean isExit() { return command.toString().equalsIgnoreCase("quit"); } @Override public Session getSession() { return session; } @Override public ServerContext getServerContext() { return server; } @Override public String toString() { return command + "[" + params.size() + "]: " + params; }}DefaultRequest实现了Request接口,它定义了command、params、session、server属性,均在结构器中传入CommandSuiteresp-server-0.16.0/src/main/java/com/github/tonivade/resp/command/CommandSuite.java ...

August 15, 2020 · 3 min · jiezi

关于redis:sanritoolsmaven-企业软件开发工具集

你还在为应用一般的 redis 可视化工具而懊恼吗 ,软件装置麻烦,查看数据乱码,查问数据卡死,没有一个丑陋的界面,来试试这个工具吧 https://gitee.com/sanri/sanri-tools-maven专门为企业打造的 redis 数据可视化工具,它具备这些性能 集群节点展现,显示节点主机,端口,角色,父节点key 列表查问,能够查问集群所有节点,hashKey 列表查问,key 的长度,ttl , type 展现查问某个 key 的数据,能够依据序列化和类加载器来间接展现成数据明文 反对的性能列表这款工具集 kafka, redis , dubbo , mybatis ,soap ,database,zookeeper 数据可视化性能于一体,实现了日常开发中大部分须要查问组件内数据的性能,欢送提交倡议 star 或者 pr ,你的反对是我最大的动静,目前曾经有这些性能: tools-core 类加载治理 上传类 zip , class, java 文件到类加载器连贯治理 创立 zookeeper, redis , database 等连贯文件治理 零碎的临时文件和配置文件的治理,提供临时文件下载性能插件治理 插件注册,提供给前端可拜访的插件,做了一个监控指标,依据插件点击次数和最初拜访工夫来排序插件数据管理 能够给 class 随机生成数据,应用正则表达式生成数据,和爬网站数据性能tools-serializable 提供序列化,反序列化等根底性能tools-zookeeper zookeeper 连贯的子节点,acl权限,节点数据,节点元数据,删除节点,写入数据门路珍藏性能,不便下次间接查找tools-redis 集群节点展现,显示节点主机,端口,角色,父节点key 列表查问,能够查问集群所有节点,hashKey 列表查问,key 的长度,ttl , type 展现查问某个 key 的数据,能够依据序列化和类加载器来间接展现成数据明文tools-kafka 生产组列表,删除生产组,生产组生产的主题列表,生产组生产主题某分区生产数据状况,生产组生产主题某分区左近的数据主题列表,主题尾部数据,创立主题,删除主题,模仿数据发送主题数据实时监控,brokers 列表所有的数据生产或监控都能够应用序列化配合类加载器来展现数据明文tools-database 数据库元数据 catalog,schema,表,列,索引,主键 查问和搜寻扩大元数据 表类型(字典表,配置表,业务表),表关联关系设置,展现和搜寻数据库文档生成和下载应用模板生成代码,组合模板成计划生成代码应用 sql 预览数据库数据和 excel 导出,导入 excel 数据 ,表随机数据生成nacos,diamond 等依赖于数据库的配置数据能够间接展现tools-mybatis ...

August 15, 2020 · 1 min · jiezi

关于redis:聊聊RespServer

序本文次要钻研一下RespServer Respresp-server-0.16.0/src/main/java/com/github/tonivade/resp/Resp.java interface Resp { void channel(SocketChannel channel); void connected(ChannelHandlerContext ctx); void disconnected(ChannelHandlerContext ctx); void receive(ChannelHandlerContext ctx, RedisToken message);}Resp接口定义了channel、connected、disconnected、receive办法RespServerresp-server-0.16.0/src/main/java/com/github/tonivade/resp/RespServer.java public class RespServer implements Resp { private static final Logger LOGGER = LoggerFactory.getLogger(RespServer.class); private static final int BUFFER_SIZE = 1024 * 1024; private static final int MAX_FRAME_SIZE = BUFFER_SIZE * 100; private static final String DEFAULT_HOST = "localhost"; private static final int DEFAULT_PORT = 12345; private EventLoopGroup bossGroup; private EventLoopGroup workerGroup; private ChannelFuture future; private final RespServerContext serverContext; public RespServer(RespServerContext serverContext) { this.serverContext = requireNonNull(serverContext); } public static Builder builder() { return new Builder(); } public void start() { bossGroup = new NioEventLoopGroup(); workerGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() * 2); ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new RespInitializerHandler(this)) .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) .option(ChannelOption.SO_RCVBUF, BUFFER_SIZE) .option(ChannelOption.SO_SNDBUF, BUFFER_SIZE) .childOption(ChannelOption.SO_KEEPALIVE, true) .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); future = bootstrap.bind(serverContext.getHost(), serverContext.getPort()); // Bind and start to accept incoming connections. future.syncUninterruptibly(); serverContext.start(); LOGGER.info("server started: {}:{}", serverContext.getHost(), serverContext.getPort()); } public void stop() { try { if (future != null) { closeFuture(future.channel().close()); } future = null; } finally { workerGroup = closeWorker(workerGroup); bossGroup = closeWorker(bossGroup); } serverContext.stop(); LOGGER.info("server stopped"); } @Override public void channel(SocketChannel channel) { LOGGER.debug("new channel: {}", sourceKey(channel)); channel.pipeline().addLast("redisEncoder", new RedisEncoder()); channel.pipeline().addLast("linDelimiter", new RedisDecoder(MAX_FRAME_SIZE)); channel.pipeline().addLast(new RespConnectionHandler(this)); } @Override public void connected(ChannelHandlerContext ctx) { String sourceKey = sourceKey(ctx.channel()); LOGGER.debug("client connected: {}", sourceKey); getSession(ctx, sourceKey); } @Override public void disconnected(ChannelHandlerContext ctx) { String sourceKey = sourceKey(ctx.channel()); LOGGER.debug("client disconnected: {}", sourceKey); serverContext.removeSession(sourceKey); } @Override public void receive(ChannelHandlerContext ctx, RedisToken message) { String sourceKey = sourceKey(ctx.channel()); LOGGER.debug("message received: {}", sourceKey); parseMessage(message, getSession(ctx, sourceKey)) .ifPresent(serverContext::processCommand); } //......}RespServer实现了Resp接口,其start办法创立bossGroup、workerGroup,设置RespInitializerHandler为childHandler,而后执行bootstrap.bind(serverContext.getHost(), serverContext.getPort())及serverContext.start();channel设置了redisEncoder、linDelimiter、RespConnectionHandler;receive办法执行parseMessage(message, getSession(ctx, sourceKey)).ifPresent(serverContext::processCommand)RespInitializerHandlerresp-server-0.16.0/src/main/java/com/github/tonivade/resp/RespInitializerHandler.java ...

August 14, 2020 · 5 min · jiezi

关于redis:聊聊RespServer

序本文次要钻研一下RespServer Respresp-server-0.16.0/src/main/java/com/github/tonivade/resp/Resp.java interface Resp { void channel(SocketChannel channel); void connected(ChannelHandlerContext ctx); void disconnected(ChannelHandlerContext ctx); void receive(ChannelHandlerContext ctx, RedisToken message);}Resp接口定义了channel、connected、disconnected、receive办法RespServerresp-server-0.16.0/src/main/java/com/github/tonivade/resp/RespServer.java public class RespServer implements Resp { private static final Logger LOGGER = LoggerFactory.getLogger(RespServer.class); private static final int BUFFER_SIZE = 1024 * 1024; private static final int MAX_FRAME_SIZE = BUFFER_SIZE * 100; private static final String DEFAULT_HOST = "localhost"; private static final int DEFAULT_PORT = 12345; private EventLoopGroup bossGroup; private EventLoopGroup workerGroup; private ChannelFuture future; private final RespServerContext serverContext; public RespServer(RespServerContext serverContext) { this.serverContext = requireNonNull(serverContext); } public static Builder builder() { return new Builder(); } public void start() { bossGroup = new NioEventLoopGroup(); workerGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() * 2); ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new RespInitializerHandler(this)) .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) .option(ChannelOption.SO_RCVBUF, BUFFER_SIZE) .option(ChannelOption.SO_SNDBUF, BUFFER_SIZE) .childOption(ChannelOption.SO_KEEPALIVE, true) .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); future = bootstrap.bind(serverContext.getHost(), serverContext.getPort()); // Bind and start to accept incoming connections. future.syncUninterruptibly(); serverContext.start(); LOGGER.info("server started: {}:{}", serverContext.getHost(), serverContext.getPort()); } public void stop() { try { if (future != null) { closeFuture(future.channel().close()); } future = null; } finally { workerGroup = closeWorker(workerGroup); bossGroup = closeWorker(bossGroup); } serverContext.stop(); LOGGER.info("server stopped"); } @Override public void channel(SocketChannel channel) { LOGGER.debug("new channel: {}", sourceKey(channel)); channel.pipeline().addLast("redisEncoder", new RedisEncoder()); channel.pipeline().addLast("linDelimiter", new RedisDecoder(MAX_FRAME_SIZE)); channel.pipeline().addLast(new RespConnectionHandler(this)); } @Override public void connected(ChannelHandlerContext ctx) { String sourceKey = sourceKey(ctx.channel()); LOGGER.debug("client connected: {}", sourceKey); getSession(ctx, sourceKey); } @Override public void disconnected(ChannelHandlerContext ctx) { String sourceKey = sourceKey(ctx.channel()); LOGGER.debug("client disconnected: {}", sourceKey); serverContext.removeSession(sourceKey); } @Override public void receive(ChannelHandlerContext ctx, RedisToken message) { String sourceKey = sourceKey(ctx.channel()); LOGGER.debug("message received: {}", sourceKey); parseMessage(message, getSession(ctx, sourceKey)) .ifPresent(serverContext::processCommand); } //......}RespServer实现了Resp接口,其start办法创立bossGroup、workerGroup,设置RespInitializerHandler为childHandler,而后执行bootstrap.bind(serverContext.getHost(), serverContext.getPort())及serverContext.start();channel设置了redisEncoder、linDelimiter、RespConnectionHandler;receive办法执行parseMessage(message, getSession(ctx, sourceKey)).ifPresent(serverContext::processCommand)RespInitializerHandlerresp-server-0.16.0/src/main/java/com/github/tonivade/resp/RespInitializerHandler.java ...

August 14, 2020 · 5 min · jiezi

关于redis:Redis-开篇

起源 java技术栈 【Redis 开篇】Redis 开篇1、概述Redis:REmote DIctionary Server(近程字典服务器)是齐全开源收费的,用C语言编写的,恪守BSD协定,是一个高性能的(key-value)分布式内存数据库,基于内存运行并反对长久化的NoSQL数据库,是以后最热门的NoSql数据库之一,也被人们称为数据结构服务器。 2、特点redis与其余key-value缓存产品有一下三个特点 Redis反对数据的长久化,能够将内存中的数据放弃在磁盘中,重启的时候能够再次加载进行应用Redis不仅仅反对简略的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储Redis反对数据的备份,即master-slave模式的数据备份3、能做什么内存存储和长久化:redis反对异步将内存中的数据写到硬盘上,同时不影响持续服务取最新N个数据的操作,如:能够将最新的10条评论的ID放在Redis的List汇合外面模仿相似于HttpSession这种须要设定过期工夫的性能公布、订阅音讯零碎定时器、计数器4、装置和部署Redis 装置和部署 5、Redis数据类型String(字符串)string是redis最根本的类型,你能够了解成与Memcached截然不同的类型,一个key对应一个value。string类型是二进制平安的。意思是redis的string能够蕴含任何数据。比方jpg图片或者序列化的对象 。string类型是Redis最根本的数据类型,一个redis中字符串value最多能够是512MHash(哈希)Redis hash 是一个键值对汇合。Redis hash是一个string类型的field和value的映射表,hash特地适宜用于存储对象。相似Java外面的Map<String,Object>List(列表)Redis 列表是简略的字符串列表,依照插入程序排序。你能够增加一个元素导列表的头部(右边)或者尾部(左边)。它的底层理论是个链表Set(汇合)Redis的Set是string类型的无序汇合。它是通过HashTable实现实现的zset(sorted set:有序汇合)Redis zset 和 set 一样也是string类型元素的汇合,且不容许反复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为汇合中的成员进行从小到大的排序。zset的成员是惟一的,但分数(score)却能够反复。6、文件阐明redis-benchmark:性能测试工具,能够在本人本子运行,看看本人本子性能如何redis-check-aof:修复有问题的AOF文件redis-check-dump:修复有问题的dump.rdb文件redis-cli:客户端,操作入口redis-sentinel:redis集群应用redis-server:Redis服务器启动命令7、启动后杂项常识单过程:单过程模型来解决客户端的申请。对读写等事件的响应是通过对epoll函数的包装来做到的。Redis的理论处理速度齐全依附主过程的执行效率,epoll是Linux内核为解决大批量文件描述符而作了改良的epoll,是Linux下多路复用IO接口select/poll的加强版本,它能显著进步程序在大量并发连贯中只有大量沉闷的状况下的零碎CPU利用率。默认16个数据库,相似数组下表从零开始,初始默认应用零号库设置数据库的数量,默认数据库为0,能够应用SELECT <dbid>命令在连贯上指定数据库id databases 16select命令切换数据库dbsize查看以后数据库的key的数量flushdb:清空以后库Flushall;通杀全副库Redis索引都是从零开始对立明码治理,16个库都是同样明码,要么都OK要么一个也连贯不上

August 14, 2020 · 1 min · jiezi

关于redis:Redis-开篇

起源 java技术栈 【Redis 开篇】Redis 开篇1、概述Redis:REmote DIctionary Server(近程字典服务器)是齐全开源收费的,用C语言编写的,恪守BSD协定,是一个高性能的(key-value)分布式内存数据库,基于内存运行并反对长久化的NoSQL数据库,是以后最热门的NoSql数据库之一,也被人们称为数据结构服务器。 2、特点redis与其余key-value缓存产品有一下三个特点 Redis反对数据的长久化,能够将内存中的数据放弃在磁盘中,重启的时候能够再次加载进行应用Redis不仅仅反对简略的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储Redis反对数据的备份,即master-slave模式的数据备份3、能做什么内存存储和长久化:redis反对异步将内存中的数据写到硬盘上,同时不影响持续服务取最新N个数据的操作,如:能够将最新的10条评论的ID放在Redis的List汇合外面模仿相似于HttpSession这种须要设定过期工夫的性能公布、订阅音讯零碎定时器、计数器4、装置和部署Redis 装置和部署 5、Redis数据类型String(字符串)string是redis最根本的类型,你能够了解成与Memcached截然不同的类型,一个key对应一个value。string类型是二进制平安的。意思是redis的string能够蕴含任何数据。比方jpg图片或者序列化的对象 。string类型是Redis最根本的数据类型,一个redis中字符串value最多能够是512MHash(哈希)Redis hash 是一个键值对汇合。Redis hash是一个string类型的field和value的映射表,hash特地适宜用于存储对象。相似Java外面的Map<String,Object>List(列表)Redis 列表是简略的字符串列表,依照插入程序排序。你能够增加一个元素导列表的头部(右边)或者尾部(左边)。它的底层理论是个链表Set(汇合)Redis的Set是string类型的无序汇合。它是通过HashTable实现实现的zset(sorted set:有序汇合)Redis zset 和 set 一样也是string类型元素的汇合,且不容许反复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为汇合中的成员进行从小到大的排序。zset的成员是惟一的,但分数(score)却能够反复。6、文件阐明redis-benchmark:性能测试工具,能够在本人本子运行,看看本人本子性能如何redis-check-aof:修复有问题的AOF文件redis-check-dump:修复有问题的dump.rdb文件redis-cli:客户端,操作入口redis-sentinel:redis集群应用redis-server:Redis服务器启动命令7、启动后杂项常识单过程:单过程模型来解决客户端的申请。对读写等事件的响应是通过对epoll函数的包装来做到的。Redis的理论处理速度齐全依附主过程的执行效率,epoll是Linux内核为解决大批量文件描述符而作了改良的epoll,是Linux下多路复用IO接口select/poll的加强版本,它能显著进步程序在大量并发连贯中只有大量沉闷的状况下的零碎CPU利用率。默认16个数据库,相似数组下表从零开始,初始默认应用零号库设置数据库的数量,默认数据库为0,能够应用SELECT <dbid>命令在连贯上指定数据库id databases 16select命令切换数据库dbsize查看以后数据库的key的数量flushdb:清空以后库Flushall;通杀全副库Redis索引都是从零开始对立明码治理,16个库都是同样明码,要么都OK要么一个也连贯不上

August 14, 2020 · 1 min · jiezi

关于redis:Redis『慢查询』分析

问题起源 顶峰盯盘期间,通过kibana查问发现不定时存在一些redis慢查问日志(客户端日志);而且目前我的项目中记录redis慢查问日志的门限默为300ms。这种不知起因且不定时的慢查问是十分危险的。 注1:redis server实例配置的slow log门限为10ms,并且存在慢查问报警。 注2:客户端与redis之间还存在Twemproxy代理(以下简称tw)。 注2:客户端为Golang服务,与tw之间是长连贯,基于连接池实现。 【案例1】客户端问题排查 2020.07.29 18:19:21左右, 呈现一小波redis慢查问日志。 分析表明: redis并没有慢查问报警;tw监控表明过后申请的ops没有显著变动;客户端同时刻所有的慢查问日志都在一台机器;tw与redis监控的cpu负载等指标没有显著异样;tw客户端连接数同时刻有突增; 联合以上景象有以下几个狐疑点: 1)tw代理导致的慢查问?那这样同时刻的慢查问应该比拟平均的散布到多台客户端机器,只存在一台机器并不是很正当; 2)redis连接池导致的,连接池通常会存在最大连接数的限度,而tw监控表明客户端连接数同时刻存在突增状况; 我的项目中redis客户应用的是 github.com/go-redis/redis ,连接池相干配置定义在redis.Options构造: type Options struct { PoolSize int //连接池大小,即最大连接数 PoolTimeout time.Duration //获取连贯超时工夫,当连接池所有连贯都被占用,最大等待时间; //默认为ReadTimeout+1秒 IdleTimeout time.Duration //连贯闲暇超时工夫,长时间闲暇的连贯会被客户端被动开释;} 可能『卡住』的中央就是获取连贯了,代码逻辑参照pool.(*ConnPool).Get: select { case p.queue <- struct{}{}: default: timer := timers.Get().(*time.Timer) timer.Reset(p.opt.PoolTimeout) select { case p.queue <- struct{}{}: if !timer.Stop() { <-timer.C } timers.Put(timer) case <-timer.C: timers.Put(timer) atomic.AddUint32(&p.stats.Timeouts, 1) return nil, false, ErrPoolTimeout } } p.queue通道大小等于poolsize,PoolTimeout即为获取连贯的最大超时工夫,超时则返回谬误。 ...

August 14, 2020 · 1 min · jiezi

关于redis:redis的5种对象与8种数据结构之字符串对象下

简介: 引言 本文是对《redis设计与实现(第二版)》中数据结构与对象相干内容的整顿与阐明。本篇文章只对对象构造,1种对象——字符串对象。以及字符串对象所对应的两种编码——raw和embstr,进行了具体介绍。 引言本文是对《redis设计与实现(第二版)》中数据结构与对象相干内容的整顿与阐明。本篇文章只对对象构造,1种对象——字符串对象。以及字符串对象所对应的两种编码——raw和embstr,进行了具体介绍。表白一些自己的想法与认识,也心愿更多敌人一起来探讨,分享交换。 作者:太阳 云掣科技-数据库团队 数据库工程师 字符串对象 字符串对象能够存储整数、浮点数、字符串,具体策略是: 当存储整数时,用到的编码是int,底层的数据结构能够用来存储long类型的整数; 当存储字符串时,如果字符串的长度小于等于32字节,那么将用编码为embstr的格局来存储;如果字符串的长度大于32字节,将用编码为raw的SDS格局来存储; 当存储浮点数时会先将浮点数转换为字符串,如果转换后的字符串长度小于32字节就用编码为embstr的格局来存储,否则用编码为raw的SDS格局来存储。下图是一个字符串对象的结构图,最左侧是对象构造,两头跟右侧合起来是raw编码的SDS数据结构(sdshdr),示例图: raw编码,简略动静字符串(simple dynamic string-SDS) redis用的并不是C语言传统的字符串,而是本人构建了简略动静字符串(simpledynamic string,SDS)。 当redis打印日志信息或输入报错信息,这些输入的字符串是不会被批改的字符串字面量(sting literal),此时用的是C语言传统的字符串来存储这些信息的。当redis须要存储的是能够被批改的字符串时,就会应用SDS构造。 除了用来保留数据库中的字符串值之外,SDS还被用作缓冲区(buffer):AOF模块中的AOF缓冲区,以及客户端状态中的输出缓冲区,都是由SDS实现的。 SDS的构造 SDS构造示意图如下所示: sdshdr是该数据结构的名称即SDS,其中: buf属性,是一个字节数组,用来保留字符串,前面箭头对应的就是理论保留的字符串内容,最初以’0’空字符串结尾; len属性,记录的是buf数组中理论已应用的字节数量,等于SDS所保留字符串的长度; free属性,记录的是buf数组中未应用字节的数量。 SDS长处 一、能够用O(1)的复杂度获取到字符串长度 SDS的len属性记录了字符串的长度,而传统C字符串要想晓得长度须要遍历整个字符串。相比于传统C字符串,redis获取字符串长度所需的复杂度从O(N)升高到了O(1)。 即便对十分长的字符串重复执行STRLEN命令(获取字符串长度),也不会造成过多的性能耗费。 二、杜绝缓冲区溢出 在传统的C字符串中,如果要批改字符串的内容,但批改后字符串的长度超过原先的长度就会产生溢出景象。详见下图: 在SDS中,当须要对buf字节数组中存储的内容进行批改(削减或删除)时,API会先通过free和len属性查看SDS的空间是否足够,如果不够的话,SDS会主动扩大空间再对内容进行批改。对于主动扩大空间的策略见下方“空间预调配”的内容。 三、缩小批改字符串长度时所需的内存重调配次数 对于传统C字符串: 如果执行的是增长字符串的操作,如拼接操作(append),那么在执行命令之前,程序须要先通过内存重调配来扩大底层数据的空间大小——否则会产生缓冲区溢出。 如果执行的是缩短字符串的操作,如截断操作(trim),那么在执行这个操作之后,程序须要通过内存重调配来开释字符串不再应用的空间——否则会产生内存透露。 对于redis中的SDS构造: 内存重调配设计简单的算法,是一个比拟耗时的操作,redis作为速度要求严苛、数据会被频繁执行的数据库,如果每次批改字符串都须要进行一次内存重调配,会重大影响性能。 应用SDS,buf数组里能够蕴含未应用的字节,这些字节的数量由free属性记录,能够缩小批改字符串长度时所需的内存重调配次数。 空间预调配和惰性空间开释 通过SDS中free属性定义的未应用空间,SDS能够实现空间预调配和惰性空间开释两种优化策略: 1、空间预调配策略——能够升高字符串增长操作引起的内存重调配 当须要批改SDS的内容,且须要进行空间扩大的时候,程序不仅会为SDS调配批改所需的必须空间,还会为SDS调配额定的未应用空间。 其中,额定调配的未应用空间数量由以下公式决定: 如果对SDS进行批改之后,SDS的长度(即len属性的值)将小于1MB,那么程序将调配和len属性同样大小的未应用空间,这时SDS len属性的值将和free属性的值雷同。 如果对SDS进行批改后,SDS的长度将大于等于1MB,那么程序会调配1MB的未应用空间。 阐明 如果对一个字符串的开端继续追加内容,当字符串整体大小大于1MB时,即便只追加一字节的字符,程序也会额定调配1MB的空间,当再次追加一字节的字符时,程序不会再额定调配1MB的空间,而是应用已有的闲暇空间。 即在扩大空间之前,会先查看未应用的空间是否足够,如果足够,是不会额定再扩大的。 通过空间预调配策略,SDS将间断增长N次字符串所需的内存重调配次数从必然N次升高为最多N次。 2、惰性空间开释策略——能够升高字符串缩短操作引起的内存重调配 当SDS中的字符串长度被缩短时,程序并不会立刻应用内存重调配来回收缩短后多进去的字节空间,而是应用free属性将这些字节的数量记录起来,以备未来应用。 当然,redis提供了相应的命令来真正开释这些未应用空间,防止不必要的内存节约。 四、二进制平安 C字符串中的字符必须合乎某种编码(比方ASCII),并且除了字符串的开端之外,字符串外面不能蕴含空字符,如果字符串除开端外还有其它空字符,那么最先被程序读入的空字符将被误认为是字符串结尾,这些限度使得C字符串只能保留文本数据,而不能保留图片、音频、视频、压缩文件这样的二进制数据。 为了确保redis能够实用于各种不同的应用场景,SDS的API都是二进制平安的(binary-safe),所有SDS API都会以解决二进制的形式来解决SDS寄存buf数组里的数据,程序不会对其中的数据做任何限度、过滤或者假如,数据在写入时是什么样的,它被读取时就是什么样。 这也是RDS的buf属性被称为字节数组的起因——redis不是用这个数组来保留字符,而是用它来保留一系列二进制数据。 五、兼容局部C字符串函数 SDS遵循空字符串结尾这一常规,益处是能够间接重用C字符串函数库里的函数,从而防止了不必要的代码反复。 embstr编码 ...

August 14, 2020 · 1 min · jiezi

关于redis:redis的5种对象与8种数据结构之字符串对象上

简介: 引言 本文是对《redis设计与实现(第二版)》中数据结构与对象相干内容的整顿与阐明。本篇文章只对对象构造,1种对象——字符串对象。以及字符串对象所对应的两种编码——raw和embstr,进行了具体介绍。 引言本文是对《redis设计与实现(第二版)》中数据结构与对象相干内容的整顿与阐明。本篇文章只对对象构造,1种对象——字符串对象。以及字符串对象所对应的两种编码——raw和embstr,进行了具体介绍。表白一些自己的想法与认识,也心愿更多敌人一起来探讨,分享交换。作者:太阳 云掣科技-数据库团队 数据库工程师 对象 redis应用对象来示意数据库中的键和值,每次当咱们在redis的数据库中新创建一个键值对时,咱们至多会创立两个对象,一个对象用作键值对的键(键对象),另一个对象用作键值对的值(值对象)。 redis的每种对象都由对象构造(redisObject)与对应编码的数据结构组合而成,redis反对5种对象类型,别离是字符串(string)、列表(list)、哈希(hash)、汇合(set)、有序汇合(zset),而每种对象类型至多对应两种编码方式,不同的编码方式所对应的底层数据结构是不同的。 每个对象会用到的编码以及对应的数据结构详见下表: 每种对象对应两至三种编码,除skiplist编码须要用到两种数据结构(字典+跳跃表)外,其余编码均用到一种底层的数据结构。 同一个对象类型,在不同的场景下用到的编码(数据结构)不同,redis反对8种编码以及8种底层的数据结构。这种形式更加灵便,能够帮忙redis取得更高的性能以及尽量占用更少的内存。比方如果字符串对象中要存储的字符串内容所占字节较小,会用embstr编码的格局,如果要存储的内容所占字节较大,会用raw编码的格局,具体细节后文会具体阐明。 总结阐明 上文说过,redis中的键和值都是由对象组成的,而对象是由对象构造和数据结构独特组成的。redis中的键,都是用字符串来存储的,即对于redis数据库中的键值对来说,键总是一个字符串对象,而值能够是字符串对象、列表对象、哈希对象、汇合对象或者有序汇合对象中的其中一种。 键、值的整体大抵构造能够如下图所示: 对象构造 对象构造(redisObject)共有5个属性,别离是type属性、encoding属性、ptr属性、refcount属性、lru属性。 其中type属性、encoding属性、ptr属性和保留数据无关: type属性:示意该对象的类型是什么; encoding属性:示意这个对象应用的底层数据结构是什么; ptr属性:是一个指向底层数据结构的指针; refcount属性是一个援用计数属性,能够用于内存回收和对象共享; lru属性,记录了对象最初一次被命令程序拜访的工夫,能够计算出某个键的空转时长。 对象构造的逻辑图如下所示: 内存回收--refcount属性 在对象构造中,有refcount这个属性,该属性用于记录对象的援用计数信息,redis利用援用计数(referencecounting)技术实现内存回收机制,通过这一机制,程序能够通过跟踪对象的援用计数信息,在适当的时候主动开释对象并进行内存回收。 具体策略: 在创立一个新对象时,援用计数的值会被初始化为1; 当对象被一个新程序应用时,它的援用计数值会被+1; 当对象不再被一个程序应用时,它的援用计数值会被-1; 当对象的援用计数值变为0时,对象所占用的内存会被开释。 对象共享--refcount属性 Redis会在初始化服务器时,服务器会创立一万个字符串对象,这些对象蕴含了从0到9999的所有整数值,当服务器、新创建的键须要用到值为0到9999的字符串对象时,服务器就会应用这些共享对象,而不是新创建对象。 对象构造中,refcount是援用指针属性,如果有N个键共享一个值,refcount对应的值就为N。创立共享字符串对象的数量能够通过redis.h/redis_shared_intengers常量来批改。object refcount命令能够查看某个键对应的值被援用了多少次。 让多个键共享一个值,须要执行以下两个步骤: 将键的值指针,指向被共享的值对象; 被共享的值对象的援用计数器加一,即refcount属性的值加一。 援用数为2的共享对象结构图如下图所示: 总结阐明 当服务器思考将一个键的值援用共享对象时,键的值作为指标对象,程序须要先查看共享对象和指标对象的类型是否完全相同,只有在完全相同的状况下,共享对象才会被援用。而一个共享对象保留的值越简单,验证共享对象与指标对象所需的复杂度就会越高,耗费的CPU工夫也会越多。 所以共享对象的长处是被其它键援用时,能够节俭内存空间,毛病是被援用时须要进行判断,这个过程须要耗费CPU,如果共享对象简略,耗费很小的CPU并节俭内存空间是值得的。 但如果对象共享很简单,进行判断就须要耗费大量CPU,耗费大量CPU去节俭内存空间是不值得的,因为redis自身的内存空间还是很大的。 知识点 redis反对5种对象,包含字符串对象、列表对象、哈希对象、汇合对象以及有序汇合对象。而字符串对象是redis中的一个根底对象,其它对象均能够在底层的数据结构外部嵌套字符串对象。 对于对象共享: 1、只有字符串对象能力被创立为共享对象,被其它字符串键应用; 2、用字符串对象创立的共享对象,不单单只有字符串键能够应用,那些在数据结构中嵌套了字符串对象的对象(linkedlist编码的列表对象、hashtable编码的哈希对象、hashtable编码的汇合对象,以及skiplist编码的有序汇合对象)都能够应用这些字符串共享对象。 Q&A Q 为什么redis不共享列表对象、哈希对象、汇合对象、有序汇合对象,只共享字符串对象? A 列表对象、哈希对象、汇合对象、有序汇合对象,自身能够蕴含字符串对象,复杂度较高。 如果共享对象是保留字符串对象,那么验证操作的复杂度为O(1); 如果共享对象是保留字符串值的字符串对象,那么验证操作的复杂度为O(N); 如果共享对象是蕴含多个值的对象,其中值自身又是字符串对象,即其它对象中嵌套了字符串对象,比方列表对象、哈希对象,那么验证操作的复杂度将会是O(N的平方); 如果对复杂度较高的对象创立共享对象,须要耗费很大的CPU,用这种耗费去换取内存空间,是不适合的。 碎碎念 1、当初咱们晓得,redis为了防止额定的内存耗费,在初始化的时候,为0~9999这些整数创立了共享对象。那除了0~9999,redis外部是否还设置了其它类型的共享对象?但具体有哪些值被作为了共享对象还不是特地分明,不过应该都是一些简略的值。 2、另外,0~9999整数是程序初始化时主动创立为共享对象的,咱们是否能够手动创立共享对象?比方咱们认为有很多键对应的值都是雷同的,是否能够手动创立共享对象以节俭内存?如果能够,又有哪些限度要求? 创立的共享对象,当其它键去援用共享对象时,须要进行判断,两者的类型完全相同才能够被利用,共享对象保留的内容越简单,进行判断时须要耗费的CPU就越大。 redis初始化创立的0~9999的共享对象,构造很简略,进行判断时耗费的CPU很小。然而如果redis容许咱们手动为某些值创立共享对象,它的构造只有略微简单一些,就须要耗费很大的CPU,这无疑是不适合的,所以redis为了防止这种不必要的影响,应该不反对手动创立共享对象。 ...

August 14, 2020 · 1 min · jiezi

关于redis:基于Redis实现分布式锁

前言最近在工作中应用到了分布式锁,所以在本文中记录一下如何在Java中应用Redis实现分布式锁 四个个性在应用分布式锁的时候,咱们须要满足以下四个条件: 互斥性 在同一时间只能有一个客户端持有锁。容错性 客户端能够实现加锁和解锁。可靠性 即便在客户端解体后,无奈被动开释锁的状况下, 也能够保障后续客户端持有该锁。一致性 加锁和解锁需是同一客户端,必须防止以后客户端的锁被其余客户端解锁。具体实现加锁间接上代码,加锁其实并不简单。 private ThreadLocal<String> threadLocal = new ThreadLocal<>();private static final String LOCK_SUCCESS = "OK";private static final String LOCK_KEY = "redisLockTest"; public void test() { Jedis jedis = getJedis(); String uuid = UUID.randomUUID().toString(); threadLocal.set(uuid); boolean result = tryLock(jedis,LOCK_KEY,uuid,10); if (result) { //......具体业务实现 } } /** * 获取分布式锁 * * @param jedis Redis客户端 * @param lockKey 锁 * @param requestId 申请标识 * @param expireTime 过期工夫 * @return 是否获取胜利 * */ public static boolean tryLock(Jedis jedis, String lockKey, String requestId, int expireTime) { String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime); if (LOCK_SUCCESS.equals(result)) { return true; } return false; }具体外围代码其实就是这一行: ...

August 13, 2020 · 2 min · jiezi

关于redis:Redis实战

Redis实战 下载地址: https://pan.baidu.com/s/1hExs6sOP9jBDj5oP9S4jFA 扫码上面二维码关注公众号回复100020 获取分享码 本书目录构造如下: 第一局部 入门 1.1 Redis简介 1.1.1 Redis与其余数据库和软件的比照 1.1.2 附加个性 1.1.3 应用Redis的理由 1.2 Redis数据结构简介 1.2.1 Redis中的字符串 1.2.2 Redis中的列表 1.2.3 Redis的汇合 1.2.4 Redis的散列 1.2.5 Redis的有序汇合 1.3 你好 1.3.1 对文章进行投票 1.3.2 公布并获取文章 1.3.3 对文章进行分组 1.4 寻求帮忙 1.5 小结 第2章 应用Redis构建Web利用 2.1 登录和cookie缓存 2.2 应用Redis实现购物车 2.3 网页缓存 2.4 数据行缓存 2.5 网页剖析 2.6 小结 第二局部 外围概念 第3章 Redis命令 3.1 字符串 3.2 列表 3.3 汇合 3.4 散列 ...

August 11, 2020 · 2 min · jiezi

关于redis:闲扯Redis十Redis-跳跃表的结构实现

一、前言Redis 提供了5种数据类型:String(字符串)、Hash(哈希)、List(列表)、Set(汇合)、Zset(有序汇合),了解每种数据类型的特点对于redis的开发和运维十分重要。<p align="right">原文解析</p> 备注: 依照剖析程序,本节应该说道有序汇合对象了,然而思考到有序汇合对象的底层实现中应用到了跳跃表构造,防止在剖析有序汇合时造成突兀,所以本节先来看看 redis 中跳跃表构造的具体实现。 二、构造解析 Redis 的跳跃表由 redis.h/zskiplistNode 和 redis.h/zskiplist 两个构造定义,其中 zskiplistNode 构造用于示意跳跃表节点,而 zskiplist 构造则用于保留跳跃表节点的相干信息,比方节点的数量,以及指向表头节点和表尾节点的指针等。 图 5-1 展现了一个跳跃示意例, 位于图片最右边的是 zskiplist 构造, 该构造蕴含以下属性: header :指向跳跃表的表头节点。tail :指向跳跃表的表尾节点。level :记录目前跳跃表内,层数最大的那个节点的层数(表头节点的层数不计算在内)。length :记录跳跃表的长度,也即是,跳跃表目前蕴含节点的数量(表头节点不计算在内)。 zskiplist 构造左边的是四个 zskiplistNode 构造, 该构造蕴含以下属性: 层(level):节点中用 L1 、 L2 、 L3 等字样标记节点的各个层, L1 代表第一层, L2 代表第二层,以此类推。每个层都带有两个属性:后退指针和跨度。后退指针用于拜访位于表尾方向的其余节点,而跨度则记录了后退指针所指向节点和以后节点的间隔。在下面的图片中,连线上带有数字的箭头就代表>后退指针,而那个数字就是跨度。当程序从表头向表尾进行遍历时,拜访会沿着层的后退指针进行。后退指针(backward):节点中用 BW 字样标记节点的后退指针,它指向位于以后节点的前一个节点。后退指针在程序从表尾向表头遍历时应用。 分值(score):各个节点中的 1.0 、 2.0 和 3.0 是节点所保留的分值。在跳跃表中,节点按各自所保留的分值从小到大排列。 成员对象(obj):各个节点中的 o1 、 o2 和 o3 是节点所保留的成员对象。留神表头节点和其余节点的结构是一样的: 表头节点也有后退指针、分值和成员对象, 不过表头节点的这些属性都不会被用到, 所以图中省略了这些局部, 只显示了表头节点的各个层。 ...

August 11, 2020 · 2 min · jiezi

关于redis:闲扯Redis九Redis五种数据类型之Set型

一、前言Redis 提供了5种数据类型:String(字符串)、Hash(哈希)、List(列表)、Set(汇合)、Zset(有序汇合),了解每种数据类型的特点对于redis的开发和运维十分重要。<p align="right">原文解析</p> Redis 中的 Set 是咱们常常应用到的一种数据类型,依据应用形式的不同,能够利用到很多场景中。 二、底层实现 汇合对象的编码能够是 intset 或者 hashtable 。 intset 编码的汇合对象应用整数汇合作为底层实现, 汇合对象蕴含的所有元素都被保留在整数汇合外面。 举个例子, 以下代码将创立一个如图 8-12 所示的 intset 编码汇合对象: redis> SADD numbers 1 3 5(integer) 3结构图 8-12: 另一方面, hashtable 编码的汇合对象应用字典作为底层实现, 字典的每个键都是一个字符串对象, 每个字符串对象蕴含了一个汇合元素, 而字典的值则全副被设置为 NULL 。 举个例子, 以下代码将创立一个如图 8-13 所示的 hashtable 编码汇合对象: redis> SADD fruits "apple" "banana" "cherry"(integer) 3结构图 8-13: 三、编码转换 当汇合对象能够同时满足以下两个条件时, 对象应用 intset 编码: 1.汇合对象保留的所有元素都是整数值;2.汇合对象保留的元素数量不超过 512 个; 不能满足这两个条件的汇合对象须要应用 hashtable 编码。 留神 : 第二个条件的上限值是能够批改的, 具体请看配置文件中对于 set-max-intset-entries 选项的阐明。对于应用 intset 编码的汇合对象来说, 当应用 intset 编码所需的两个条件的任意一个不能被满足时, 对象的编码转换操作就会被执行: 本来保留在整数汇合中的所有元素都会被转移并保留到字典外面, 并且对象的编码也会从 intset 变为 hashtable。 ...

August 11, 2020 · 2 min · jiezi

关于redis:闲扯Redis八Redis字典的哈希表执行Rehash过程分析

一、前言随着操作的一直执行, 哈希表保留的键值对会逐步地增多或者缩小, 为了让哈希表的负载因子(load factor)维持在一个正当的范畴之内, 当哈希表保留的键值对数量太多或者太少时, 程序须要对哈希表的大小进行相应的扩大或者膨胀。<p align="right">原文解析</p> 二、实现剖析1.rehash过程剖析扩大和膨胀哈希表的工作能够通过执行 rehash (从新散列)操作来实现。Redis 对字典的哈希表执行 rehash 的步骤: 1.为字典的 ht[1] 哈希表调配空间, 这个哈希表的空间大小取决于要执行的操作, 以及 ht[0] 以后蕴含的键值对数量 (也即是ht[0].used 属性的值): 如果执行的是扩大操作, 那么 ht[1] 的大小为第一个大于等于 ht[0].used * 2 的 2^n (2 的 n 次方幂);如果执行的是膨胀操作, 那么 ht[1] 的大小为第一个大于等于 ht[0].used 的 2^n 。2.将保留在 ht[0] 中的所有键值对 rehash 到 ht[1] 下面: rehash 指的是从新计算键的哈希值和索引值, 而后将键值对搁置到 ht[1] 哈希表的指定地位上。 3.当 ht[0] 蕴含的所有键值对都迁徙到了 ht[1] 之后 (ht[0] 变为空表), 开释 ht[0] , 将 ht[1] 设置为 ht[0] , 并在 ht[1] 新创建一个空白哈希表, 为下一次 rehash 做筹备。 ...

August 11, 2020 · 2 min · jiezi

关于redis:闲扯Redis七Redis字典结构的底层实现

一、前言上节《闲扯Redis六》Redis五种数据类型之Hash型 中说到 Hash(哈希对象)的底层实现有:1、ziplist 编码的哈希对象应用压缩列表作为底层实现2、hashtable 编码的哈希对象应用字典作为底层实现 <p align="right">原文解析</p> 那么第二种形式中的字典到底是怎么的一种构造呢? 字典, 又称符号表(symbol table)、关联数组(associative array)或者映射(map), 是一种用于保留键值对(key-value pair)的形象数据结构。在字典中, 一个键(key)能够和一个值(value)进行关联(或者说将键映射为值), 这些关联的键和值就被称为键值对。 字典中的每个键都是举世无双的, 程序能够在字典中依据键查找与之关联的值, 或者通过键来更新值, 又或者依据键来删除整个键值对, 等等。 二、实现剖析Redis 的字典采纳哈希表作为底层实现, 一个哈希表外面能够有多个哈希表节点, 而每个哈希表节点就保留了字典中的一个键值对。所以咱们顺次来剖析一下哈希表、哈希表节点、以及字典的构造。1.哈希表构造哈希表构造定义 (dict.h/dictht): typedef struct dictht { // 哈希表数组 dictEntry **table; // 哈希表大小 unsigned long size; // 哈希表大小掩码,用于计算索引值 // 总是等于 size - 1 unsigned long sizemask; // 该哈希表已有节点的数量 unsigned long used;} dictht;形容: table 属性是一个数组, 数组中的每个元素都是一个指向 dict.h/dictEntry 构造的指针, 每个 dictEntry 构造保留着一个键值对。size 属性记录了哈希表的大小, 也即是 table 数组的大小, 而 used 属性则记录了哈希表目前已有节点(键值对)的数量。sizemask 属性的值总是等于 size - 1 , 这个属性和哈希值一起决定一个键应该被放到 table 数组的哪个索引下面。构造图解:一个空的哈希表 ...

August 11, 2020 · 2 min · jiezi

关于redis:闲扯Redis六Redis五种数据类型之Hash型

一、前言Redis 提供了5种数据类型:String(字符串)、Hash(哈希)、List(列表)、Set(汇合)、Zset(有序汇合),了解每种数据类型的特点对于redis的开发和运维十分重要。<p align="right">原文解析</p> Redis 中的 hash 是咱们常常应用到的一种数据类型,依据应用形式的不同,能够利用到很多场景中。 二、实现剖析 由上述结构图可知,Hash类型有以下两种实现形式: 1、ziplist 编码的哈希对象应用压缩列表作为底层实现2、hashtable 编码的哈希对象应用字典作为底层实现1.ziplist 编码作为底层实现ziplist 编码的哈希对象应用压缩列表作为底层实现, 每当有新的键值对要退出到哈希对象时, 程序会先将保留了键的压缩列表节点推入到压缩列表表尾, 而后再将保留了值的压缩列表节点推入到压缩列表表尾, 因而: 保留了同一键值对的两个节点总是紧挨在一起, 保留键的节点在前, 保留值的节点在后;先增加到哈希对象中的键值对会被放在压缩列表的表头方向,而起初增加到哈希对象中的键值对会被放在压缩列表的表尾方向。例如, 咱们执行以下 HSET 命令, 那么服务器将创立一个列表对象作为 profile 键的值: redis> HSET profile name "Tom"(integer) 1redis> HSET profile age 25(integer) 1redis> HSET profile career "Programmer"(integer) 1profile 键的值对象应用的是 ziplist 编码, 其中对象所应用的压缩列表构造如下图所示。 2.hashtable 编码作为底层实现hashtable 编码的哈希对象应用字典作为底层实现, 哈希对象中的每个键值对都应用一个字典键值对来保留: 字典的每个键都是一个字符串对象, 对象中保留了键值对的键;字典的每个值都是一个字符串对象, 对象中保留了键值对的值。例如, 如果后面 profile 键创立的不是 ziplist 编码的哈希对象, 而是 hashtable 编码的哈希对象, 那么这个哈希对象构造如下图所示。 三、命令实现因为哈希键的值为哈希对象, 所以用于哈希键的所有命令都是针对哈希对象来构建的, 下表列出了其中一部分哈希键命令, 以及这些命令在不同编码的哈希对象下的实现办法。 ...

August 11, 2020 · 2 min · jiezi

关于redis:闲扯Redis五List数据类型底层之quicklist

一、前言Redis 提供了5种数据类型:String(字符串)、Hash(哈希)、List(列表)、Set(汇合)、Zset(有序汇合),了解每种数据类型的特点对于redis的开发和运维十分重要。<p align="right">原文解析</p> Redis 中的 list 是咱们常常应用到的一种数据类型,依据应用形式的不同,能够利用到很多场景中。 二、底层解析1、上节回顾 上节《闲扯Redis四》List数据类型底层编码转换 说道,在 3.0 版本的 Redis 中,List 类型有两种实现形式: 1、应用压缩列表(ziplist)实现的列表对象。2、应用双端链表(linkedlist)实现的列表对象。 在 3.2 版本后新增了 quicklist 数据结构实现了 list,当初就来剖析下 quicklist 的构造 2、官网形容 先来看看 Redis 官网对 quicklist 的形容: A doubly linked list of ziplists A generic doubly linked quicklist implementation 可见 quicklist 是一个双向链表,并且是一个 ziplist 的双向链表,也就是说 quicklist 的每个节点都是一个 ziplist。而通过后面的文章咱们能够晓得,ziplist 自身也是一个能维持数据项先后顺序的列表,而且数据项保留在一个间断的内存块中。那是不是意味着 quicklist 联合了压缩列表和双端链表的特点呢! 3、构造剖析quicklist 构造定义/* * quicklist */typedef struct quicklist { //头结点 quicklistNode *head; //尾节点 quicklistNode *tail; //所有ziplist中entry数量 unsigned long count; //quicklistNodes节点数量 unsigned int len; //ziplist中entry能保留的数量,由list-max-ziplist-size配置项管制 int fill : 16; //压缩深度,由list-compress-depth配置项管制 unsigned int compress : 16; } quicklist;quicklist 构造属性正文正文: ...

August 11, 2020 · 2 min · jiezi

关于redis:闲扯Redis四List数据类型底层编码转换

一、前言Redis 提供了5种数据类型:String(字符串)、Hash(哈希)、List(列表)、Set(汇合)、Zset(有序汇合),了解每种数据类型的特点对于redis的开发和运维十分重要。<p align="right">原文解析</p> Redis 中的 list 是咱们常常应用到的一种数据类型,依据应用形式的不同,能够利用到很多场景中。 二、编码转换 上节《闲扯Redis三》Redis五种数据类型之List型 中说道,List类型有两种实现形式: 1、应用压缩列表(ziplist)实现的列表对象2、应用双端链表(linkedlist)实现的列表对象 并且,留下了一个疑难?Redis列表什么时候会应用 ziplist 编码,什么时候又会应用 linkedlist 编码呢? 参见了《Redis设计与实现》,得出了一个论断: ziplist 与 linkedlist 之间存在着一种编码转换机制,当列表对象能够同时满足下列两个条件时,列表对象采纳ziplist编码,否则采纳linkedlist编码 (1)列表对象保留的所有字符串元素的长度都小于64字节;(2)列表元素保留的元素数量小于512个; 留神 :以上两个条件的上限值能够在配置文件中批改 list-max-ziplist-value 选项和 list-max-ziplist-entries 选项,另外对于应用 ziplist 编码的列表对象,当以上两个条件中任何一个不能满足时,对象的编码转换操作就会执行,本来保留在压缩列表外面的所有列表元素都会被转移并保留到双端链表外面,对象的编码也从 ziplist 变为 linkedlist 。 三、操作验证 书中是这样说的,然而还是要本人操作验证一下的,go! 咦,什么鬼,不是说 ziplist 和 linkedlist ,还有编码转换吗,咋一个都不是,这个 quicklist 是什么构造?看到这大家可能有一点懵逼,细品之后甚至想要入手:七哥,你TM是不是在忽悠我? 嗯,这个不要急,听我持续哔哔( 伪装沉着 ) 先来看一下操作的redis的版本: ./redis-server --version 版本显示: 再看一下,黄建宏老师《Redis设计与实现》第二版中对应的redis版本:3.0,查阅材料发现 redis 在 3.2 版本的时候,思考到redis的空间存储效率和工夫效率,引入了quicklist(疾速列表)作为 list 的底层实现,原来是这样啊! 这就能说的通了,哈哈哈,下节咱们就来看看这个 quicklist 到底是个什么啥,后面针对 3.0 版本的剖析,看都看了,还能怎么办呢,权当做个理解了! 四、要点总结(1)(Redis 3.2 版本前)列表对象底层实现的形式,压缩列表(ziplist)与双端链表(linkedlist)存在转换(2)(Redis 3.2 版本前)同时满足两个条件:列表对象保留的所有字符串元素的长度都小于64字节;列表元素保留的元素数量小于512个;列表对象采纳 ziplist 编码,否则采纳 linkedlist 编码 ...

August 11, 2020 · 1 min · jiezi

关于redis:闲扯Redis三Redis五种数据类型之List型

一、前言Redis 提供了5种数据类型:String(字符串)、Hash(哈希)、List(列表)、Set(汇合)、Zset(有序汇合),了解每种数据类型的特点对于redis的开发和运维十分重要。<p align="right">原文解析</p> Redis 中的 list 是咱们常常应用到的一种数据类型,依据应用形式的不同,能够利用到很多场景中。 二、操作命令 List数据类型在 Redis 中的相干命令:: 命令形容用法LPUSH1.将一个或多个值value插入到列表key的表头2.如果有多个value值,那么各个value值按从左到右的程序顺次插入表头3.key不存在,一个空列表会被创立并执行LPUSH操作4.key存在但不是列表类型,返回谬误LPUSH key value [value ...]LPUSHX1.将值value插入到列表key的表头,当且仅当key存在且为一个列表2.key不存在时,LPUSHX命令什么都不做LPUSHX key valueLPOP1.移除并返回列表key的头元素LPOP keyLRANGE1.返回列表key中指定区间内的元素,区间以偏移量start和stop指定2.start和stop都以0位底3.可应用正数下标,-1示意列表最初一个元素,-2示意列表倒数第二个元素,以此类推4.start大于列表最大下标,返回空列表5.stop大于列表最大下标,stop=列表最大下标LRANGE key start stopLREM1.依据count的值,移除列表中与value相等的元素2.count>0示意从头到尾搜寻,移除与value相等的元素,数量为count3.count<0示意从从尾到头搜寻,移除与value相等的元素,数量为count4.count=0示意移除表中所有与value相等的元素LREM key count valueLSET1.将列表key下标为index的元素值设为value2.index参数超出范围,或对一个空列表进行LSET时,返回谬误LSET key index valueLINDEX1.返回列表key中,下标为index的元素LINDEX key indexLINSERT1.将值value插入列表key中,位于pivot后面或者前面2.pivot不存在于列表key时,不执行任何操作3.key不存在,不执行任何操作LINSERT key BEFORE AFTER pivot valueLLEN1.返回列表key的长度2.key不存在,返回0LLEN keyLTRIM1.对一个列表进行修剪,让列表只返回指定区间内的元素,不存在指定区间内的都将被移除LTRIM key start stopRPOP1.移除并返回列表key的尾元素RPOP keyRPOPLPUSH在一个原子工夫内,执行两个动作:1.将列表source中最初一个元素弹出并返回给客户端2.将source弹出的元素插入到列表desination,作为destination列表的头元素RPOPLPUSH source destinationRPUSH1.将一个或多个值value插入到列表key的表尾RPUSH key value [value ...]RPUSHX1.将value插入到列表key的表尾,当且仅当key存在并且是一个列表2.key不存在,RPUSHX什么都不做RPUSHX key value 实际:别偷懒,入手一下,try it out 三、利用场景1、lpush+lpop=Stack(栈)2、lpush+rpop=Queue(队列)3、lpush+ltrim=Capped Collection(无限汇合)4、lpush+brpop=Message Queue(音讯队列)5、排行榜,数据最新列表等等四、底层解析结构图上显示,List类型有两种实现形式:举例说明,创立列表对象 numbers 1、应用压缩列表(ziplist)实现的列表对象构造如下 2、应用双端链表(linkedlist)实现的列表对象构造如下 五、疑难思考压缩列表与双端链表是什么样的构造? 1、压缩列表(ziplist)压缩列表(ziplist)是Redis为了节俭内存而开发的,是由一系列非凡编码的间断内存块组成的程序型数据结构,一个压缩列表能够蕴含任意多个节点(entry),每个节点能够保留一个字节数组或者一个整数值。 构造如下 压缩列表的每个节点形成如下 1)previous_entry_ength:以字节为单位,记录了压缩列表中前一个字节的长度。previous_entry_ength 的长度能够是1字节或者5字节: 如果前一节点的长度小于254字节,那么 previous_entry_ength 属性的长度为1字节,前一节点的长度就保留在这一个字节外面。 如果前一个节点的长度大于等于254,那么 previous_entry_ength 属性的长度为5字节,其中属性的第一字节会被设置为0xFE(十进制254),而之后的四个字节则用于保留前一节点的长度。 利用此原理即以后节点地位减去上一个节点的长度即失去上一个节点的起始地位,压缩列表能够从尾部向头部遍历,这么做很无效地缩小了内存的节约。2)encoding:记录了节点的 content 属性所保留数据的类型以及长度。3)content:保留节点的值,节点的值能够是一个字节数组或者整数,值的类型和长度由节点的 encoding 属性决定。 ...

August 11, 2020 · 1 min · jiezi

关于redis:闲扯Redis二String数据类型之底层解析

一、前言Redis 提供了5种数据类型:String(字符串)、Hash(哈希)、List(列表)、Set(汇合)、Zset(有序汇合),了解每种数据类型的特点对于redis的开发和运维十分重要。 二、疑难与解析 结构图上显示,String类型有三种实现形式: 应用整数值实现的字符串对象应用 embstr 编码的动静字符串实现的字符串对象动静字符串实现的字符串对象 疑难:<font color='red'>embstr</font> 是什么意思,<font color='red'>动静字符串</font>又是什么意思?<font color='red'>字符串对象</font>到底什么构造?<font color='red'>三种实现形式</font>有什么区别呢?<center><p></p></center> 不急,咱们一步一步的往下看: 1、Redis中定义的对象的构造体/* * Redis 对象 */typedef struct redisObject { // 类型 4bits unsigned type:4; // 编码方式 4bits unsigned encoding:4; // LRU 工夫(绝对于 server.lruclock) 24bits unsigned lru:22; // 援用计数 Redis外面的数据能够通过援用计数进行共享 32bits int refcount; // 指向对象的值 64-bit void *ptr;} robj;// 16bytes正文:type示意该对象的类型,即下面 [String,List,Hash,Set,Zset] 中的一个,但为了进步存储效率与程序执行效率,每种对象的底层数据结构实现都可能不止一种,encoding 示意对象底层所应用的编码。 2、Redis对象底层八种数据结构 REDIS_ENCODING_INT(long 类型的整数) REDIS_ENCODING_EMBSTR embstr (编码的简略动静字符串) REDIS_ENCODING_RAW (简略动静字符串) REDIS_ENCODING_HT (字典) REDIS_ENCODING_LINKEDLIST (双端链表) REDIS_ENCODING_ZIPLIST (压缩列表) REDIS_ENCODING_INTSET (整数汇合) REDIS_ENCODING_SKIPLIST (跳跃表和字典)3、embstr与动静字符串<font color='red'>embstr</font> :是专门用于保留短字符串的一种优化编码方式,跟失常的字符编码相比,字符编码会调用两次内存调配函数来别离创立 redisObject 和 sdshdr 构造(<font color='red'>动静字符串构造</font>),而 embstr 编码则通过调用一次内存调配函数来调配一块间断的内存空间,空间中蕴含 redisObject 和 sdshdr(动静字符串)两个构造,两者在同一个内存块中。从 Redis 3.0 版本开始,字符串引入了 embstr 编码方式,长度小于 OBJ_ENCODING_EMBSTR_SIZE_LIMIT(39) 的字符串将以EMBSTR形式存储。 ...

August 11, 2020 · 2 min · jiezi

关于redis:闲扯Redis一五种数据类型之String型

一、前言Redis 提供了5种数据类型:String(字符串)、Hash(哈希)、List(列表)、Set(汇合)、Zset(有序汇合),了解每种数据类型的特点对于redis的开发和运维十分重要。<p align="right">原文解析</p> String是Redis的最根本的数据类型,能够了解为与 Memcached 截然不同的类型,即Key-Value型的数据,String类型是二进制平安的,另外 Redis 的 String 能够蕴含任何数据,简略的字符串、简单的字符串(xml、json)、数字(整数、浮点数)、二进制(图片、音频、视频),一个 Redis 中字符串 value 最多能够是 512M。 二、String 类型操作命令命令形容用法SET1.将字符串值Value关联到Key2.Key已关联则笼罩,忽视类型3.本来Key带有生存工夫TTL,那么TTL被革除SET key value [EX seconds] [PX milliseconds] [NX|XX]GET1.返回key关联的字符串值2.Key不存在返回nil3.Key存储的不是字符串,返回谬误,因为GET只用于解决字符串GET keyMSET1.同时设置一个或多个Key-Value键值对2.某个给定Key曾经存在,那么MSET新值会笼罩旧值3.如果下面的笼罩不是心愿的,那么应用MSETNX命令,所有Key都不存在才会进行笼罩4.MSET是一个原子性操作,所有Key都会在同一时间被设置,不会存在有些更新有些没更新的状况MSET key value [key value ...]MGET1.返回一个或多个给定Key对应的Value2.某个Key不存在那么这个Key返回nilMGET key [key ...]SETEX1.将Value关联到Key2.设置Key生存工夫为seconds,单位为秒3.如果Key对应的Value曾经存在,则笼罩旧值4.SET也能够设置生效工夫,然而不同在于SETNX是一个原子操作,即关联值与设置生存工夫同一时间实现SETEX key seconds valueSETNX1.将Key的值设置为Value,当且仅当Key不存在2.若给定的Key曾经存在,SEXNX不做任何动作SETNX key value三、利用场景缓存性能:字符串最经典的应用场景,redis最为缓存层,Mysql作为贮存层,绝大部分申请数据都是redis中获取,因为redis具备撑持高并发个性,所以缓存通常能起到减速读写和升高 后端压力的作用。计数器:许多使用都会应用redis作为计数的根底工具,他能够实现疾速计数、查问缓存的性能,同时数据能够一步落地到其余的数据源。如:视频播放数零碎就是应用redis作为视频播放数计数的根底组件。共享session:出于负载平衡的思考,分布式服务会将用户信息的拜访平衡到不同服务器上,用户刷新一次拜访可能会须要从新登录,为防止这个问题能够用redis将用户session集中管理,在这种模式下只有保障redis的高可用和扩展性的,每次获取用户更新或查问登录信息都间接从redis中集中获取。限速:处于平安思考,每次进行登录时让用户输出手机验证码,为了短信接口不被频繁拜访,会限度用户每分钟获取验证码的频率。四、非凡的String操作:INCR/DECR INCR/DECR操作能够利用Redis主动帮忙咱们对一个Key对应的Value进行加减,在理论工作中还是很罕用的。 INCR/DECR操作在 Redis 中的相干命令: 命令形容用法INCR1)Key中存储的数字值+1,返回减少之后的值2)Key不存在,那么Key的值被初始化为0再执行INCR3)如果值蕴含谬误类型或者字符串不能被示意为数字,那么返回谬误4)值限度在64位有符号数字示意之内即-9223372036854775808~9223372036854775807INCR keyDECR1)Key中存储的数字值-12)其余同INCRDECR keyINCRBY1)将key所存储的值加上增量返回减少之后的值2)其余同INCRINCRBY key incrementDECRBY1)将key所存储的值减去减量decrement2)其余同INCRDECRBY key decrement五、INCR/DECR 利用场景原先单机环境中统计在线人数,变成分布式部署之后能够应用INCR/DECR因为Redis自身极高的读写性能,一些秒杀的场景库存增减能够基于Redis来做而不是间接操作DB

August 11, 2020 · 1 min · jiezi

关于redis:redis-文章投票

参考《redis实战》 需要1、文章信息包含:题目、内容、链接、公布工夫、公布人,公布后就给本人投一张票。2、一个人只能给一篇文章投一张票。3、文章只能七天内容许投票。4、文章投票排名思考文章公布工夫以及投票数量,以公布工夫作为降序排序,如果投票数量为200,则工夫向前移一天。5、文章排序包含公布工夫排序,投票得分排序剖析第一个需要能够采纳散列来保留文章信息。存入信息用HMSET,取出对应字段信息用HMGET,取出key的所有信息用HGETALL,根本用法如下: // 赋值local:0>hmset person:001 name '张三' age 18"OK"// 取对应key的某个属性值 local:0>hmget person:001 name1) "张三"// 取对应key的所有属性值local:0>hgetall person:0011) "name"2) "张三"3) "age"4) "18"第二个需要这边思考到不能反复的概念,在java里用set,redis也有汇合的概念。汇合简略的说,就是不能有反复的数据,根本用法如下: // 赋值local:0>sadd name '张三'"1"// 赋值local:0>sadd name '李四'"1"// 赋值失败,返回0,因为曾经增加过local:0>sadd name '张三'"0"// 获取对应key的所有成员local:0>smembers name1) "张三"2) "李四"第三个需要有工夫管制需要,须要把文章id和公布工夫保存起来,思考到前面用工夫进行排序,所以用有序汇合。有序汇合和下面汇合不一样的是,多了一个分值的概念,能够通过分值进行排序等操作。七天后不能投票就能够通过这个分值来计算。 // 增加local:0>zadd score 88 '赵大'"1"// 增加local:0>zadd score 93 '熊二'"1"// 增加local:0>zadd score 92 '张三'"1" // 增加local:0>zadd score 89 '李四'"1"// 增加local:0>zadd score 70 '王五'"1"// 反复增加失败返回0,local:0>zadd score 60 '王五'"0"// 分数加5,返回最终值local:0>zincrby score 5 '王五'"65"// 从低到高排序,取前四个local:0>zrange score 0 3 withscores1) "王五"2) "65"3) "赵大"4) "88"5) "李四"6) "89"7) "张三"8) "92"// 从高到低排序,取前四个local:0>zrevrange score 0 3 withscores1) "熊二"2) "93"3) "张三"4) "92"5) "李四"6) "89"7) "赵大"8) "88"// 获取分数值local:0>zscore score '张三'"92"第四个需要一天有84600秒,200票工夫向前挪动一天,则每票就是84600/200=432分。 ...

August 10, 2020 · 2 min · jiezi

关于redis:redis-文章投票

参考《redis实战》 需要1、文章信息包含:题目、内容、链接、公布工夫、公布人,公布后就给本人投一张票。2、一个人只能给一篇文章投一张票。3、文章只能七天内容许投票。4、文章投票排名思考文章公布工夫以及投票数量,以公布工夫作为降序排序,如果投票数量为200,则工夫向前移一天。5、文章排序包含公布工夫排序,投票得分排序剖析第一个需要能够采纳散列来保留文章信息。存入信息用HMSET,取出对应字段信息用HMGET,取出key的所有信息用HGETALL,根本用法如下: // 赋值local:0>hmset person:001 name '张三' age 18"OK"// 取对应key的某个属性值 local:0>hmget person:001 name1) "张三"// 取对应key的所有属性值local:0>hgetall person:0011) "name"2) "张三"3) "age"4) "18"第二个需要这边思考到不能反复的概念,在java里用set,redis也有汇合的概念。汇合简略的说,就是不能有反复的数据,根本用法如下: // 赋值local:0>sadd name '张三'"1"// 赋值local:0>sadd name '李四'"1"// 赋值失败,返回0,因为曾经增加过local:0>sadd name '张三'"0"// 获取对应key的所有成员local:0>smembers name1) "张三"2) "李四"第三个需要有工夫管制需要,须要把文章id和公布工夫保存起来,思考到前面用工夫进行排序,所以用有序汇合。有序汇合和下面汇合不一样的是,多了一个分值的概念,能够通过分值进行排序等操作。七天后不能投票就能够通过这个分值来计算。 // 增加local:0>zadd score 88 '赵大'"1"// 增加local:0>zadd score 93 '熊二'"1"// 增加local:0>zadd score 92 '张三'"1" // 增加local:0>zadd score 89 '李四'"1"// 增加local:0>zadd score 70 '王五'"1"// 反复增加失败返回0,local:0>zadd score 60 '王五'"0"// 分数加5,返回最终值local:0>zincrby score 5 '王五'"65"// 从低到高排序,取前四个local:0>zrange score 0 3 withscores1) "王五"2) "65"3) "赵大"4) "88"5) "李四"6) "89"7) "张三"8) "92"// 从高到低排序,取前四个local:0>zrevrange score 0 3 withscores1) "熊二"2) "93"3) "张三"4) "92"5) "李四"6) "89"7) "赵大"8) "88"// 获取分数值local:0>zscore score '张三'"92"第四个需要一天有84600秒,200票工夫向前挪动一天,则每票就是84600/200=432分。 ...

August 10, 2020 · 2 min · jiezi

关于redis:redis分布式锁

前言上回说到如何应用zookeeper实现分布式锁,它是通过节点的新建和删除来实现的,这种频繁的io操作在并发很高的状况下必定是不实用的,那这节咱们来看看如何应用redis实现分布式锁。 咱们都晓得redis最大的劣势就是速度快,大部分操作都是在内存中间接实现的,这就和io操作不在一个数量级上了。 手动实现分布式锁实现逻辑首先咱们要理清redis实现分布式锁的逻辑:在redis中,咱们次要通过setnx这个命令来实现分布式锁,这个命令什么意思呢?set not exit,只有在key不存在时,才会设值胜利,这就能够模拟出各个客户端过去拿锁的过程,当多个客户端同时过去获取锁时,当有一个setnx胜利时,那其余客户端都会失败,那这些失败的客户端怎么办呢?咱们能够通过循环期待来反复获取锁,直到获取锁胜利。 获取到锁的客户端能够通过删除节点来开释锁,这里有一点须要留神:在zookeeper中能够通过长期节点来避免客户端宕机等起因造成的死锁问题,那在redis中就须要无效工夫来避免了,当客户端在肯定工夫范畴内还没有开释锁的话,就须要主动删除节点来开释锁。 留神点1、在获取锁的时候须要setnx和设置无效工夫,这两步必须原子操作,否则在高并发状况下必定会出问题。 从 Redis 2.6.12 版本开始, set 命令的行为能够通过一系列参数来批改:SET key value [EX seconds] [PX milliseconds] [NX|XX],通过参数设值,能够同时实现setnx和setex两种成果,因而不必散布执行,这就保障了操作的原子性。 2、在获取锁设值的时候,须要设值一个线程的惟一id,这里能够应用uuid来实现,在开释锁的时候,也就是删除key须要判断值是否和以后线程的uuid一样,一样才示意是以后持有的锁。 代码实现测试redis客户端选用jeds,须要引入jedis包 <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId></dependency>写一个工具类来获取jedis public class RedisUtil { private static volatile JedisPool jedisPool = null; private RedisUtil(){} public static JedisPool getJedisPool() { if (jedisPool == null) { synchronized (RedisUtil.class) { if (jedisPool == null) { GenericObjectPoolConfig config = new GenericObjectPoolConfig(); config.setMaxTotal(100); config.setMaxIdle(20); jedisPool = new JedisPool(config, "127.0.0.1", 6379); } } } return jedisPool; } public static Jedis getJedis() { return getJedisPool().getResource(); }}下面jedis连接池应用单例来保障唯一性,如果应用spring框架的话,能够交给spring来治理,我这里只是单纯测试类。 ...

August 10, 2020 · 2 min · jiezi

关于redis:redis分布式锁

前言上回说到如何应用zookeeper实现分布式锁,它是通过节点的新建和删除来实现的,这种频繁的io操作在并发很高的状况下必定是不实用的,那这节咱们来看看如何应用redis实现分布式锁。 咱们都晓得redis最大的劣势就是速度快,大部分操作都是在内存中间接实现的,这就和io操作不在一个数量级上了。 手动实现分布式锁实现逻辑首先咱们要理清redis实现分布式锁的逻辑:在redis中,咱们次要通过setnx这个命令来实现分布式锁,这个命令什么意思呢?set not exit,只有在key不存在时,才会设值胜利,这就能够模拟出各个客户端过去拿锁的过程,当多个客户端同时过去获取锁时,当有一个setnx胜利时,那其余客户端都会失败,那这些失败的客户端怎么办呢?咱们能够通过循环期待来反复获取锁,直到获取锁胜利。 获取到锁的客户端能够通过删除节点来开释锁,这里有一点须要留神:在zookeeper中能够通过长期节点来避免客户端宕机等起因造成的死锁问题,那在redis中就须要无效工夫来避免了,当客户端在肯定工夫范畴内还没有开释锁的话,就须要主动删除节点来开释锁。 留神点1、在获取锁的时候须要setnx和设置无效工夫,这两步必须原子操作,否则在高并发状况下必定会出问题。 从 Redis 2.6.12 版本开始, set 命令的行为能够通过一系列参数来批改:SET key value [EX seconds] [PX milliseconds] [NX|XX],通过参数设值,能够同时实现setnx和setex两种成果,因而不必散布执行,这就保障了操作的原子性。 2、在获取锁设值的时候,须要设值一个线程的惟一id,这里能够应用uuid来实现,在开释锁的时候,也就是删除key须要判断值是否和以后线程的uuid一样,一样才示意是以后持有的锁。 代码实现测试redis客户端选用jeds,须要引入jedis包 <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId></dependency>写一个工具类来获取jedis public class RedisUtil { private static volatile JedisPool jedisPool = null; private RedisUtil(){} public static JedisPool getJedisPool() { if (jedisPool == null) { synchronized (RedisUtil.class) { if (jedisPool == null) { GenericObjectPoolConfig config = new GenericObjectPoolConfig(); config.setMaxTotal(100); config.setMaxIdle(20); jedisPool = new JedisPool(config, "127.0.0.1", 6379); } } } return jedisPool; } public static Jedis getJedis() { return getJedisPool().getResource(); }}下面jedis连接池应用单例来保障唯一性,如果应用spring框架的话,能够交给spring来治理,我这里只是单纯测试类。 ...

August 10, 2020 · 2 min · jiezi

关于redis:赵强老师Redis的RDB持久化

Redis 提供了多种不同级别的长久化形式: RDB 长久化能够在指定的工夫距离内生成数据集的工夫点快照(point-in-time snapshot)。AOF (Append-only file)长久化记录服务器执行的所有写操作命令,并在服务器启动时,通过从新执行这些命令来还原数据集。 AOF 文件中的命令全副以 Redis 协定的格局来保留,新命令会被追加到文件的开端。 Redis 还能够在后盾对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保留数据集状态所需的理论大小。Redis 还能够同时应用 AOF 长久化和 RDB 长久化。 在这种状况下, 当 Redis 重启时, 它会优先应用 AOF 文件来还原数据集, 因为 AOF 文件保留的数据集通常比 RDB 文件所保留的数据集更残缺。你甚至能够敞开长久化性能,让数据只在服务器运行时存在。一、RDB的长久化工作原理:每隔肯定工夫给内存照一个快照,将内存中的数据写入文件(rdb文件)。这是Redis默认的长久化形式。当redis生成dump.rdb文件时,工作过程如下: 当达到RDB生成条件时,redis主过程fork一个子过程fork进去的子过程将内存的数据集dump到长期的RDB中当子过程对长期的RDB文件写入结束,redis用新的RDB文件代替旧的RDB文件配置参数如下: RDB示例测试:能够应用redis-benchmark进行压力测试,察看RDB文件大小的变动。 bin/redis-benchmark -n 100000 示意执行100000个操作二、RDB的毛病:在两次快照之间,如果产生断电,数据会失落。举例:在生成rdb后,插入新值。忽然断电,数据可能会失落。 三、监控RDB:Redis监控最间接的办法当然就是应用零碎提供的 info 命令来做了,只须要执行上面一条命令,就能取得 Redis 零碎的状态报告。 bin/redis-cli info | grep rdb_rdb_changes_since_last_save 表明上次RDB保留当前扭转的key次数rdb_bgsave_in_progress 示意以后是否在进行bgsave操作,1示意正在进行;0示意没有进行rdb_last_save_time 上次保留RDB文件的工夫戳rdb_last_bgsave_time_sec 上次保留的耗时rdb_last_bgsave_status 上次保留的状态rdb_current_bgsave_time_sec 目前保留RDB文件已破费的工夫

August 10, 2020 · 1 min · jiezi

关于redis:Redis持久化之RDB-AOF

1. 为什么须要长久化?Redis的读写和响应速度为什么如此之快?不同于传统的关系型数据库,Redis的数据库是全副加载在实时内存中的,读写操作可能间接在内存中进行,省去了大量IO操作,因而性能十分优越。但所有依靠于内存的服务都有个最大的软肋,那便是在设施呈现故障(如断点)时,内存中所有的实时数据都将会失落。为了解决这个问题,Redis中提供了两种长久化计划:RDB和AOF,其机制实际上都是设法将内存中的数据以文件的模式备份到磁盘上,从而解决断电等机器故障导致数据失落的问题。 2. RDB (Redis DataBase)实现机制:Redis会独自创立(fork)一个子过程来进行长久化,会先将数据写入到一个临时文件中,待长久化过程完结后,再用这个临时文件替换上次长久化好的文件(dump.rdb)。整个过程中,主过程不会进行任何IO操作的,这样就可能保障Redis主过程继续的高性能。在复原数据时,重启后会Redis会主动加载工作目录下的dump.rdb文件复原数据到内存 相干参数: ## 设置触发RDB dump的条件,在<seconds>工夫至多产生了<changes>次数据的操作,则<seconds>工夫到了就将数据写到磁盘中,为dump.rdb Redis默认设置了三个条件,满足之一即可触发。移除所有条件或者设置 save 后加空字符串为禁用RDB性能save <seconds> <changes>save 900 1 ## for 低频写入 save 300 10 ## for 中频写入save 60 10000 ## for 高频写入## 设置当RDB过程呈现故障时,Redis是否进行写操作,默认为yesstop-writes-on-bgsave-error yes## 设置RDB时是否应用LZF压缩算法对dump.rdb文件进行压缩,默认yesrdbcompression yes## 设置是否对dump.rdb文件进行校验,默认CRC64校验算法,会耗费RDB过程的CPU资源的10%,默认开rdbchecksum yes## 设置输入文件名, 默认dump.rdbdbfilename dump.rdb## 设置dump.rdb文件输入门路,默认为以后启动工作目录dir ./劣势: 无论是长久化过程还是数据恢复过程,RDB的速度都十分快用户可能手动设置参数以管制长久化的频率,利用场景更加灵便适宜大规模且对数据的完整性和一致性要求不高的数据恢复,劣势: RDB的长久化须要fork一条子线程,且子线程将会copy主线程所有数据,相当于设施的内存应用长期翻了一倍,在数据量特地大的状况下应用RDB可能有内存溢出危险最初一次长久化的数据可能失落,因而对数据完整性比拟敏感的业务不倡议应用RDB进行长久化PLUS:为什么最初一次长久化的数据可能失落? RDB通过设置save <seconds> <changes>条件来触发dump,其机制为满足任一save条件后再该条件的<seconds>后进行刷写,但实际上可能最初一次dump时尽管达到批改次数<changes>要求,但在工夫还未达到时redis过程停止,则这一阶段的数据就失落了。比方save 300 10这一条件,Redis在300s内的批改操作次数超过了10次,因而触发长久化条件,零碎将会在300s到期后进行刷写,然而可能在到期前几秒,设施异样关机,那么这300s内产生的数据批改信息将会全副失落3. AOF(Append Only File)实现机制:与RDB间接写原数据不同,AOF记录的是Redis自启动期间所有的写操作,相似于日志,且写入的形式只容许在开端追加,不容许批改之前写入的内容,默认保留为appendonly.aof文件。复原数据时,Redis将AOF加载到内存并执行其中的每一条数据进行数据的复原。 相干参数: ## 启动开关 -- 默认敞开appendonly no ## 文件名appendfilename "appendonly.aof"## 刷写频率appendfsync always ## 同步长久化,每当有数据变更就立刻记录到磁盘,性能较差但数据完整性好appendfsync everysec ## 出厂默认配置,异步操作,每秒记录,如果一秒内宕机,则会有数据失落append no ## 不设置刷写频率,当Redis本人触发刷写条件时才进行刷写劣势: ...

August 6, 2020 · 1 min · jiezi

关于redis:Redis持久化之RDB-AOF

1. 为什么须要长久化?Redis的读写和响应速度为什么如此之快?不同于传统的关系型数据库,Redis的数据库是全副加载在实时内存中的,读写操作可能间接在内存中进行,省去了大量IO操作,因而性能十分优越。但所有依靠于内存的服务都有个最大的软肋,那便是在设施呈现故障(如断点)时,内存中所有的实时数据都将会失落。为了解决这个问题,Redis中提供了两种长久化计划:RDB和AOF,其机制实际上都是设法将内存中的数据以文件的模式备份到磁盘上,从而解决断电等机器故障导致数据失落的问题。 2. RDB (Redis DataBase)实现机制:Redis会独自创立(fork)一个子过程来进行长久化,会先将数据写入到一个临时文件中,待长久化过程完结后,再用这个临时文件替换上次长久化好的文件(dump.rdb)。整个过程中,主过程不会进行任何IO操作的,这样就可能保障Redis主过程继续的高性能。在复原数据时,重启后会Redis会主动加载工作目录下的dump.rdb文件复原数据到内存 相干参数: ## 设置触发RDB dump的条件,在<seconds>工夫至多产生了<changes>次数据的操作,则<seconds>工夫到了就将数据写到磁盘中,为dump.rdb Redis默认设置了三个条件,满足之一即可触发。移除所有条件或者设置 save 后加空字符串为禁用RDB性能save <seconds> <changes>save 900 1 ## for 低频写入 save 300 10 ## for 中频写入save 60 10000 ## for 高频写入## 设置当RDB过程呈现故障时,Redis是否进行写操作,默认为yesstop-writes-on-bgsave-error yes## 设置RDB时是否应用LZF压缩算法对dump.rdb文件进行压缩,默认yesrdbcompression yes## 设置是否对dump.rdb文件进行校验,默认CRC64校验算法,会耗费RDB过程的CPU资源的10%,默认开rdbchecksum yes## 设置输入文件名, 默认dump.rdbdbfilename dump.rdb## 设置dump.rdb文件输入门路,默认为以后启动工作目录dir ./劣势: 无论是长久化过程还是数据恢复过程,RDB的速度都十分快用户可能手动设置参数以管制长久化的频率,利用场景更加灵便适宜大规模且对数据的完整性和一致性要求不高的数据恢复,劣势: RDB的长久化须要fork一条子线程,且子线程将会copy主线程所有数据,相当于设施的内存应用长期翻了一倍,在数据量特地大的状况下应用RDB可能有内存溢出危险最初一次长久化的数据可能失落,因而对数据完整性比拟敏感的业务不倡议应用RDB进行长久化PLUS:为什么最初一次长久化的数据可能失落? RDB通过设置save <seconds> <changes>条件来触发dump,其机制为满足任一save条件后再该条件的<seconds>后进行刷写,但实际上可能最初一次dump时尽管达到批改次数<changes>要求,但在工夫还未达到时redis过程停止,则这一阶段的数据就失落了。比方save 300 10这一条件,Redis在300s内的批改操作次数超过了10次,因而触发长久化条件,零碎将会在300s到期后进行刷写,然而可能在到期前几秒,设施异样关机,那么这300s内产生的数据批改信息将会全副失落3. AOF(Append Only File)实现机制:与RDB间接写原数据不同,AOF记录的是Redis自启动期间所有的写操作,相似于日志,且写入的形式只容许在开端追加,不容许批改之前写入的内容,默认保留为appendonly.aof文件。复原数据时,Redis将AOF加载到内存并执行其中的每一条数据进行数据的复原。 相干参数: ## 启动开关 -- 默认敞开appendonly no ## 文件名appendfilename "appendonly.aof"## 刷写频率appendfsync always ## 同步长久化,每当有数据变更就立刻记录到磁盘,性能较差但数据完整性好appendfsync everysec ## 出厂默认配置,异步操作,每秒记录,如果一秒内宕机,则会有数据失落append no ## 不设置刷写频率,当Redis本人触发刷写条件时才进行刷写劣势: ...

August 6, 2020 · 1 min · jiezi

关于redis:Redis-606-发布看看有哪些特性

Redis 6.0.6 已公布,此版本修复了一些 bug,其中次要内容包含: 修复启用带前缀的 CLIENT TRACKING 时解体的问题EXEC 始终会因 EXECABORT 而失败,并且已革除多状态RESTORE ABSTTL 不会将过期的密钥存储到数据库中redis-cli 可能更好地解决非优先键名TLS:敞开 tls-auth-clients 时疏忽客户端证书Tracking:修复刷新时的有效音讯在 Sentinel 启动时告诉 systemd修复因滥用 STRALGO 而导致的解体module API 中的一些修复修复一些常见的透露(STRALGO 谬误滥用,Sentinel)修复了脚本碎片整顿中可能的有效拜访新个性LPOS 命令,用于在列表中搜寻在集群模式下的 redis-cli 和 redis-benchmark 中应用 user+pass 进行迁徙redis-cli 反对 --pipe、-rdb 和 --replica 选项的 TLSTLS:反对会话缓存配置更新阐明:https://groups.google.com/for...

July 27, 2020 · 1 min · jiezi

关于redis:linux下安装和简单使用redis

最近在linux服务器上须要装置redis,来存放数据,减少用户拜访数据的速度,因为是第一次装置,于是在百度上搜了一篇文章,依照这篇博客,顺利装置好了,因而将博主的文章拷过去记录一下,不便当前应用,也为须要的敌人提供一个不便 参考博文地址:https://www.cnblogs.com/lauhp... 装置:1.获取redis资源 wget http://download.redis.io/releases/redis-4.0.8.tar.gz2.解压 tar xzvf redis-4.0.8.tar.gz3.编译与装置 cd redis-4.0.8makecd srcmake install PREFIX=/usr/local/redis4.挪动配置文件到装置目录下 cd ../mkdir /usr/local/redis/etcmv redis.conf /usr/local/redis/etc 5.配置redis为后盾启动 vi /usr/local/redis/etc/redis.conf //将daemonize no 改成daemonize yes 6.将redis退出到开机启动 vi /etc/rc.local //在外面增加内容:/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf (意思就是开机调用这段开启redis的命令) 7.开启redis /usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf  8.将redis-cli,redis-server拷贝到bin下,让redis-cli指令能够在任意目录下间接应用 cp /usr/local/redis/bin/redis-server /usr/local/bin/ cp /usr/local/redis/bin/redis-cli /usr/local/bin/ 9.设置redis明码 a.运行命令: redis-cli b.查看现有的redis明码命令(可选操作,能够没有) config get requirepass 如果没有设置过明码的话运行后果会如下 requirepass ''c.设置redis明码 运行命令: config set requirepass ****(****为你要设置的明码),设置胜利的话会返回‘OK’字样 d.测试连贯 重启redis服务 redis-cli -h 127.0.0.1 -p 6379 -a ****(****为你设置的明码) ...

July 24, 2020 · 4 min · jiezi

关于redis:数据安全与性能Redis实战

长久化快照(rdb):保留某个工夫点内存中所有数据的正本。(BGSAVE/SAVE)毛病:存在数据失落:下一次快照创立过程中,若产生解体,会失落上次快照后的所有数据。AOF:保留写命令到AOF文件开端,记录数据变动。(相似Mysql的binlog)毛病:保留的文件可能会比拟宏大,占用很多硬盘空间。两者既能够独自应用,也能够协同应用。 RDBBGSAVE会创立子过程来进行贮存,会与主过程竞争资源;而SAVE则间接阻塞客户端申请,转而去备份。 对于占用几十个GB内存的Redis服务器,BGSAVE创立过程要花费15秒,备份完须要15~20分钟。而SAVE仅须要3~5分钟。所以罕用做法是,写一个脚本,在中午去手动SAVE。AOF 审慎应用appendfsync:always。尤其是固态,个别举荐应用everysec。bgrewriteaof命令:相似bgsave,新建子线程,用于精简AOF文件,删除冗余命令,重写AOF文件。可通过auto-aof-rewrite-min-size和auto-aof-rewrite-percentage来主动执行重写。 复制主从同步主用于写,从用于读。master的rdb文件先交给slave去写入本人内存,再将同步期间master收到的写命令放入缓冲区,rdb复原完后,会再次接管master缓冲区传来的写命令。而后master都将aof定期交给slave实现同步。客户端写入master,读取拜访slave。相干配置: 主:dir和dbfilename必须配好。从:slaveof host port这个为必须的。 即可在conf配置文件设置salveof来选定从服务器,也可通过slaveof命令来设置。slave同步前会清空之前的所有数据。redis不反对主-主复制。(相互设成对方的slave,不反对!)毛病:如果主挂了,那就没有写服务器了。(主从连,sentinel模式能够改善这一毛病)主从连从服务器能够领有本人的从服务器。主从复制与从从复制的惟一区别:在主服务器A,从服务器X有一个从服务器Y的模式下,在X与A同步时X写入rdb至内存时,会与Y断开连接,导致Y从新同步。(与主同步时,断开本人从的连贯) 实用场景:读的需要一台从服务器不能满足,须要更多的读服务器。注:查看是否已全副写入硬盘:info中aof-pending-bio-fsync属性是否为0更换主服务器在主服务器A,从服务器B,A挂了的场景下。 法一:另寻新主。晋升C为主服务器。作法:B进行一次SAVE,而后将快照交给C,C启动redis,而后B改设为C的从。法二:本人作主。B本人作为主服务器,再创立新的从服务器C。(redis sentinel就是监听故障,晋升从为主)

July 23, 2020 · 1 min · jiezi

关于redis:数据类型及操作Redis实战

redis中key和value最大为512M字节Redis中武官网:Redis官网-stringstring可存储:字符串(512M)、整数、浮点数 整数的取值范畴 == 零碎的long integer(32位零碎为32位,64位零碎为64位);浮点数与double雷同。整数与浮点数能够进行自增、自减`INCR/DECR`;减少固定整数`INCRBY/DECRBY`;步长为浮点数`INCRBYFLOAT`。当对字符串进行增减时,会抛出谬误(error) ERR value is not an integer or out of range 其余操作: 分为字符串和二进制位,如下图: list双向链表。阻塞式 阻塞式罕用于消息传递和工作队列。(繁难的rabbitMQ)此外还能够利用于基于工夫先后的情景:如,最近浏览记录。(暗含了工夫先后,能够代替zset)set常用命令:增,删,查,求总数,随机弹出,随机移除,移到另一汇合。高级操作:并(union),交(inter),差集(diff),以及它们的转储。简略示例: 利用场景:独特XX,独有XX,多人共享XXhash存储多个键值对在一个key中。 一次放单个的指令hset(个别不须要,因为hmset也能够放单个)高级:是否存在,所有键,所有值,所有键值对,自增 hgetall审慎应用,个别应用hkeys查出所有键,再hget一个接一个取出值,防止阻塞。zset带权(score)的set。 除了set的基本操作外,还包含了对于score的操作:zcount、zrank、zscore、`` 因为zset也是set,所以还蕴含并交差,相应的转储。这里要留神,并交差的规定:汇集函数: 不带权的set,每一个被视作1,而带权的zset,权是为score,默认为权的总和,如果申明了汇集函数aggregate(SUN/MIN/MAX),则依据汇集函数来计算。权重: 可选WEIGHTS参数,若未设置,则为1比1,可设为WEIGHTS 2 3,则示意 总分数 = 前一个set*2 + 后一个set*3pub/sub基于管道/频道(channel)的通信。(Java NIO也是基于管道) 理解即可,有很多毛病,因为redis是单线程,所以有可能会呈现音讯失落,客户端卡顿等等问题。个别应用业余的MQ来做,如RabbitMQ,ActiveMQ,RocketMQ等等。其余命令排序(sort)性能很弱小,详情到:redis官网-sortRedis事务事务是将所有命令退出到队列中,一次性发给redis。命令: WATCH、MULTI、EXEC、UNWATCH、DISCARD 最根本的就两个命令:MULTI开启事务,EXEC执行事务。watch:用于multi前,监听一个/多个键,若有key在exec前被批改,则回滚事务,所有的命令都不执行。(乐观锁)unwatch就是勾销监听某些key了。discard:勾销掉事务,抛弃MULTI之后的命令,同时勾销掉所有WATCH。 留神:redis事务自身不是原子操作,而是批量操作(batch),如果两头某个命令失败,是不会影响前后的命令的,也不会回滚。所以叫做批量操作更适合。(watch会可能导致回滚的)键的过期工夫 不得不提setex这个命令,用于【原子操作】 设置string类型的键值和过期工夫。默认状况:键的过期工夫是-1,是永恒存在的。此外,redis还反对Geo(地理坐标)、HyperLogLog(基数)等等类型数据,能够去redis官网查看。

July 23, 2020 · 1 min · jiezi

关于redis:缓存应用场景Redis实战

登录cookie缓存两种cookie:签名cookie,令牌cookie(token)。签名cookie:存储用户名、用户id,最新登录工夫,sessionId,数据签名等。令牌cookie(token):存储一串字符串(如:JWT生成)这里采纳token,限度会话总数,移除最多100个令牌。 定期作业能够采纳守护线程来做,也可用任务调度框架来做。购物车通过sessionId来存储购物车信息,前端将sessionId存储cookie中。 网页缓存缓存一些常常应用,不怎么扭转的可缓存网页,防止动静生成页面。 数据行缓存缓存一些常常被拜访的数据,通常为JSON格局。

July 22, 2020 · 1 min · jiezi

关于redis:文章投票分组排序Redis实战

根底命令就跳过了,命令详情能够参照redis.io官网,或者Redis中武官网 Redis概要介绍: Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它能够用作数据库、缓存和消息中间件。 它反对多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 汇合(sets), 有序汇合(sorted sets) 与范畴查问, bitmaps, hyperloglogs 和 天文空间(geospatial) 索引半径查问。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘长久化(persistence), 并通过 Redis哨兵(Sentinel)和主动 分区(Cluster)提供高可用性(high availability)。 投票系统——仅反对票目标:设计出一个随工夫流逝一直递加的评分零碎。同时实现按组的最热/最新。分数计算形式:最终评分 = 文章反对票数 × 一个常量 + 文章公布工夫(Unix工夫)文章存储形式: 键为article:92617,示意为article表中id为92617的文章(`hmset`)实现:两个zset:一个key为文章id,score为 $\underline{公布工夫}$;(最新)另一个key为文章id,score寄存 $\underline{评分}$。(最热)避免用户屡次投票:用个set保留已投票用户id。 性能实现投票: 文章hash中votes的值加一 HINCRBY;记录score的zset中对应的article的score减少ZINCRBY;对应的记录投票人员的set表退出该用户。(遵循一个事务)公布: 生成articleId,存储文章信息hmset,生成vote汇合,生成score,time有序汇合。获取: zrevrange/zrange获取最热/最新的article_ids汇合,通过遍历 id来hgetall获取每篇文章的所有属性键值对。分组: 新建一个名为group的set保留所有article_id。要使能按组获取最热/最新文章。 zinterstore命令 (比拟耗费工夫,能够设置为60s才过期)get_group_articles()当然,在article的hash中退出group_id来限度文章的分组个数。(这里投票时也要对score:programming进行加分)

July 22, 2020 · 1 min · jiezi

关于redis:Redis-缓存穿透-缓存雪崩-缓存击穿

明天看了面试材料 简直都要问Redis 开始温习吧 ~ 缓存穿透缓存雪崩缓存击穿 1.1 什么是缓存穿透?业务零碎要查问的数据基本就存在!当业务零碎发动查问时,依照上述流程,首先会返回缓存中查问,因为缓存中不存在,而后再返回数据库中查问。因为该数据压根就不存在,因而数据库也返回空。这就是缓存穿透。 综上所述:业务零碎拜访压根就不存在的数据,就称为缓存穿透 1.2 缓存穿透的危害如果存在海量申请查问压根就不存在的数据,那么这些海量申请都会落到数据库中,数据库压力剧增,可能会导致系统解体(你要晓得,目前业务零碎中最软弱的就是IO,略微来点压力它就会解体,所以咱们要想种种方法爱护它)。 1.3 为什么会产生缓存穿透?产生缓存穿透的起因有很多,个别为如下两种: 歹意攻打,成心营造大量不存在的数据申请咱们的服务,因为缓存中并不存在这些数据,因而海量申请均落在数据库中,从而可能会导致数据库解体。 代码逻辑谬误。这是程序员的锅,没啥好讲的,开发中肯定要防止! 1.4 缓存穿透的解决方案上面来介绍两种避免缓存穿透的伎俩。 1.4.1 缓存空数据之所以产生缓存穿透,是因为缓存中没有存储这些空数据的key,导致这些申请全都打到数据库上。 那么,咱们能够略微批改一下业务零碎的代码,将数据库查问后果为空的key也存储在缓存中。当后续又呈现该key的查问申请时,缓存间接返回null,而无需查询数据库。 缓存空对象会有两个问题: 第一,空值做了缓存,意味着缓存层中存了更多的键,须要更多的内存空间 ( 如果是攻打,问题更重大 ),比拟无效的办法是针对这类数据设置一个较短的过期工夫,让其主动剔除。 第二,缓存层和存储层的数据会有一段时间窗口的不统一,可能会对业务有肯定影响。例如过期工夫设置为 5 分钟,如果此时存储层增加了这个数据,那此段时间就会呈现缓存层和存储层数据的不统一,此时能够利用音讯零碎或者其余形式革除掉缓存层中的空对象。 1.4.2 BloomFilter第二种防止缓存穿透的形式即为应用BloomFilter。 它须要在缓存之前再加一道屏障,外面存储目前数据库中存在的所有key,如下图所示: 当业务零碎有查问申请的时候,首先去BloomFilter中查问该key是否存在。若不存在,则阐明数据库中也不存在该数据,因而缓存都不要查了,间接返回null。若存在,则继续执行后续的流程,先返回缓存中查问,缓存中没有的话再返回数据库中的查问。 这种办法实用于数据命中不高,数据绝对固定实时性低(通常是数据集较大)的利用场景,代码保护较为简单,然而缓存空间占用少。 1.4.3 两种计划的比拟这两种计划都能解决缓存穿透的问题,但应用场景却各不相同。 对于一些歹意攻打,查问的key往往各不相同,而且数据贼多。此时,第一种计划就显得提襟见肘了。因为它须要存储所有空数据的key,而这些歹意攻打的key往往各不相同,而且同一个key往往只申请一次。因而即便缓存了这些空数据的key,因为不再应用第二次,因而也起不了爱护数据库的作用。因而,对于空数据的key各不相同、key反复申请概率低的场景而言,应该抉择第二种计划。而对于空数据的key数量无限、key反复申请概率较高的场景而言,应该抉择第一种计划。 2. 缓存雪崩2.1 什么是缓存雪崩?通过上文可知,缓存其实表演了一个爱护数据库的角色。它帮数据库抵御大量的查问申请,从而防止软弱的数据库受到挫伤。 如果缓存因某种原因产生了宕机,那么本来被缓存抵御的海量查问申请就会像疯狗一样涌向数据库。此时数据库如果抵御不了这微小的压力,它就会解体。 这就是缓存雪崩。 2.2 如何防止缓存雪崩?2.2.1 应用缓存集群,保障缓存高可用和飞机都有多个引擎一样,如果缓存层设计成高可用的,即便个别节点、个别机器、甚至是机房宕掉,仍然能够提供服务,例如后面介绍过的 Redis Sentinel 和 Redis Cluster 都实现了高可用。 2.2.1 应用HystrixHystrix是一款开源的“防雪崩工具”,它通过 熔断、降级、限流三个伎俩来升高雪崩产生后的损失。 Hystrix就是一个Java类库,它采纳命令模式,每一项服务解决申请都有各自的处理器。所有的申请都要通过各自的处理器。处理器会记录以后服务的申请失败率。一旦发现以后服务的申请失败率达到预设的值,Hystrix将会回绝随后该服务的所有申请,间接返回一个预设的后果。这就是所谓的“熔断”。当通过一段时间后,Hystrix会放行该服务的一部分申请,再次统计它的申请失败率。如果此时申请失败率合乎预设值,则齐全关上限流开关;如果申请失败率依然很高,那么持续回绝该服务的所有申请。这就是所谓的“限流”。而Hystrix向那些被回绝的申请间接返回一个预设后果,被称为“降级”。 2.2.2 罕用和举荐应用的形式: 定义对立fallback接口pom.xml依赖 <!--Hystrix断路器--><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency>application.properties配置文件 #指定运行端口server.port=8200#服务名称spring.application.name=order#获取注册实例列表eureka.client.fetch-registry=true#注册到Eureka的注册核心eureka.client.register-with-eureka=true#配置注册核心地址#eureka.client.zhang.service-url.defaultZone=http://localhost:8001/eureka/eureka.client.service-url.defaultZone=http://localhost:8000/eureka/ #feign客户端建设连贯超时工夫feign.client.config.default.connect-timeout=10000#feign客户端建设连贯后读取资源超时工夫feign.client.config.default.read-timeout=10000 #开启Hystrix断路器feign.hystrix.enabled=true#配置Hystrix 超时工夫 超时敞开#hystrix.command.default.execution.timeout.enabled=false#超时工夫(默认1000ms)在调用方配置,被该调用方的所有办法的超时工夫都是该值,优先级低于下边的指定配置hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000#在调用方配置,被该调用方的指定办法(HystrixCommandKey办法名)的超时工夫是该值hystrix.command.HystrixCommandKey.execution.isolation.thread.timeoutInMilliseconds=4000#线程池外围线程数 默认为10hystrix.threadpool.default.coreSize=10#最大排队长度。默认-1 如果要从-1换成其余值则需重启,即该值不能动静调整,若要动静调整,须要应用到下边这个配置hystrix.threadpool.default.maxQueueSize=100#排队线程数量阈值,默认为5,达到时回绝,如果配置了该选项,队列的大小是该队列hystrix.threadpool.default.queueSizeRejectionThreshold=5# 简言之,10s内申请失败数量达到20个,断路器开。 当在配置工夫窗口内达到此数量的失败后,进行短路。默认20个hystrix.command.default.circuitBreaker.requestVolumeThreshold=20#短路多久当前开始尝试是否复原,默认5shystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5#出错百分比阈值,当达到此阈值后,开始短路。默认50%hystrix.command.default.circuitBreaker.errorThresholdPercentage=50%#调用线程容许申请HystrixCommand.GetFallback()的最大数量,默认10。超出时将会有异样抛出,留神:该项配置对于THREAD隔离模式也起作用hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests=50000定义Feign调用接口,和新建对立fallback解决类并实现Feign调用接口 ...

July 20, 2020 · 2 min · jiezi

关于redis:初学redis必须要掌握的

欢送学习交换:php高级学习Redis是一个开源的应用ANSI C语言编写、反对网络、可基于内存亦可长久化的日志型、Key-Value数据库,并提供多种语言的API。 redis 开源文档 [http://www.redis.cn/documenta...](http://www.redis.cn/documenta... 设置redis 明码 ~~~ vi /usr/local/redis/etc/redis.conf 500 行左右 requirepass 123456 ~~~ Redis反对的数据类型有 Stirng(字符串), List(列表), Hash(字典), Set(汇合), Sorted Set(有序汇合); 连贯: ~~~ //实例化redis $redis = new Redis(); //连贯 $redis->connect('127.0.0.1', 6379); //检测是否连贯胜利 echo "Server is running: " . $redis->ping(); // 输入后果 Server is running: +PONG ~~~ Strng(字符串): ~~~ // 设置一个字符串的值 $redis->set('cat', 111); //获取一个字符串的值 echo $redis->get('cat'); // 111 // 反复set $redis->set('cat', 222); echo $redis->get('cat'); // 222 ...

July 20, 2020 · 7 min · jiezi

关于redis:在Redis中设置了过期时间的Key需要注意哪些问题

作者:千山qianshanjuejin.im/post/5d6bda096fb9a06acc009dc8相熟Redis的同学应该晓得,Redis的每个Key都能够设置一个过期工夫,当达到过期工夫的时候,这个key就会被主动删除。 在为key设置过期工夫须要留神的事项1、 DEL/SET/GETSET等命令会革除过期工夫 在应用DEL、SET、GETSET等会笼罩key对应value的命令操作一个设置了过期工夫的key的时候,会导致对应的key的过期工夫被革除。 //设置mykey的过期工夫为300s127.0.0.1:6379> set mykey hello ex 300OK//查看过期工夫127.0.0.1:6379> ttl mykey(integer) 294//应用set命令笼罩mykey的内容127.0.0.1:6379> set mykey ollehOK//过期工夫被革除127.0.0.1:6379> ttl mykey(integer) -12、INCR/LPUSH/HSET等命令则不会革除过期工夫 而在应用INCR/LPUSH/HSET这种只是批改一个key的value,而不是笼罩整个value的命令,则不会革除key的过期工夫。 INCR: //设置incr_key的过期工夫为300s 127.0.0.1:6379> set incr_key 1 ex 300 OK 127.0.0.1:6379> ttl incr_key (integer) 291 //进行自增操作 127.0.0.1:6379> incr incr_key (integer) 2 127.0.0.1:6379> get incr_key "2" //查问过期工夫,发现过期工夫没有被革除 127.0.0.1:6379> ttl incr_key (integer) 277LPUSH: //新增一个list类型的key,并增加一个为1的值 127.0.0.1:6379> LPUSH list 1 (integer) 1 //为list设置300s的过期工夫 127.0.0.1:6379> expire list 300 (integer) 1 //查看过期工夫 127.0.0.1:6379> ttl list (integer) 292 //往list外面增加值2 127.0.0.1:6379> lpush list 2 (integer) 2 //查看list的所有值 127.0.0.1:6379> lrange list 0 1 1) "2" 2) "1" //能看到往list外面增加值并没有使过期工夫革除 127.0.0.1:6379> ttl list (integer) 2523、PERSIST命令会革除过期工夫 当应用PERSIST命令将一个设置了过期工夫的key转变成一个长久化的key的时候,也会革除过期工夫。 127.0.0.1:6379> set persist_key haha ex 300 OK 127.0.0.1:6379> ttl persist_key (integer) 296 //将key变为长久化的 127.0.0.1:6379> persist persist_key (integer) 1 //过期工夫被革除 127.0.0.1:6379> ttl persist_key (integer) -14、应用RENAME命令,老key的过期工夫将会转到新key上 在应用例如:RENAME KEY_A KEY_B命令将KEY_A重命名为KEY_B,不论KEY_B有没有设置过期工夫,新的key KEY_B将会继承KEY_A的所有个性。 ...

July 20, 2020 · 1 min · jiezi

关于redis:Redis-RDB-AOF

Redis 有两种长久化计划,RDB (Redis DataBase)和 AOF (Append Only File) RDBRedis 默认的长久化计划。在指定的工夫距离内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis 重启会通过加载dump.rdb文件复原数据。 AOFRedis 默认不开启。它的呈现是为了补救RDB的有余(数据的不一致性),采纳日志的模式来记录每个写操作,并追加到文件中。Redis 重启的会依据日志文件的内容将写指令从前到后执行一次以实现数据的复原工作。

July 18, 2020 · 1 min · jiezi

关于redis:Redis-面试常见问答

本文出自:https://thinkinjava.cn作者:莫那 鲁道 1. 什么是缓存雪崩?怎么解决? 通常,咱们会应用缓存用于缓冲对 DB 的冲击,如果缓存宕机,所有申请将间接打在 DB,造成 DB 宕机——从而导致整个零碎宕机。 如何解决呢? 2 种策略(同时应用): 对缓存做高可用,避免缓存宕机应用断路器,如果缓存宕机,为了避免零碎全副宕机,限度局部流量进入 DB,保障局部可用,其余的申请返回断路器的默认值。2. 什么是缓存穿透?怎么解决?解释 1:缓存查问一个没有的 key,同时数据库也没有,如果黑客大量的应用这种形式,那么就会导致 DB 宕机。 解决方案:咱们能够应用一个默认值来避免,例如,当拜访一个不存在的 key,而后再去拜访数据库,还是没有,那么就在缓存里放一个占位符,下次来的时候,查看这个占位符,如果产生时占位符,就不去数据库查问了,避免 DB 宕机。 解释 2:大量申请查问一个刚刚生效的 key,导致 DB 压力倍增,可能导致宕机,但实际上,查问的都是雷同的数据。 解决方案:能够在这些申请代码加上双重查看锁。然而那个阶段的申请会变慢。不过总比 DB 宕机好。 3. 什么是缓存并发竞争?怎么解决?解释:多个客户端写一个 key,如果程序错了,数据就不对了。然而程序咱们无法控制。 解决方案:应用分布式锁,例如 zk,同时退出数据的工夫戳。同一时刻,只有抢到锁的客户端能力写入,同时,写入时,比拟以后数据的工夫戳和缓存中数据的工夫戳。 4.什么是缓存和数据库双写不统一?怎么解决?解释:间断写数据库和缓存,然而操作期间,呈现并发了,数据不统一了。 通常,更新缓存和数据库有以下几种程序: 先更新数据库,再更新缓存。先删缓存,再更新数据库。先更新数据库,再删除缓存。三种形式的优劣来看一下: 先更新数据库,再更新缓存。 这么做的问题是:当有 2 个申请同时更新数据,那么如果不应用分布式锁,将无法控制最初缓存的值到底是多少。也就是并发写的时候有问题。 先删缓存,再更新数据库。 这么做的问题:如果在删除缓存后,有客户端读数据,将可能读到旧数据,并有可能设置到缓存中,导致缓存中的数据始终是老数据。 有 2 种解决方案: 应用“双删”,即删更删,最初一步的删除作为异步操作,就是避免有客户端读取的时候设置了旧值。应用队列,当这个 key 不存在时,将其放入队列,串行执行,必须等到更新数据库结束能力读取数据。总的来讲,比拟麻烦。 先更新数据库,再删除缓存 这个理论是罕用的计划,然而有很多人不晓得,这里介绍一下,这个叫 Cache Aside Pattern,老外创造的。如果先更新数据库,再删除缓存,那么就会呈现更新数据库之前有霎时数据不是很及时。 同时,如果在更新之前,缓存刚好生效了,读客户端有可能读到旧值,而后在写客户端删除完结后再次设置了旧值,十分偶合的状况。 有 2 个前提条件:缓存在写之前的时候生效,同时,在写客户度删除操作完结后,搁置旧数据 —— 也就是读比写慢。设置有的写操作还会锁表。 所以,这个很难呈现,然而如果呈现了怎么办?应用双删!!!记录更新期间有没有客户端读数据库,如果有,在更新完数据库之后,执行提早删除。 还有一种可能,如果执行更新数据库,筹备执行删除缓存时,服务挂了,执行删除失败怎么办??? 这就坑了!!!不过能够通过订阅数据库的 binlog 来删除。 ...

July 17, 2020 · 1 min · jiezi

Memcached与Redis有什么区别

Redis 和 Memcached 都是基于内存的数据存储系统。Memcached是高性能分布式内存缓存服务,其本质上就是一个内存key-value数据库。Redis是一个开源的key-value存储系统。与Memcached相似,Redis将大部分数据存储在内存中,反对的数据类型包含:字符串、哈希表、链表、汇合、有序汇合以及基于这些数据类型的相干操作。那么,Memcached与Redis有什么区别呢? 1、数据操作不同 与Memcached仅反对简略的key-value构造的数据记录不同,Redis反对的数据类型要丰盛得多。Memcached根本只反对简略的key-value存储,不反对枚举,不反对长久化和复制等性能。Redis反对服务器端的数据操作相比Memcached来说,领有更多的数据结构和并反对更丰盛的数据操作,反对list、set、sorted set、hash等泛滥数据结构,还同时提供了长久化和复制等性能。而通常在Memcached里,使用者须要将数据拿到客户端来进行相似的批改再set回去,这大大增加了网络IO的次数和数据体积。在Redis中,这些简单的操作通常和个别的GET/SET一样高效。所以,如果须要缓存可能反对更简单的构造和操作, Redis会是更好的抉择。 2、内存管理机制不同 在Redis中,并不是所有的数据都始终存储在内存中的。这是和Memcached相比一个最大的区别。当物理内存用完时,Redis能够将一些很久没用到的value替换到磁盘。Redis只会缓存所有的key的信息,如果Redis发现内存的使用量超过了某一个阀值,将触发swap的操作,Redis依据“swappability = age*log(size_in_memory)”计算出哪些key对应的value须要swap到磁盘。而后再将这些key对应的value长久化到磁盘中,同时在内存中革除。这种个性使得Redis能够放弃超过其机器自身内存大小的数据。 而Memcached默认应用Slab Allocation机制治理内存,其次要思维是依照预先规定的大小,将调配的内存宰割成特定长度的块以存储相应长度的key-value数据记录,以齐全解决内存碎片问题。 从内存利用率来讲,应用简略的key-value存储的话,Memcached的内存利用率更高。而如果Redis采纳hash构造来做key-value存储,因为其组合式的压缩,其内存利用率会高于Memcached。 3、性能不同 因为Redis只应用单核,而Memcached能够应用多核,所以均匀每一个核上Redis在存储小数据时比Memcached性能更高。而在100k以上的数据中,Memcached性能要高于Redis,尽管Redis也在存储大数据的性能上进行了优化,然而比起Memcached,还是稍有逊色。 4、集群治理不同 Memcached是全内存的数据缓冲零碎,Redis尽管反对数据的长久化,然而全内存毕竟才是其高性能的实质。作为基于内存的存储系统来说,机器物理内存的大小就是零碎可能包容的最大数据量。如果须要解决的数据量超过了单台机器的物理内存大小,就须要构建分布式集群来扩大存储能力。 Memcached自身并不反对分布式,因而只能在客户端通过像一致性哈希这样的分布式算法来实现Memcached的分布式存储。相较于Memcached只能采纳客户端实现分布式存储,Redis更偏差于在服务器端构建分布式存储。 小结:Redis和Memcached哪个更好? Redis更多场景是作为Memcached的替代者来应用,当须要除key-value之外的更多数据类型反对或存储的数据不能被剔除时,应用Redis更适合。如果只做缓存的话,Memcached曾经足够应酬绝大部分的需要,Redis 的呈现只是提供了一个更加好的抉择。总的来说,依据使用者本身的需要去抉择才是最合适的。 It’s not uncommon to hear Redis compared to memcached, which is a very high-performance, key-value cache server. Like memcached, Redis can also store a mappingof keys to values and can even achieve similar performance levels as memcached. But the similarities end quickly—Redis supports the writing of its data to disk automaticallyin two different ways, and can store data in four structures in addition to plain string keysas memcached does. These and other differences allow Redis to solve a wider range ofproblems, and allow Redis to be used either as a primary database or as an auxiliary data-base with other storage systems.

July 14, 2020 · 1 min · jiezi

Redis概要介绍

本文参考: Redis中武官网Redis英文官网本文相干好文: Redis主从复制,集群复制配置Redis单点、主从、sentinel、clusterJava3y Redis相干概念与策略Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和音讯队列代理。它反对字符串、哈希表、列表、汇合、有序汇合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU发出、事务以及不同级别磁盘长久化性能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供主动分区。以上摘自官网。 能够看出Redis是基于内存存储的,同时反对不同策略的磁盘长久化。 数据结构有:string,hash,list,set,sorted set。还反对高级数据类型:Geo(地理位置),HyperLogLog(基数预估),Pub/Sub(公布/订阅)。 反对的性能有:复制、脚本、连贯、事务。能够通过Redis Sentinel进步可用性,能够通过Redis Cluster提供主动分区。 Redis的server是单线程服务器,基于Event-Loop模式来解决申请。单线程模型能够不必思考线程平安问题,同时防止线程切换耗费额定的工夫。Redis event libRedis event loop剖析最初,待我看了《Redis 实战》,再写相干文章进行梳理。

July 13, 2020 · 1 min · jiezi

redis哨兵模式从节点的哨兵之间不能通信

1.问题从节点中主动生成的配置文件中,发现从节点的哨兵只能和主节点的哨兵进行通信,和从节点的哨兵不能通信 2.解决办法在从节点的哨兵配置文件中sentinels monitor 监督的不是从节点的端口,也应该是主节点的端口 sentinel monitor xiayu_master 192.168.1.108 6379 2

July 12, 2020 · 1 min · jiezi

redis-超时时间小记

问题:redis中已存在key-value对,并对其设置超时工夫事 如果再对该key设置新值,超时工夫会怎么变动? 通过试验,论断:从新笼罩后将失去超时工夫。

July 10, 2020 · 1 min · jiezi

mac-redis-安装启动

次要参考这篇文章 https://www.jianshu.com/p/bb7c19c5fc47当后续电脑关机后,重开,redis被断开或呈现以下报错执行 redis-server重启服务

July 9, 2020 · 1 min · jiezi

Redis教程string字符串数据类型

注:如有侵权,请分割删除 字符串类型: String类型是Redis能与键关联的最简略的数据类型,它是Memcached当中仅有的数据类型,因而能够很快地被初学者学习。Redis的Key名称也是一个字符串,当咱们应用字符串类型作为其对应的值时,咱们能够依据Key名称来查找映射对应的值。Redis字符串是二进制平安的,这意味着一个Redis字符串能蕴含任意类型的数据,例如: 一张JPEG格局的图片或者一个序列化的Ruby对象。一个字符串类型的值最多能存储512MB的内容。利用场景: 高速缓存HTML片段或者页面;高速缓存关系型数据库查问的数据后果;高速缓存会话控制数据;分布式锁;避免反复提交;存储设置固定格局的字符串序列(例如:工夫序列)统计网站访问者数量;每天注册用户数;限度API在某一时段的拜访次数;用户签到;统计沉闷用户;用户在线状态。Redis字符串类型的根本命令Redis字符串类型键的设置Redis字符串类型键的查问Redis字符串类型键的计数操作Redis字符串类型键的二进制操作Redis字符串类型键的设置我的项目案例:案例一:高速缓存(HTML / DATA / SESSION)案例二:分布式锁案例三:避免反复提交案例四:存储设置固定格局的字符串序列(例如:工夫序列) 命令名称:set 语法: set key value [EX seconds] [PX milliseconds] [NX|XX]性能:给一个key增加字符串类型的值,如果该key曾经存在,值会被新值笼罩,不论是什么类型的key。SET设置后的键,之前的生存工夫会被抛弃。选项:EX seconds(生存工夫:秒) / PX milliseconds(生存工夫:毫秒) / NX(仅在键不存在时设置) / XX(仅在键存在时设置)返回值:如果设置胜利,返回OK。如果设置失败,返回nil。命令名称:mset 语法: mset key value [key value ...]性能:同时设置多个key,如果key存在会笼罩。mset 是原子的,所有键会同时设置胜利或者失败。返回值:胜利返回ok命令名称:setex 语法: setex key seconds value性能:给一个键设置为字符串类型,并指定生存工夫(单位:秒)。该命令是原子的,如果设置失败或者指定生存工夫失败,会复原初始状态。返回值:如果设置胜利,返回OK。如果设置失败,返回错误信息。命令名称:psetex 语法: psetex key milliseconds value性能:给一个键设置为字符串类型,并指定生存工夫(单位:毫秒)。该命令是原子的,如果设置失败或者指定生存工夫失败,会复原初始状态。返回值:如果设置胜利,返回OK。如果设置失败,返回错误信息。命令名称:setnx 语法: setnx key value性能:如果key不存在,将其设置为字符串类型。返回值:如果设置胜利,返回1。如果设置失败,返回0。命令名称:msetnx 语法: msetnx key value [key value ...]性能:同时设置多个key,如果其中一个key存在则设置失败,不思考其余键是否存在。msetnx 是原子的,所有键会同时设置胜利或者失败。返回值:如果所有键设置胜利返回1,如果所有键设置失败返回0。命令名称:setrange 语法: setrange key offset value性能:批改或者设置一个键的字符串类型值的内容。如果键不存在,就设置一个新的,并且补充offset个NULL,再退出value,换句话说,值为 “offset个NULL+value”。如果键曾经存在,从该键值offset处开始插入value,如果offset的值大于该键字符串长度,用NULL补充到该长度,再开端插入value。返回:胜利返回字符串长度,失败返回错误信息注意事项:offset最大值229 -1(536870911)。命令名称:append 语法: append key value性能:如果key存在,则在前面追加value的内容。如果key不存在,会创立一个key,并设置其值为空字符串,并在后追加value的内容。返回:追加胜利当前的字符串长度。Redis字符串类型键的查问命令名称:get ...

July 9, 2020 · 2 min · jiezi

redis常用命令

Redis键的基本操作Redis键的过期操作Redis键的序列化操作Redis键的排序操作Redis键的迁移操作Redis键的基本操作 Redis键的键名查询Redis键的类型查询Redis键的重命名操作Redis键的修改最后访问时间Redis键的删除操作命令名称:keys 语法: keys patternPattern的用法:?* //全部[ae][^ae][a-c]功能:返回匹配模式的所有键名命令名称:exists 语法: exists key [key ...]功能:检查给定 key 是否存在返回值:1 代表存在0 代表不存在命令名称:scan 语法: scan cursor [MATCH pattern] [COUNT count]示例: scan 0 match key* count 3功能:SCAN 命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。返回值:完整遍历的数据命令名称:randomkey 语法: randomkey功能:从当前数据库随机返回一个键名。返回值:键名命令名称:type 语法: type key功能:查询键的类型返回值:返回存储在键的值的类型的字符串表示形式。 可以返回的不同类型是:string,list,set,zset和hash。命令名称:object 语法: object subcommand [arguments [arguments ...]]功能:从内部察看给定 key 的 Redis 对象。子命令:object refcount <key>object encoding <key>object idletime<key>返回值:refcount 和 idletime返回数字 / encoding 返回相应的编码类型。命令名称:rename 语法: rename key newkey功能:将 key 改名为 newkey。返回值:当 key 和 newkey 相同,或者 key 不存在时,返回一个错误。当 newkey 已经存在时, rename 命令将覆盖旧值。改名成功时提示 OK ,失败时候返回一个错误。命令名称:renamenx ...

July 8, 2020 · 2 min · jiezi

redis6-伪集群搭建

redis 伪集群搭建资源有限,在一台机器上搭建了6个节点的redis集群,记录搭建过程已经遇到的问题。在搭建过程中发现搭建方式和之前的版本差别挺大的 环境Centos7redis6redis命令启动、停止、重启./redis-server [conf]./redis-server stop./redis-server restart连接命令./redis-cli [-h] [-p] [-a]参数说明:-h连接ip(和配置文件中的bind属性有关),-p连接端口,-a密码(配置文件requirpass属性) 操作命令命令举例说明authauth 123456连接后认证selectselect 15选择数据库(redis单机版默认16个)keyskeys *查询所有keysetset name lisi保存key-valuegetget namekey获取valueflushall-清除所有数据 cluster nodes-查看节点info-内存使用情况slowlog getslowlog get 2获取2条慢记录集群搭建<!-- more --> 环境准备阶段redis6和之前的版本没差别 配置文件准备先修改redis基础配置文件redis.conf.default port 6379requirpass 123456 # 密码bind 192.168.1.17 # 绑定当前机器 IPcluster-enabled yes # 取消注释,启动集群模式cluster-config-file nodes-6379.conf # 取消注释cluster-node-timeout 15000 # 取消注释appendonly yes # 将 no 修改为 yes集群配置文件cd /usr/local/redis/confecho 9001.conf 9002.conf 9003.conf 9004.conf 9005.conf 9006.conf | xargs -n 1 cp -v redis.conf.defaultsed -i 's/6379/9001/g' 9001.conf sed -i 's/6379/9002/g' 9002.conf sed -i 's/6379/9003/g' 9003.conf sed -i 's/6379/9004/g' 9004.conf sed -i 's/6379/9005/g' 9005.conf sed -i 's/6379/9006/g' 9006.conf 配置完成后,各自启动./redis-server ../conf/9001.conf ...

July 7, 2020 · 1 min · jiezi

Redis的过期策略有哪些

我们一般都用内存作为缓存,但是内存是无限的吗?当然不是,内存是很宝贵且有限的自然。 可能一台机器也就十几G的内存,但是却可以挂几个T,十几个T的硬盘空间,Reids主要是基于内存做高性能,高并发的读写操作的。 那么问题来了?既然内存时有限的,比如Redis就只给分配了10G的内存,你却要往里硬怼20G的数据,会咋办?毫无疑问,当然会被干掉10G的数据了,那问题又来了,干掉那些数据?保留那些数据?我们当然希望干掉那些不常用的,保留常用的数据了。 所以说,这是缓存的一种基本概念,数据是会过期的,要么你自己设置过期时间,要么在满足某些条件下Redis帮你干掉。 如果自己设置了过期时间,那你知道Redis是怎么给你弄过期的吗?什么时候被删除的? 比如我们在set key时可以给定一个expire time就是过期时间,比如指定这个key的过期时间为1小时?10分钟? 这个很有用的,我们自己控制缓存的存活时间。 假如你设置了一批key只能存活1小时,那么一小时后,Redis是如何对这一批key进行删除的尼?请往下看! 定期删除+惰性删除所谓定期删除指的是Redis默认会每隔一定时间(默认100ms)就会抽取一批设置了过期时间的key来检测是否过期,过期就删除。假设你Redis存放了100万key都设置了过期时间,你每隔几百毫秒,就检查100万key,那Reids基本就挂了, cup负载会飙升,都消耗在你检测过期key上了。注意⚠️!这里可不是每隔100ms就遍历所有的设置了过期的key,那样将是一场性能上的灾难。实际上Redis是每隔100ms随机的抽取一部分设置过期时间的key来检测和删除。 但是问题来了?? 定期删除可能会导致一部分过期了的key时间到了却并没有被删除,怎么办?别着急,这就该我们的惰性删除登场了。 惰性删除,就是你在获取某个Key时Redis会先检测一下,这个key是否设置了过期时间?如果设置了过期时间那么是否过期?过期就删除。 通过上述两种手段基本上可以保证过期的key一定会被干掉。 但是实际问题比这复杂多了,比喻定期删除漏掉了很多过期的key,然后也没及时的去查也就没有走惰性删除,此时会怎么样?大批的过期key躺在内存了,导致Redis内存很快别被耗尽,咋办?咋办?咋办? 别急! 这种情况会走内存淘汰机制 内存淘汰如果Redis的内存占用过多的时候,此时Redis会进行内存淘汰,如何淘汰?会有如下几种淘汰策略。 noeviction: 当内存不足以容纳新写入的数据时,新写入数据时会报错。这个一般不会有人使用,感觉太????人了。allkeys-lru:当内存不足以容纳新写入的数据时,在键空间中,移除最近最少使用的key.(这也是我们经常使用的策略)allkes-random:当内存不足以容纳新写入的数据时,在键空间中,随机移除某个key。(这个正常人都不会使用吧,为啥要随机?肯定移除最近最少使用的key呀)volatile-lru:当内存不足以容纳新写入的数据时,在设置了过期时间的键空间中,移除最近最少使用的key。(这个一般也不太合适)volatile-random:当内存不足以容纳新写入的数据时,在设置了过期时间的键空间中,随机移除某个key。volatile-ttl:当内存不足以容纳新写入的数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。(这个也不合理)到这Redis的过期策略基本介绍完了。也就三把 ???? ???? ???? ????定期删除+????惰性删除+????内存淘汰(最常用的allkeys-lru)

July 7, 2020 · 1 min · jiezi

Redis命令性能优化及事务使用过程

场景`假设有这样一个使用场景,依次执行下面的5条命令 命令1:hset mall:sale:freq:ctrl:860000000000001 599055114591 1(hash结构,field表示购买的商品ID,value表示购买次数) 简单说明: mall:sale:freq:ctrl:860000000000001是一个hash表;599055114591表示key;1表示key对应的value 命令2:hset mall:sale:freq:ctrl:860000000000001 599055114592 2命令3:expire mall:sale:freq:ctrl:860000000000001 3127(设置过期时间) 简单说明:给hash表mall:mall:sale:freq:ctrl:860000000000001设置过期时间 命令4:set mall:total:freq:ctrl:860000000000001 3 简单说明:set key vlaue 命令5:expire mall:total:freq:ctrl:860000000000001 3127(设置过期时间) 简单说明: set key 过期时间` 优化缘由执行一条命令 经历的过程 发送命令网络传输时间命令在Redis服务端队列中等待的时间命令执行的时间(Redis中的slowlog只是检测这一步骤的时间)结果返回的Redis客户端的时间 `执行一条命令 就需要经过上面的过程,发送命令-〉命令排队-〉命令执行-〉返回结果那么执行5条命令,可想而知,性能优化的空间还是蛮大的,下面咱们来进行优化一下吧` 优化第一次优化:利用hmset命令将两条hmset命令合二为一`命令1和命令2 合二为一 hmset mall:sale:freq:ctrl:860000000000001 599055114591 1 599055114592 2 expire mall:sale:freq:ctrl:860000000000001 3127 set mall:total:freq:ctrl:860000000000001 3 expire mall:total:freq:ctrl:860000000000001 3127` 第二次优化:将set和expire命令合二为一`将命令4和命令5合二为一 命令a: hmset mall:sale:freq:ctrl:860000000000001 599055114591 1 599055114592 2命令b: expire mall:sale:freq:ctrl:860000000000001 3127 命令c: setex mall:total:freq:ctrl:860000000000001 3127 3` ...

July 7, 2020 · 2 min · jiezi

Ubuntu1804安装Redis

1、更新软件包sudo apt-get update2、安装sudo apt install redis-server命令执行完毕即安装成功并已经启动。3、检查是否已安装并启动ps aux | grep redis 此时的默认配置只能本机访问,并且处于保护模式,需要修改配置。4、修改配置注释 bind 127.0.0.1 ::1,使其支持远程访问。protected-mode属性值改为no,关闭保护模式。5、重启服务sudo service redis-server restart

July 4, 2020 · 1 min · jiezi

5000字硬核干货Redis-分布式集群部署实战

原理:Redis集群采用一致性哈希槽的方式将集群中每个主节点都分配一定的哈希槽,对写入的数据进行哈希后分配到某个主节点进行存储。集群使用公式(CRC16 key)& 16384计算键key数据那个槽。16384个slot均匀分布在各个节点上。集群中每个主节点将承担一部分槽点的维护,而槽点中存储着数据,每个主节点都有至少一个从节点用于高可用。节点通信方式:开启一个端口 设置的端口号+10000,用于集群之间节点通信交换信息。每个节点默认每秒10次选择随机5个节点发送ping消息,将自身信息和知道的集群信息传递,收到ping消息后返回pong消息做回复,最后通过这种随机的消息交换,最终每个节点将获得所有信息。当某个主节点挂掉,所有节点将会发现主节点挂掉了,作为主节点的从节点,就会接替主节点的工作,然后告诉所有其它节点,他成为了主。这样其它存活节点,就将它们维护的信息表更新从节点将接任做主,如果都挂掉集群将报错。当从一个节点操作,根据一致性哈希计算后将存储在其中一个主节点中,从节点将同步主的数据。 redis cluster是去中心化的,集群中的每个节点都是平等的关系,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃。搭建集群时,会为每一个分片的主节点,对应一个从节点。实现slaveof功能,同时当主节点down,实现sentinel哨兵的自动failover切换功能Redis分布式集群(部署):端口号:7000-7005 本次分布式分片集群在一台LInux系统即可,只需要安装多个实例作为集群配置。 安装ruby环境支持:yum -y install ruby rubygemsyum安装2.0.0版本,但是gem需要2.2.2版本以上,所以选择编译 下载并安装ruby环境:wget https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.1.tar.gztar xf ruby-2.6.1.tar.gz && cd ruby-2.6.1/./configure --prefix=/usr/local/rubymake && make install && echo $?echo 'export PATH=$PATH:/usr/local/ruby/bin' >> /etc/profilesource /etc/profile修改gem工具国内源:# 查看gem工具源地址gem sources -l# 添加一个阿里云的gem工具源gem sources -a http://mirrors.aliyun.com/rubygems/# 删除gem工具默认国外源gem sources --remove https://rubygems.org/# 下载当前最新版本集群插件gem install redis -v 4.1.0集群节点准备:mkdir /data/700{0..5}配置7000端口实例:vim /data/7000/redis.confport 7000daemonize yespidfile /data/7000/redis.pidloglevel noticelogfile "/data/7000/redis.log"dbfilename dump.rdbdir /data/7000protected-mode nocluster-enabled yescluster-config-file nodes.confcluster-node-timeout 5000appendonly yes拷贝其他端口实例:# 拷贝配置文件cp /data/7000/redis.conf /data/7001/cp /data/7000/redis.conf /data/7002/cp /data/7000/redis.conf /data/7003/cp /data/7000/redis.conf /data/7004/cp /data/7000/redis.conf /data/7005/# 修改配置文件内容sed -i 's#7000#7001#g' /data/7001/redis.confsed -i 's#7000#7002#g' /data/7002/redis.confsed -i 's#7000#7003#g' /data/7003/redis.confsed -i 's#7000#7004#g' /data/7004/redis.confsed -i 's#7000#7005#g' /data/7005/redis.conf启动所有实例:redis-server /data/7000/redis.confredis-server /data/7001/redis.confredis-server /data/7002/redis.confredis-server /data/7003/redis.confredis-server /data/7004/redis.confredis-server /data/7005/redis.conf创建命令软链接:(这个命令过期了,现在使用redis-cli命令)(可选执行命令)ln -s /usr/local/redis-5.0.2/src/redis-trib.rb /usr/sbin/查看进程:ps -ef |grep redis-server 加入所有实例节点到集群管理:# --replicas 1",1是代表每一个主有一个从,后面的是所有节点的地址与端口信息redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005分布式主从规则为,前三个实例节点是主,对应的后面三个实例节点为从节点,如果replicas 2,那就多加3个实例节点 查看主节点状态:redis-cli -p 7000 cluster nodes|grep master 查看从节点状态:redis-cli -p 7000 cluster nodes|grep slave Redis-分布式集群(管理)集群节点增加准备:mkdir /data/700{6..7}拷贝其他端口实例:# 拷贝配置文件cp /data/7000/redis.conf /data/7006/cp /data/7000/redis.conf /data/7007/# 修改配置文件内容sed -i 's#7000#7006#g' /data/7006/redis.confsed -i 's#7000#7007#g' /data/7007/redis.conf启动新节点实例: ...

July 3, 2020 · 1 min · jiezi

Redis-创始人宣布退居二线不再进行项目的日常代码维护

近日,Redis 创始人 Salvatore Sanfilippo 在个人博客发表名为《The end of the Redis adventure 》的博文,在文中表示将退出代码的日常开发,以后只充当该项目的顾问。 对于这一决定,他给出的理由之一是“我写代码是为了表达自己,而现在我的大部分精力都花在检查其他人提交的代码,但我从不想成为软件维护者。” Redis 和 @antirezRedis 是一个基于 BSD 开源的项目,是一个把结构化的数据放在内存中的一个存储系统,你可以把它作为数据库,缓存和消息中间件来使用。同时支持 strings,lists,hashes,sets,sorted sets,bitmaps,hyperloglogs和geospatial indexes 等数据类型。它还内建了复制,lua 脚本,LRU,事务等功能,通过 redis sentinel 实现高可用,通过 redis cluster 实现了自动分片。以及事务,发布/订阅,自动故障转移等等。 Redis 之父 Salvatore Sanfilippo,一名意大利程序员,大家更习惯称呼他 Antirez。 早在十年前,Redis 还只是一个内部项目,没有任何开源的计划。它不过是用来解决 web 应用程序扩容时遇到的一系列让人棘手的问题而已(应该是用户会话缓存)。 但 Antirez 在当时就已经是一个在开源领域活跃了十年的元老了,所以在 Redis 体现出价值之后,便将它开源了。 开源项目维护者的挣扎与无奈随着 Redis 开源社区规模越来越大,越来越多的人开始在社区进行分享讨论。但这对 Antirez 来说却是一个”甜蜜的烦恼“。作为一个兼职业余的工作,Redis 开始占据他越来越多的精力和时间,甚至开始影响到了他的本职工作。并且 Redis 也变得越来越复杂。 2019 年的时候,Antirez 就曾发布公开信表示作为一名开源项目维护者的挣扎和无奈。他经过反复的思索和自我分析,坦承的表示“维护一个开源项目会带来乐趣”,但“也有消极的一面”。 当一个项目达到像 Redis 这样的流行程度,并且人与人之间的交流因为新的社交工具而变得更为容易时,作者收到的消息、issue、PR 和建议的数量也将呈指数增长。 自从 Redis 流行起来之后,Antirez 不得不进行更多查看 PR 和 issue 的工作,但这给了他很多的压力和困惑。 ...

July 1, 2020 · 1 min · jiezi

Redis发布订阅与Stream

Redis-发布订阅与Stream授权转载自: https://github.com/wmyskxz/Mo...一、Redis 中的发布/订阅功能发布/ 订阅系统 是 Web 系统中比较常用的一个功能。简单点说就是 发布者发布消息,订阅者接受消息,这有点类似于我们的报纸/ 杂志社之类的: (借用前边的一张图) 图片引用自:「消息队列」看过来! - https://www.wmyskxz.com/2019/...从我们 前面(下方相关阅读) 学习的知识来看,我们虽然可以使用一个 list 列表结构结合 lpush 和 rpop 来实现消息队列的功能,但是似乎很难实现实现 消息多播 的功能: 为了支持消息多播,Redis 不能再依赖于那 5 种基础的数据结构了,它单独使用了一个模块来支持消息多播,这个模块就是 PubSub,也就是 PublisherSubscriber (发布者/ 订阅者模式)。 PubSub 简介我们从 上面的图 中可以看到,基于 list 结构的消息队列,是一种 Publisher 与 Consumer 点对点的强关联关系,Redis 为了消除这样的强关联,引入了另一种概念:频道 (channel): 当 Publisher 往 channel 中发布消息时,关注了指定 channel 的 Consumer 就能够同时受到消息。但这里的 问题 是,消费者订阅一个频道是必须 明确指定频道名称 的,这意味着,如果我们想要 订阅多个 频道,那么就必须 显式地关注多个 名称。 为了简化订阅的繁琐操作,Redis 提供了 模式订阅 的功能 Pattern Subscribe,这样就可以 一次性关注多个频道 了,即使生产者新增了同模式的频道,消费者也可以立即受到消息: ...

July 1, 2020 · 7 min · jiezi

Redis的过期策略和内存淘汰策略最全总结与分析

文章前言提到内存管理,我们就需要考虑Redis的内存过期策略和内存淘汰机制。该文章便从这两方面入手,分享一些在Redis内存方面相关的基础知识。 文章中使用的示例版本为Redis5.0版本。内存过期策略内存过期策略主要的作用就是,在缓存过期之后,能够及时的将失效的缓存从内存中删除,以减少内存的无效暂用,达到释放内存的目的。 过期策略分类Redis内存过期策略分为三类,定时策略、惰性策略和定期策略。 定时策略含义:在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除。 优点:保证内存被尽快释放,减少无效的缓存暂用内存。 缺点:若过期key很多,删除这些key会占用很多的CPU时间,在CPU时间紧张的情况下,CPU不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些key。定时器的创建耗时,若为每一个设置过期时间的key创建一个定时器(将会有大量的定时器产生),性能影响严重。一般来说,是不会选择该策略模式。 惰性策略含义:key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期,则删除,返回null。 优点:删除操作只发生在从数据库取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步(如果此时还不删除的话,我们就会获取到了已经过期的key了)。 缺点:若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,此时的无效缓存是永久暂用在内存中的,那么可能发生内存泄露(无用的垃圾占用了大量的内存)。 定期策略含义:每隔一段时间对设置了缓存时间的key进行检测,如果可以已经失效,则从内存中删除,如果未失效,则不作任何处理。 优点:通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用--处理"定时删除"的缺点定期删除过期key--处理"惰性删除"的缺点。 缺点:在内存友好方面,不如"定时删除",因为是随机遍历一些key,因此存在部分key过期,但遍历key时,没有被遍历到,过期的key仍在内存中。在CPU时间友好方面,不如"惰性删除",定期删除也会暂用CPU性能消耗。 难点:合理设置删除操作的执行时长(每次删除执行多长时间)和执行频率(每隔多长时间做一次删除)(这个要根据服务器运行情况来定了) 该方式不是去便利所有的ky,而是随机抽取一些key做过期检测。 策略注意事项过期策略对持久化存储的影响持久化存储,指的是将内存的缓存永久存在磁盘中。也就是说我们的AOF和RDB持久化存储方式。因为该两种方式,将内存中的数据写入磁盘,这时候就需要考虑到我们过期的缓存是否会被写入到磁盘中?如果写入磁盘又是怎么处理的? RDB持久化持久化key之前,会检查是否过期,过期的key不进入RDB文件。 数据载入数据库之前,会对key先进行过期检查,如果过期,不导入数据库(主库情况)。 AOF持久化当key过期后,还没有被删除,此时进行执行持久化操作(该key是不会进入aof文件的,因为没有发生修改命令)。 当key过期后,在发生删除操作时,程序会向aof文件追加一条del命令(在将来的以aof文件恢复数据的时候该过期的键就会被删掉)。 因为AOF方式,向存储文件追加的是Redis的操作命令,而不是具体的数据,然而RDB确是存储的安全的二进制内容。重写时,会先判断key是否过期,已过期的key不会重写到aof文件。 即使在重写时,不验证是否过期,然而追加了del命令,测试无效的key同样会被删除。判断的情况是为了防止没有加入del命令的key。内存淘汰机制定义说明内存淘汰机制针对是内存不足的情况下的一种Redis处理机制。例如,当前的Redis存储已经超过内存限制了,然而我们的业务还在继续往Redis里面追加缓存内容,这时候Redis的淘汰机制就起到作用了。 淘汰机制分类根据redis.conf的配置文件中,我们可以得出,主要分为如下六种淘汰机制。 # volatile-lru -> Evict using approximated LRU among the keys with an expire set.# allkeys-lru -> Evict any key using approximated LRU.# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.# allkeys-lfu -> Evict any key using approximated LFU.# volatile-random -> Remove a random key among the ones with an expire set.# allkeys-random -> Remove a random key, any key.# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)# noeviction -> Don't evict anything, just return an error on write operations.这六种机制主要是什么意思内,下面是分别针对上面的几种机制做一个说明。 ...

July 1, 2020 · 4 min · jiezi

Centos安装Redis

一、安装redis第一步:下载redis安装包wget http://download.redis.io/releases/redis-4.0.6.tar.gz第二步:解压压缩包tar -zxvf redis-4.0.6.tar.gz第三步:yum安装gcc依赖yum install gcc第四步:跳转到redis解压目录下cd redis-4.0.6第五步:编译安装 make MALLOC=libc二、启动redis的三种方式 cd src1、直接启动redis./redis-server 2、以后台进程方式启动redis第一步:修改redis.conf文件将daemonize no修改为daemonize yes 第二步:指定redis.conf文件启动 ./redis-server /usr/local/redis-4.0.6/redis.conf3、设置redis开机自启动在/etc目录下新建redis目录 cd /etcmkdir redis2、将/usr/local/redis-4.0.6/redis.conf 文件复制一份到/etc/redis目录下,并命名为6379.conf cp /usr/local/redis-4.0.6/redis.conf /etc/redis/6379.conf3、将redis的启动脚本复制一份放到/etc/init.d目录下 cp /usr/local/redis-4.0.6/utils/redis_init_script /etc/init.d/redisd4、设置redis开机自启动 先切换到/etc/init.d目录下 然后执行自启命令 chkconfig redisd on看结果是redisd不支持chkconfig 解决方法: 使用vim编辑redisd文件,在第一行加入如下两行注释,保存退出 # chkconfig: 2345 90 10# description: Redis is a persistent key-value database注释的意思是,redis服务必须在运行级2,3,4,5下被启动或关闭,启动的优先级是90,关闭的优先级是10。 再次执行开机自启命令,成功 chkconfig redisd on现在可以直接已服务的形式启动和关闭redis了 service redisd start

June 30, 2020 · 1 min · jiezi

Redis教程超详细长期更新

长期更新Redis概述与介绍Redis的应用场景Redis的下载与安装Redis基础命令Redis数据类型的介绍Redis其他服务器命令Redis高级功能的使用Redis的优势极高的读写性能丰富的数据类型原子性操作支持主从热备丰富的特性 课程大纲高性能缓存缓存是Redis最常见的应用场景;Redis读写性能优异;取代memcached;缓存数据;缓存Page;缓存会话信息session等。多类型数据结构string;hash;list;set;sorted set;HyperLogLog;Pub/Sub。Redis的分布式锁Redis分布式;高并发下的数据一致性问题;单线程;用作分布式锁;性能优秀,不会成为性能瓶颈。自动过期Redis的键可以设置过期时长;一段时间以后自动删除。高并发和海量数据的处理支持主从热备,保证可用性;分片应用应对高并发的请求。数据持久化数据构建于内存当中;可进行缓存的设置;也可进行数据的持久化存储。string字符串数据类型Redis字符串类型基本概念及应用场景Redis字符串类型的基本命令 字符串类型:String类型是Redis能与键关联的最简单的数据类型,它是Memcached当中仅有的数据类型,因此可以很快地被初学者学习。Redis的Key名称也是一个字符串,当我们使用字符串类型作为其对应的值时,我们可以根据Key名称来查找映射对应的值。Redis字符串是二进制安全的,这意味着一个Redis字符串能包含任意类型的数据,例如: 一张JPEG格式的图片或者一个序列化的Ruby对象。一个字符串类型的值最多能存储512MB的内容。 应用场景:高速缓存HTML片段或者页面;高速缓存关系型数据库查询的数据结果;高速缓存会话控制数据;分布式锁;防止重复提交;存储设置固定格式的字符串序列(例如:时间序列);统计网站访问者数量;每天注册用户数;限制API在某一时段的访问次数;用户签到;统计活跃用户;用户在线状态。 命令名称:set语法: set key value [EX seconds] [PX milliseconds] [NX|XX]功能:给一个key添加字符串类型的值,如果该key已经存在,值会被新值覆盖,不论是什么类型的key。SET设置后的键,之前的生存时间会被丢弃。选项:EX seconds(生存时间:秒) / PX milliseconds(生存时间:毫秒) / NX(仅在键不存在时设置) / XX(仅在键存在时设置)返回值:如果设置成功,返回OK。如果设置失败,返回nil。

June 30, 2020 · 1 min · jiezi

redis605编译安装

一、安装gcc套装yum install cppyum install binutilsyum install glibcyum install glibc-kernheadersyum install glibc-commonyum install glibc-develyum install gccyum install make二、升级gccyum -y install centos-release-sclyum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutilsscl enable devtoolset-9 bash三、下载安装rediswget http://download.redis.io/releases/redis-6.0.5.tar.gztar -xzvf redis-6.0.5.tar.gz -C /usr/local/cd /usr/local/redis-6.0.5make && make install四、拷贝配置文件到指定位置并修改mkdir /etc/rediscp /usr/local/redis-6.0.5/redis.conf /etc/redis/6379.conf这里要这样配置是因为redis的redis_init_script的默认配置是这样,也可以不选择在这里创建新的配置文件而直接使用原位置的配置文件,这时就需要相应的修改redis_init_script的config配置。配置redis服务为守护进程 #将redis改为以守护进程的方式运行 vim /etc/redis/6379.confdaemonize yes五、配置服务到/etc/init.d中cp /usr/local/redis-6.0.5/utils/redis_init_script /etc/init.d/redis

June 29, 2020 · 1 min · jiezi

聊聊Redis和Memcached有啥区别

要说redis和memcached有啥区别这事吧?说起来就比较多,随着redis的功能越来越完善和强大,现在使用memcached的公司基本没有了,这里我们就来聊聊redis作者给出的几点区别吧。 1、redis支持服务端的数据操作redis相比memcached来说,拥有更多的数据结构并且支持丰富的数据操作。通常在memcached里,你需要将数据拿到客户端来进行类似的修改在set回去。这就大大增加网络IO的次数和数据的体积。在redis中,这些复杂的操作通常和一般的GET/SET一样高效。所以如果需要缓存能够支持更复杂的结构和操作,那么redis会是一个不错的选择。 2、内存使用率对比使用简单的key-value存储的话,memcached的内存使用率更高,而如果redis采用hash结构来做key-value存储的,由于某组合式的压缩,其内存使用率会高于memcached。 3、性能对比由于redis只使用单核,而memcached可以使用多核,所以平均每个核上redis在存储小数据时性能上比memcached性能高,而在100k以上的数据中memcached的性能高。虽然redis也在存储大数据的性能上进行优化,但是比起memcached来说还是稍逊一筹。 4、集群模式memcached没有原生的集群模式,需要依赖客户端来实现往集群中分片写入数据,而redis原生就支持claster模式的。 基本就这四点了,其实我觉得主要也就1、4比较重要点,其它的也就不是那么重要。 而且现在确实memcached慢慢淡出了人们的视野。

June 29, 2020 · 1 min · jiezi

Redis性能指标监控你知道多少

来源:https://blog.51cto.com/yht199...监控指标性能指标:Performance内存指标: Memory基本活动指标:Basic activity持久性指标: Persistence错误指标:Error 监控方式redis-benchmarkredis-statredis-fainaredisliveredis-climonitorshowlogget:获取慢查询日志len:获取慢查询日志条目数reset:重置慢查询日志相关配置: slowlog-log-slower-than 1000 # 设置慢查询的时间下线,单位:微秒slowlog-max-len 100 # 设置慢查询命令对应的日志显示长度,单位:命令数info(可以一次性获取所有的信息,也可以按块获取信息) server:服务器运行的环境参数clients:客户端相关信息memory:服务器运行内存统计数据persistence:持久化信息stats:通用统计数据Replication:主从复制相关信息CPU:CPU使用情况cluster:集群信息Keypass:键值对统计数量信息终端info命令使用 ./redis-cli info 按块获取信息 | grep 需要过滤的参数./redis-cli info stats | grep ops交互式info命令使用  #./redis-cli  > info server性能监控:redis-cli info | grep ops # 每秒操作数内存监控[root@CombCloud-2020110836 src]# ./redis-cli info | grep used | grep human      used_memory_human:2.99M  # 内存分配器从操作系统分配的内存总量used_memory_rss_human:8.04M  #操作系统看到的内存占用,top命令看到的内存used_memory_peak_human:7.77M # redis内存消耗的峰值used_memory_lua_human:37.00K   # lua脚本引擎占用的内存大小由于BLPOP,BRPOP,or BRPOPLPUSH而备阻塞的客户端 [root@CombCloud-2020110836 src]# ./redis-cli info | grep blocked_clientsblocked_clients:0  由于最大内存限制被移除的key的数量 [root@CombCloud-2020110836 src]# ./redis-cli info | grep evicted_keysevicted_keys:0  #内存碎片率 [root@CombCloud-2020110836 src]# ./redis-cli info | grep mem_fragmentation_ratiomem_fragmentation_ratio:2.74 已使用内存 [root@CombCloud-2020110836 src]# ./redis-cli info | grep used_memory:used_memory:3133624  基本活动指标:redis连接了多少客户端 通过观察其数量可以确认是否存在意料之外的连接。如果发现数量不对劲,就可以使用lcient list指令列出所有的客户端链接地址来确定源头。 [root@CombCloud-2020110836 src]# ./redis-cli info | grep connected_clientsconnected_clients:1[root@CombCloud-2020110836 src]# ./redis-cli info | grep connectedconnected_clients:1   # 客户端连接数量connected_slaves:1   # slave连接数量持久性指标:[root@CombCloud-2020110836 src]# ./redis-cli info | grep rdb_last_save_timerdb_last_save_time:1591876204  # 最后一次持久化保存磁盘的时间戳[root@CombCloud-2020110836 src]# ./redis-cli info | grep rdb_changes_since_last_saverdb_changes_since_last_save:0   # 自最后一次持久化以来数据库的更改数错误指标由于超出最大连接数限制而被拒绝的客户端连接次数,如果这个数字很大,则意味着服务器的最大连接数设置得过低,需要调整maxclients [root@CombCloud-2020110836 src]# ./redis-cli info | grep connected_clientsconnected_clients:1key值查找失败(没有命中)次数,出现多次可能是被hei ke gong ji [root@CombCloud-2020110836 src]# ./redis-cli info | grep keyspacekeyspace_misses:0   主从断开的持续时间(以秒为单位) [root@CombCloud-2020110836 src]# ./redis-cli info | grep rdb_changes_since_last_saverdb_changes_since_last_save:0  复制积压缓冲区如果设置得太小,会导致里面的指令被覆盖掉找不到偏移量,从而触发全量同步 [root@CombCloud-2020110836 src]# ./redis-cli info | grep backlog_sizerepl_backlog_size:1048576通过查看sync_partial_err变量的次数来决定是否需要扩大积压缓冲区,它表示主从半同步复制失败的次数 [root@CombCloud-2020110836 src]# ./redis-cli info | grep sync_partial_errsync_partial_err:1redis性能测试命令./redis-benchmark -c 100 -n 5000说明:100个连接,5000次请求对应的性能。 如有错误或其它问题,欢迎小伙伴留言评论、指正。如有帮助,欢迎点赞+转发分享。 欢迎大家关注民工哥的公众号:民工哥技术之路

June 26, 2020 · 1 min · jiezi

Redission-spring-boot

redission + spring bootmaven<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.linseven</groupId> <artifactId>cache</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.8.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent><dependencies> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.13.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.1.8.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.1.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency></dependencies><build> <defaultGoal>dataCache</defaultGoal> <resources> <resource> <directory>src/main/java</directory> </resource> <resource> <directory>src/main/resources</directory> </resource> </resources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.1.8.RELEASE</version> </plugin> </plugins></build></project> application.yml spring: redis: redisson: config: classpath:redisson.ymlserver: port: 8080redisson.yml ...

June 25, 2020 · 1 min · jiezi

Redis从入门到精通八Redis-集群

此篇介绍Redis Cluster 集群,简单介绍一下集群的实现,主要还是具体的实践部分:集群的开启,故障转移,添加节点,移除节点Redis集群中的基本概念Redis的集群模式提供了数据的分片功能,并能保证每个分片上的可用性。Redis集群每个节点需要打开2个TCP连接,一个为客户端提供服务,如 6379,另一个端口在第一个端口上 +10000,为 16379。用于集群总线,节点使用集群总线进行故障检测,配置更新,故障转移授权等。客户端不应尝试与集群总线接口进行连接。必须打开这两个端口,集群才能正常工作。(两个端口的偏移量始终是 10000) 集群中的数据分片Redis Cluster 不使用一致性Hash,它使用的是另一种方式:hash slot(哈希槽)。 Redis集群中共有16384个Hash槽,采用了 CRC 16 & 16384 计算出hash槽的值。Redis集群的每个节点负责一部分hash槽,例如:3个节点,其中A节点(0-5500),B节点(5501-11000),C节点(11001-16383). 在添加或这删除节点时,只需要将节点上的部分槽移动到新节点上即可,例如,需要添加一个新节点D,将A,B,C上部分的槽移动到D上即可。删除节点同理,例如需要删除A,只需要将A上的槽移动到B,C上。 集群中的主从模型为了保证集群中每个节点的可用性,cluster使用了主从模型。例如,三主三从的架构中:A,B,C,A1,B1,C1 ,A和A1为主从复制,A节点出现故障,集群将A1升为主节点,保证了集群的可用性,如果A1也出现故障,集群将不可用。 集群一致性保证Redis Cluster 无法保证数据的强一致性。Redis的异步复制是导致无法保证数据一致的第一个原因: 客户端向A写入数据 --> A向客户端回复 --> A将数据扩散至从节点A1,A2,A3,可能在扩散的过程中出现了故障,导致数据数据丢失。可以将复制扩散设置为同步,全部写入后再向客户端返回,但是这样就导致性能过低。 网络发生分区时,客户端与少数实例隔离,也可能出现数据丢失的情况, A,A1,B,B1,C,C1,Client, client 正在向C中写入数据,出现了网络分区,导致 client 和 C 被隔离,集群选举C1成为新的主节点,网络恢复后,C中的数据将会丢失,这种情况以通过设置 node timeout 来控制数据丢失的情况。节点超时过后,主节点被视为失败,可以由其中一个副本替换。类似地,在节点超时已经过去而主节点无法感知大多数其他主节点之后,它进入错误状态并停止接受写入。 Redis 集群的一致性,基本上要在性能和y一致性之间做一个平衡。 Redis 集群配置cluster-enabled <yes/no>cluster-config-file <filename>cluster-node-timeout <milliseconds>cluster-slave-validity-factor <factor>cluster-migration-barrier <count>cluster-require-full-coverage <yes/no>cluster-enabled <yes/no> ,是否开启集群模式,默认不开启cluster-config-file <filename> ,开启集群后,Redis集群会在每次发生更改时的配置写入到文件中,在重新启动时,使用这个配置文件。这个文件一般不去认为的改动。cluster-node-timeout <milliseconds>,节点超时时长,如果节点无法访问超过这个时间,从节点将会进行故障转移。cluster-slave-validity-factor <factor>,如果设置为零,则从站将始终尝试对主站进行故障切换,而不管主站和从站之间的链路是否保持断开连接的时间长短。如果该值为正,则计算最大断开时间作为节点超时值乘以此选项提供的因子,如果节点是从属节点,则如果主链接断开连接的时间超过指定的时间,则不会尝试启动故障转移。例如,如果节点超时设置为5秒,并且有效性因子设置为10,则从主设备断开超过50秒的从设备将不会尝试故障转移其主设备。请注意,如果没有从站能够对其进行故障转移,则任何不同于零的值都可能导致Redis群集在主站发生故障后不可用。在这种情况下,只有当原始主服务器重新加入群集时,群集才会返回。cluster-migration-barrier <count> ,主服务器将保持连接的最小从服务器数,以便另一个从服务器迁移到不再由任何从服务器覆盖的主服务器。cluster-require-full-coverage <yes/no>,:如果设置为yes,则默认情况下,如果任何节点未覆盖某个百分比的key space,则集群将停止接受写入。如果该选项设置为no,即使只能处理有关键子集的请求,集群仍将提供查询。Redis Cluster 使用我这里使用的Redis版本为 5.0.3,不同版本可能命令会有些不同。 首先准备6个Redis节点,条件有限,就在一台服务器上进行,拷贝6份redis.conf,最基本的必须配置有: port 7000cluster-enabled yescluster-config-file nodes.confcluster-node-timeout 5000appendonly yes6个节点的端口号为: 7000,7001,7002,7003,7004,7005 ...

June 25, 2020 · 8 min · jiezi

Redis从入门到精通四Redis的持久化和数据备份与恢复

本文将对Redis的两种持久化方式做详细的介绍,从配置,机制,优缺点几方面讲起,最后是关于数据的备份与恢复的介绍。Redis持久化简介Redis提供了两种持久化的选项,一种是快照文件(snapshotting,RDB),它会基于某个时间点将数据全部写入硬盘中(默认为dump.rdb)。另一种是只追加文件(append-only,AOF),它会在执行写入命令时将命令写入到硬盘中。 Redis持久化数据最主要是为了数据备份,故障恢复,也有一些经过耗时较长的计算结果存在Redis中,如果这些数据存在硬盘中,即使服务器重启了之后,这些数据还是存在的,不用再去耗时计算了。 这两种方式可以单独使用,也可以结合起来使用。最重要的还是要理解RDB和AOF的优劣势,结合自己的应用做一个权衡。 RDB (SNAPSHOTTING)RDB 配置项save 900 1stop-writes-on-bgsave-error yesrdbcompression yesrdbchecksum yesdbfilename dump.rdbdir ./上面6项配置中,前5项均是RDB的配置,最后一个是RDB 与 AOF 公用的配置 dir ./,指定 RDB 和 AOF 文件的路径save 900 1 , 多久执行一次快照操作, 900秒内有1次写入则执行快照操作stop-writes-on-bgsave-error , 创建快照失败后是否依然写命令,默认为yesrdbcompression , 是否对快照文件进行压缩,默认为 yesrdbchecksum , rdb文件是否启用CRC64文件数据完整性检查,5.0版本之后的属性,默认为 yes,开启后在 save 和 load 时将耗费 10%的性能,可以关闭此配置来提高性能dbfilename , rdb文件名,默认为 dump.rdbRDB 详解Redis 通过创建快照的方式,获得内存中某个时间点的数据副本。Redis重启时可以从 RDB 文件上恢复数据。我们也可以把 RDB 文件备份在别的服务器上。 根据上述的几个配置项,快照被写入 dir 目录下的 dbfilename 文件中。如果在新的快照文件创建完成之前,Redis服务器发生了宕机,那这期间的数据将丢失。 Redis中有两个命令可以创建快照文件, SAVE 和 BGSAVE。 执行 SAVE 命令后,服务器将不会再相应任何命令,直到快照文件完成。执行 BGSAVE 命令后,Redis父进程会调用 fork 来创建一个子进程,由子进程去负责快照文件的写入,父进程则继续处理客户端的命令请求。客户端可以直接向服务器发送 SAVE 或者 BGSAVE如果设置了 save 配置项,达到配置项的要求时,Redis会自动执行 BGSAVE 命令。该配置项可以设置多组,如果设置了多组,只要达到其中一组的要求,就会执行BGSAVERedis通过SHUTDOWN 指令,或者接收到标准 TERM 信号时,会执行 SAVE 命令,阻塞所有客户端,直到 SAVE 命令执行完成后,关闭服务器Redis配置了复制集之后,从服务器向主服务器发来 SYNC 命令,主服务器会执行 BGSAVE 命令,然后将快照文件发给从服务器需要注意的是, 执行 BGSAVE 命令可能会造成服务器暂停几毫秒或者几秒,具体时长要根据数据量的大小以及服务器的配置来看。在数据量特别大,服务器内存吃紧的情况下,可能会造成长时间的停顿,甚至宕机。通常情况下, BGSAVE 是要比 SAVE 好一些,因为不会影响客户端的请求,不过在数据量巨大的情况下, BGSAVE 可能会比 SAVE 指令耗时更长。所以还是要结合具体的数据情况来选择。如果可以接受数据丢失5分钟,15分钟,1小时甚至更长时间的数据的话,甚至可以关闭自动保存,由客户端决定什么时候来执行快照副本的创建。 ...

June 25, 2020 · 4 min · jiezi

Redis从入门到精通七Redis-Sentinel

Redis Sentinel 是 Redis官方提供的高可用解决方案。Redis本身可以实现主从复制,但是并没有自动切换主备的功能,当出现其中一个主实例宕机的情况,客户端将无法执行写入命令,Sentinel可以在没有人工干预的情况下执行故障转移,不影响正常的写入操作。 Sentinel是一个分布式的系统,多个Sentinel进程之间协调工作,一来保证故障检测的误报率,二来保证了Sentinel本身的可用性。 Redis SentinelSentinel提供了以下功能: 监控。Sentinel会不断检查主从是否按预期工作。通知。Sentinel可以通过API通知系统管理员,另一台计算机程序,其中一个受监控的Redis实例出现问题。自动故障转移。如果主服务器未按预期工作,Sentinel可以启动故障转移过程,其中从服务器升级为主服务器,其他其他服务器重新配置为使用新主服务器,并且使用Redis服务器的应用程序通知有关新服务器的地址连接。配置提供商。Sentinel充当客户端服务发现的权限来源:客户端连接到Sentinels,以便询问负责给定服务的当前Redis主服务器的地址。如果发生故障转移,Sentinels将报告新地址。配置我挑了一些主要的配置项出来,其它的都与redis.conf类似 # sentinel announce-ip <ip># sentinel announce-port <port>sentinel auth-pass mymaster <password># sentinel monitor <master-group-name> <ip> <port> <quorm>sentinel monitor mymaster 127.0.0.1 6379 2# sentinel down-after-milliseconds <master-name> <milliseconds>sentinel down-after-milliseconds mymaster 30000# sentinel parallel-syncs <master-name> <numreplicas>sentinel parallel-syncs mymaster 1# sentinel failover-timeout <master-name> <milliseconds>sentinel failover-timeout mymaster 180000sentinel auth-pass <master-group-name> <password> ,配置主服务器的密码sentinel monitor <master-group-name> <ip> <port> <quorm>,配置需要监视的主服务器,最后一个为 仲裁参数 ,代表Sentinel集群中必须有至少2个实例认为主服务器不可用,其中一个尝试启动故障转移,默认 127.0.0.1 6379 2sentinel down-after-milliseconds <master-name> <milliseconds> , 主服务器未回复sentinel ping的时长,如果超过这个时长,则这个sentinel实例主观的认为主服务器不可用,默认3000sentinel parallel-syncs <master-name> <numreplicas> , 设置可在同一故障转移后重新配置以使用新主服务器的从服务器数,默认 1sentinel failover-timeout <master-name> <milliseconds> ,指定故障转移超时时长,默认 180000启动可以通过以下两种方式开启Sentinel: ...

June 25, 2020 · 1 min · jiezi

Redis从入门到精通三Redis的过期策略和内存淘汰机制

此篇介绍了Redis过期策略以及Redis的内存淘汰机制,从内存淘汰的8种策略,如何开启内存淘汰策略到如何选择合适的淘汰策略,对Redis的内存淘汰机制做了全方位的阐述Redis 过期策略Redis 可以对 key 设置过期时间,这是一个非常实用的功能,那 Redis 是如何实现这个机制的呢?答案就是:定期删除 + 惰性删除 定期删除,Redis默认每隔100ms会从设置了过期时间的key中随机抽取一部分来检查是否过期,如果过期就删除。惰性删除,定期删除可能会导致很多设置了过期时间的key没有被及时删除,所以就有了惰性删除,即在查询这个key时,检查一下是否过期,如果过期就删除。Redis 内存淘汰机制结合定期删除 + 惰性删除 Redis 实现了key的过期时间机制,但还是会有一些key会没有被定期删除掉,也没有被查询,就遗留在了内存中,当数据量大到一定程度后,会导致内存的堆积。这就涉及到了 内存淘汰机制。 当内存容量到达了上限或者 配置的maxmemory时,会触发 内存淘汰策略 Redis提供了8种策略供我们选择: volatile-lru -> Evict using approximated LRU among the keys with an expire set.allkeys-lru -> Evict any key using approximated LRU.volatile-lfu -> Evict using approximated LFU among the keys with an expire set.allkeys-lfu -> Evict any key using approximated LFU.volatile-random -> Remove a random key among the ones with an expire set.allkeys-random -> Remove a random key, any key.volatile-ttl -> Remove the key with the nearest expire time (minor TTL)noeviction -> Don't evict anything, just return an error on write operations.先明确一下 LRU 与 LFU: ...

June 25, 2020 · 1 min · jiezi

Redis从入门到精通二Redis的数据类型

此篇罗列了 Redis 的所有数据类型并介绍了常用的5种数据类型(strings,Lists,Hashes,Sets,Sorted sets)简单的命令和使用场景Redis的数据类型Binary-safe strings , 简单的K-V 结构的存储Lists , 按插入顺序排序的字符串元素集合。基本上就是链表Sets ,唯一的,不排序的集合Sorted sets ,类似于集合,但每个字符串元素都与一个称为score的浮数值相关联,元素总是按分数排序,因此与集合不同,可以检索一系列元素Hashes , 由与值关联的字段组成的映射。字段和值都是字符串Bit arrays (or simply bitmaps) , 可以使用特殊命令处理字符串值HyperLogLogs , 这是一个概率数据结构,用于估计集合的基数Streams ,仅附加的类似于地图的条目集合,提供抽象日志数据类型Redis 常用的数据类型1. stringsstrings 可以用来存储 k-v 结构的数据,做计数器等; 常用命令: set <key> <value>get <key>incr <key>mget <key> <key...>2. listsLists,可以用来实现粉丝列表,评论列表等; 常用命令: lpush <key> <value>lpop <key>rpush <key> <value>rpop <key>lrange <key> <start> <end># lpush 是把元素插入到链表的头部,lpop 是从头部弹出一个元素并删除# rpush 是把元素插入到链表的尾部,rpop 是从尾部弹出一个元素并删除3. setsSets ,可以利用其无序,唯一(自动去重)的特性,例如,共同好友(用到了 SINTER 命令)等; 执行sadd <key> <value> , 成功返回1,数据已存在返回0,数据类型不对返回异常 ...

June 25, 2020 · 1 min · jiezi

Redis从入门到精通一安装

本文介绍了 Redis 在 Linux 和 Dcoker 中的安装和配置流程,Windows版的直接在 这里 下载解压即可使用,在此不做过多的描述linux下安装下载redis,在https://redis.io/ 或 https://github.com/antirez/re... 下载 redis-xxxx.tar.gz解压,我这里下载的是 5.0.0 版本 tar zxvf redis-5.0.0.tar.gz安装依赖环境,需要安装gcc yum install gcc-c++没有安装gcc在接下来的安装过程中会出现多处如下错误:1. `CC adlist.o`2. `/bin/sh: cc: 未找到命令`3. `make[1]: *** [adlist.o] 错误 127`4. `make[1]: 离开目录“/usr/local/redis/redis-5.0.0/src”`5. `make: *** [install] 错误 2`解压后进入目录,在 /redis.5.0.0 目录下,执行 make install至此已经安装成功,拷贝一份 redis.conf,对 redis做相关的配置 cp redis.conf ../redis.conf回到命令所在的目录,启动redis,验证是否安装成功,执行 `redis-server redis.conf` 出现下述文字则表示安装成功:1. `[root@wangkai-centos redis]# redis-server redis-conf`2. `12241:C 24 Jan 2019 22:24:42.589 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo`3. `12241:C 24 Jan 2019 22:24:42.589 # Redis version=5.0.0, bits=64, commit=00000000, modified=0, pid=12241, just started`4. `12241:C 24 Jan 2019 22:24:42.589 # Configuration loaded`5. `12241:M 24 Jan 2019 22:24:42.590 * Increased maximum number of open files to 10032 (it was originally set to 1024).`6. `_._` 7. `_.-``__ ''-._` 8. ```_.-`` `. `_. ''-._ Redis 5.0.0 (00000000/0) 64 bit```9. `.-`` .-```. ```\/ _.,_ ''-._` 10. ``( ' , .-` | `, ) Running in standalone mode``11. ```|`-._`-...-` __...-.``-._|'` _.-'| Port: 6379```12. ``| `-._ `._ / _.-' | PID: 12241``13. `` `-._ `-._ `-./ _.-' _.-'`` 14. ``|`-._`-._ `-.__.-' _.-'_.-'|`` 15. ``| `-._`-._ _.-'_.-' | http://redis.io`` 16. `` `-._ `-._`-.__.-'_.-' _.-'`` 17. ``|`-._`-._ `-.__.-' _.-'_.-'|`` 18. ``| `-._`-._ _.-'_.-' |`` 19. `` `-._ `-._`-.__.-'_.-' _.-'`` 20. `` `-._ `-.__.-' _.-'`` 21. `` `-._ _.-'`` 22. `` `-.__.-'`` 使用docker拉取Redis镜像 ...

June 25, 2020 · 1 min · jiezi

rodert单排学习redis进阶白银一

redis之白银一 说些题外话,最近帝都疫情又严重,大家都身处时代洪流中,这不是个别人能左右的,希望你能保护好自己,天天开心。 [toc] 前言声明:参考来源互联网,有任何争议可以留言。站在前人的肩上,我们才能看的更远。前文推荐阅读: rodert单排学习redis入门【黑铁】rodert 单排学习 redis 进阶【青铜】 1.Redis 客户端1.1.Redis Desktop Manager使用称手的工具,做起事来 事半功倍 ,用 redis-cli 自然不错。我推荐一款我经常用的 Redis 可视化工具,Redis Desktop Manager 。 启动界面如下: 至于安装方式是 一键安装 。 官网下载地址:https://redisdesktop.com/pricing学生和学习者可以公众号后台回复:【666】,免费获取。 2.Redis 连接池2.2.0.连接池池技术被广泛使用在系统开发中,像 JDBC 连接池、线程池等。连连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。在处理一个任务时,我们大多情况要在数毫秒级别就完成,如果重复创建、关闭资源,会占用较长时间和大量系统资源。 使用连接池优势减少连接创建时间连接在系统初始化时就创建完成,需要时直接从池中取用,减少了时间开销。 简化的编程模式当使用连接池时,每一个单独的线程能够像创建了一个自己的 JDBC 连接一样操作。 受控的资源使用连接池能够控制一个模块的资源占用率,不会让一个模块资源占用过高,导致整个系统崩溃。 2.1.Redis 连接池2.1.1.前言引入Redis 修炼之连接池篇,前面讲了Redis入门篇:rodert单排学习redis入门【黑铁】、rodert 单排学习 redis 进阶【青铜】,对 Redis 基本数据类型的操作做了讲解。在以前没有开源连接池时,很多人自写连接池工具,简单来说就是创建一个集合,存放一批连接,动态维护着。保证每个连接都是有效的。 2.1.2.Redis 连接池本教程涉及到的一些代码都是 Java 语言编写。maven 依赖,引入 pom.xml 文件 pom.xml<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.3.0</version></dependency>RedisUtil.javapublic final class RedisUtil { //IP 地址 private static String ADDR = "127.0.0.1"; //端口号 private static int PORT = 6379; //redis 服务端密码 private static String PWD = "123456"; //可用连接实例最大数目,默认为 8,若赋值 -1,表示不被限制 private static Integer MAX_TOTAL = 1024; //控制一个连接池最多有多少个状态为空闲的 jedis 实例,默认值为 8 private static Integer MAX_IDLE = 200; //等待可用连接最大的等待时间,单位 ms,默认值 -1,表示永不超时,若等待超时抛出 JedisConnectionException private static Integer MAX_WAIT_MILLIS = 10000; //超时 private static Integer TIMEOUT = 10000; //在用一个 jedis 实例时,是否提前进行 validate 操作,若结果为 true 则 jedis 实例可用 private static Boolean TEST_ON_BORROW = true; //jedis 连接池 private static JedisPool jedisPool = null; /** * 初始化 jedis 连接池的静态块,RedisPool 第一次类加载时执行,以后便不再执行 */ static { try { JedisPoolConfig conf = new JedisPoolConfig(); /* * 高版本 jedis jar 中 JedisPoolConfig 没有 setMaxActive 和 setMaxWait 属性,因为官方在高版本 * 中启用了此方法,用以下两个属性替换 * maxActive ==> maxTotal * maxWait ==> maxWaitMillis */ //设置连接实例最大数目 conf.setMaxTotal(MAX_TOTAL); //设置最多多少空闲的 jedis 实例 conf.setMaxIdle(MAX_IDLE); //设置等待可用连接的最大时间 conf.setMaxWaitMillis(MAX_WAIT_MILLIS); //设置是否提前进行测试借用 conf.setTestOnBorrow(TEST_ON_BORROW); //新建 jedis 连接池 jedisPool = new JedisPool(conf, ADDR, PORT, TIMEOUT, PWD); } catch (Exception e) { e.printStackTrace(); } } /* * 获取 jedis 实例来操作数据,每次使用完要将连接返回给连接池 jedis.close() * @return */ public synchronized static Jedis getRedis() { try { if(jedisPool != null) { //获取 jedis 实例 Jedis jedis = jedisPool.getResource(); return jedis; } else{ System.out.println("没有找到 Jedis 连接池!"); return null; } } catch (Exception e) { e.printStackTrace(); return null; } } /* * 用来回收 Jedis 对象资源,用户需要用到此方法释放资源,否则一直占用资源,在新版本中,`returnResource(jedis) 将被废弃不推荐使用,`直接调用 `jedis.close();` 归还连接到连接池。 * @param Jedis jedis */ public synchronized static void returnJedis(Jedis jedis) { try { if(jedis != null) { //回收 jedis 对象资源 jedisPool.returnResource(jedis); System.out.println("Jedis 被成功回收!"); } } catch (Exception e) { e.printStackTrace(); } }}- ...

June 25, 2020 · 2 min · jiezi

Redis进阶

键的过期策略在redis中当键到了过期的时间,就会立马被删除掉吗? 我们了解一下删除策略的知识,删除策略可分为三种 定时删除(对内存友好,对CPU不友好)惰性删除(对CPU极度友好,对内存极度不友好)定期删除(折中)惰性删除是指每次从键空间取键的时候,判断一下该键是否过期了,如果过期了就删除。 Redis采用的是惰性删除+定期删除两种策略,所以说,在Redis里边如果键到了过期的时间了,未必被立马删除的! 所谓定期删除,指的是 redis 默认是每隔 100ms 就随机抽取一些设置了过期时间的 key,检查其是否过期,如果过期就删除。 假设 redis 里放了 10w 个 key,都设置了过期时间,你每隔几百毫秒,就检查 10w 个 key,那 redis 基本上就死了,cpu 负载会很高的,消耗在你的检查过期 key 上了。 注意,这里可不是每隔 100ms 就遍历所有的设置过期时间的 key,那样就是一场性能上的灾难。 实际上 redis 是每隔 100ms 随机抽取一些 key 来检查和删除的。 但是问题是,定期删除可能会导致很多过期 key 到了时间并没有被删除掉,那咋整呢? 所以就是惰性删除了。这就是说,在你获取某个 key 的时候,redis 会检查一下 ,这个 key 如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。 获取 key 的时候,如果此时 key 已经过期,就删除,不会返回任何东西。但是实际上这还是有问题的,如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没走惰性删除,此时会怎么样? 答案是:走内存淘汰机制。 内存淘汰机制如果定期删除漏掉了很多过期key,也没及时去查(没走惰性删除),大量过期key堆积在内存里,导致redis内存块耗尽了,咋整? 我们可以设置内存最大使用量,当内存使用量超出时,会施行数据淘汰策略。 Redis的内存淘汰机制有以下几种: volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰 volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。 volatile-random:从已设置过期时间的数据集中任意选择数据淘汰 allkeys-lru:从数据集中挑选最近最少使用的数据淘汰 allkeys-random:从数据集中任意选择数据淘汰 no-enviction:当内存达到限制的时候,不淘汰任何数据慢查询简介和很多关系型数据库一样, Redis 也提供了慢查询日志记录,Redis会把执行比较慢的命令放到内部的一个list列表中。 注意:慢查询记录的只是命令的执行时间,不包括网络传输和排队时间。慢查询配置关于 Redis 慢查询的配置有两个,分别是 slowlog-log-slower-than 和 slowlog-max-len slowlog-log-slower-than 用来控制慢查询的阈值,所有执行时间超过该值的命令都会被记录下来。该值的单位为微秒,默认值为 10000,如果设置为 0,那么所有的记录都会被记录下来,如果设置为小于 0 的值,那么对于任何命令都不会记录,即关闭了慢查询。可以通过在配置文件中设置,或者用 config set 命令来设置: ...

June 25, 2020 · 2 min · jiezi

618-Tech-Talk高并发场景下的数据访问速度如何保障

云妹导读: 达达集团618迎来了“达达物流”和“京东到家”双平台业绩新高,京东智联云云缓存Redis保障了京东到家在大促期间高并发场景下的数据访问速度。那Redis是什么?云上缓存Redis概况?京东智联云云缓存Redis又如何助力“京东到家”呢?缓存Redis已成为高并发场景下提升数据访问速度的标配。大多数企业都会面临大量并发读写数据时访问速度慢、数据库压力大的问题,Redis起到了降低数据库压力,提升数据访问速度的作用。下图是某网站业务的解决访问速度慢的问题,引入缓存Redis提升访问速度的流程: 01 Redis 是什么?Redis(Remote Dictionary Server)是意大利人开发的一款内存高速缓存数据库,由C语言编写而成,是典型的NoSQL数据库,号称世界上最快的数据库之一。有如下特点: 支持多种数据结构,支持如下图所示的五种数据结构 读写速度快、性能高,由于数据都存在内存中,非SSD或者HDD等存储介质,内存型数据库的最大特点就是性能高、响应时间极低持久化到硬盘上(Redis 提供了两种持久化方式:RDB 和 AOF),即使断点或机器故障时,内存数据不会丢失,保证了数据的高可靠高可用和分布式,提供哨兵机制保证服务高可用,不同节点保障分布式部署多语言客户端,支持Java、PHP、Python、C、C++、Nodejs等丰富的功能,键过期、发布订阅实现消息系统等支持事务原子性,即操作数据要么全部成功,要么全部失败02 云上缓存Redis概况各大云厂商都已纷纷推出云上缓存服务,云上缓存Redis在开源Redis基础上,除了单实例架构(无高可用性),大部分厂商增加了Proxy代理,更方便的对Redis实例进行管理运维,控制台可对实例便捷管理及可视化监控。京东智联云云缓存Redis在各行业积累了丰富的行业经验,目前已为各行业龙头公司提供稳定的高性能缓存服务。 一般客户业务系统使用云缓存Redis后,相比传统自建机房、自己搭建维护Redis集群千万级别的投入,成本能大幅度降低。使用云缓存Redis前期投入少,每年的云上费用也比自建维护费以及租金少一个数量级;同时云缓存Redis可拓展性更高,可以随着公司业务量和场景的变化随时扩缩容或新增购买,周期时间短;更重要的一点是,云上缓存Redis横向扩展能力很强、速度快,随时保证高性能应对海量高并发读写,而传统缓存数据库高压读写时容易崩溃。 03 京东智联云云缓存 Redis 助力“京东到家”达达快送是达达集团旗下中国领先的本地即时配送平台,为各类商家和个人用户提供专业高效的本地即时配送服务。截至2020年一季度,达达快送业务覆盖全国2400多个县区市,日单量峰值达千万级。 京东到家是达达集团旗下中国领先的本地即时零售平台。京东到家携手零售商和品牌商等合作伙伴,依托达达快送的全国即时配送网络,为消费者提供超市便利、生鲜果蔬、医药健康、鲜花蛋糕、烘焙茶点、家居时尚等海量商品约1小时配送到家的服务体验。截至2020年一季度,京东到家业务覆盖全国700多个县区市,年度活跃门店数近10万。 作为典型的电商,京东到家的缓存需求为: 1.读写高性能、低时延; 2.缓存数据持久化存储,保证服务高可用; 3.定期备份数据,可按需恢复; 4.监控报警服务,超过阈值的监控会及时通知; 5.提供大容量缓存实例、促销时能快速平滑扩容; 6.提供数据迁移上云服务,支撑用户前期数据的迁移上云; 7.定期缓存分析,查看访问命令、大key热key情况,便于调整缓存数据存取策略。 京东智联云云缓存 Redis 给京东到家购物车系统、签到系统等提供了强大稳定的缓存服务。在满足客户所提需求之外,京东智联云云缓存 Redis 还提供了24小时技术支撑服务,应对各种突发事件,已经历过618、双11、双12大促考验。 那云缓存Redis又是如何解决以上需求的呢? 数据迁移上云:用户把数据从自建Redis迁移到云缓存Redis,使用迁移工具,支持不停服双写迁移备份恢复:定期备份,用户数据需要持久化并保证数据的可靠性备份下载:为了防止数据丢失,7天外的数据需要提前下载到本地参数修改:客户需要修改Redis的一些参数配置,可通过参数修改功能实现按需扩容:随着客户的数据量越来越大,开通的实例容量需要平滑扩容监控报警:设置报警策略,触发阈值时及时电话邮件通知,方便随时查看处理缓存分析:可定时缓存分析,查看TOP命令、大key热key和key类型分布,根据分析调整缓存策略达达618期间迎战巨大的物流配送挑战,保障了末端“最后3公里”的高效履约。在这张覆盖全国上千县市、数十万配送员的强大运力网络背后,京东智联云云缓存 Redis 有效地保障了达达系统的稳定性和高效性。 在此次京东 618 大促期间,京东智联云云缓存 Redis 支撑了京东物流仓储管理、供应链、大件物流开放平台等核心系统,和京东到家的签到、秒杀、红包等多个系统的数据存储和访问需求。 点击【阅读】了解更多京东智联云云缓存 Redis 信息

June 24, 2020 · 1 min · jiezi

为什么-Redis-单线程能达到百万QPS

作者:在江湖中codinghttps://juejin.im/post/5e6097... 性能测试报告 查看了下阿里 Redis 的性能测试报告如下,能够达到数十万、百万级别的 QPS(暂时忽略阿里对 Redis 所做的优化),我们从 Redis 的设计和实现来分析一下 Redis 是怎么做的。 Redis的设计与实现 其实 Redis 主要是通过三个方面来满足这样高效吞吐量的性能需求 高效的数据结构多路复用 IO 模型事件机制1、高效的数据结构Redis 支持的几种高效的数据结构 string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合) 以上几种对外暴露的数据结构它们的底层编码方式都是做了不同的优化的,不细说了,不是本文重点。 2、多路复用 IO 模型假设某一时刻与 Redis 服务器建立了 1 万个长连接,对于阻塞式 IO 的做法就是,对每一条连接都建立一个线程来处理,那么就需要 1万个线程,同时根据我们的经验对于 IO 密集型的操作我们一般设置,线程数 = 2 * CPU 数量 + 1,对于 CPU 密集型的操作一般设置线程 = CPU 数量 + 1。 当然各种书籍或者网上也有一个详细的计算公式可以算出更加合适准确的线程数量,但是得到的结果往往是一个比较小的值,像阻塞式 IO 这也动则创建成千上万的线程,系统是无法承载这样的负荷的更加弹不上高效的吞吐量和服务了。 而多路复用 IO 模型的做法是,用一个线程将这一万个建立成功的链接陆续的放入 event_poll,event_poll 会为这一万个长连接注册回调函数,当某一个长连接准备就绪后(建立建立成功、数据读取完成等),就会通过回调函数写入到 event_poll 的就绪队列 rdlist 中,这样这个单线程就可以通过读取 rdlist 获取到需要的数据。 需要注意的是,除了异步 IO 外,其它的 I/O 模型其实都可以归类为阻塞式 I/O 模型,不同的是像阻塞式 I/O 模型在第一阶段读取数据的时候,如果此时数据未准备就绪需要阻塞,在第二阶段数据准备就绪后需要将数据从内核态复制到用户态这一步也是阻塞的。而多路复用 IO 模型在第一阶段是不阻塞的,只会在第二阶段阻塞。 ...

June 22, 2020 · 1 min · jiezi

深入剖析Redis高可用系列持久化-AOF和RDB

欢迎关注公众号:「码农富哥」,致力于分享后端技术 (高并发架构,分布式集群系统,消息队列中间件,网络,微服务,Linux, TCP/IP, HTTP, MySQL, Redis), Python 等 原创干货 和 面试指南!免费视频福利推荐: 2T学习视频教程+电子书 免费送:BAT面试精讲视频,亿级流量秒杀系统,分布式系统架构,中间件消息队列,Python Go入门到精通,Java实战项目,Linux, 网络,MySQL高性能,Redis集群架构,大数据,架构师速成,微服务,容器化Docker K8s, ELK Stack日志系统等免费视频教程! Redis高可用概述在介绍Redis高可用之前,先说明一下在Redis的语境中高可用的含义。 我们知道,在web服务器中,高可用是指服务器可以正常访问的时间,衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999% 等等)。但是在Redis语境中,高可用的含义似乎要宽泛一些,除了保证提供正常服务(如主从分离、快速容灾技术),还需要考虑数据容量的扩展、数据安全不会丢失等。 在Redis中,实现高可用的技术主要包括持久化、复制、哨兵和集群,下面分别说明它们的作用,以及解决了什么样的问题。 持久化:持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要作用是数据备份,即将数据存储在硬盘,保证数据不会因进程退出而丢失。复制:复制是高可用Redis的基础,哨兵和集群都是在复制基础上实现高可用的。复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。缺陷:故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制。哨兵:在复制的基础上,哨兵实现了自动化的故障恢复。缺陷:写操作无法负载均衡;存储能力受到单机的限制。集群:通过集群,Redis解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。Redis持久化概述Redis 的数据全部在内存里,如果突然宕机,数据就会全部丢失,因此必须有一种机制来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的持久化机制。 Redis为持久化提供了两种方式: RDB:在指定的时间间隔能对你的数据进行快照存储。AOF:记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据。由于AOF持久化的实时性更好,即当进程意外退出时丢失的数据更少,因此AOF是目前主流的持久化方式,不过RDB持久化仍然有其用武之地。 下面依次介绍RDB持久化和AOF持久化; RDB持久化RDB是默认的持久化方式,按照一定的策略周期性的将内存中的数据生成快照保存到磁盘。 每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。 1. 工作原理:Redis调用fork(),产生一个子进程。子进程把数据写到一个临时的RDB文件。当子进程写完新的RDB文件后,把旧的RDB文件替换掉。2. 触发机制RDB触发持久化分为手动触发和自动触发 save 命令(手动触发)当客户端向Redis server发送save命令请求进行持久化时,由于Redis是用一个主线程来处理所有,save命令会阻塞Redis server处理其他客户端的请求,直到数据同步完成。save命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在Redis服务器阻塞期间,服务器不能处理任何命令请求,因此线上环境不推荐使用 bgsave命令(手动触发)与save命令不同,bgsave是异步执行的,当执行bgsave命令之后,Redis主进程会fork 一个子进程将数据保存到rdb文件中,同步完数据之后,对原有文件进行替换,然后通知主进程表示同步完成。 自动触发除了手动触发RDB持久化,Redis内部还存在自动触发机制, 在配置中集中配置 save m n 的方式,表示 m秒内数据集存在n次修改时,系统自动触发bgsave 操作。  3. RDB自动持久化配置# 时间策略save 900 1save 300 10save 60 10000# 文件名称dbfilename dump.rdb# 文件保存路径dir /etc/redis/data/# 如果持久化出错,主进程是否停止写入stop-writes-on-bgsave-error yes# 是否压缩rdbcompression yes# 导入时是否检查rdbchecksum yesrdb持久化策略比较简单,下面解释一下:save 900 1 表示900s内如果有1条是写入命令,就触发产生一次快照,可以理解为就进行一次备份save 300 10 表示300s内有10条写入,就产生快照下面的类似,那么为什么需要配置这么多条规则呢?因为Redis每个时段的读写请求肯定不是均衡的,为了平衡性能与数据安全,我们可以自由定制什么情况下触发备份。所以这里就是根据自身Redis写入情况来进行合理配置。 ...

June 21, 2020 · 1 min · jiezi

腾讯云Redis混合存储版重磅推出万字长文助你破解缓存难题

导语 | 缓存+存储的系统架构是目前常见的系统架构,缓存层负责加速访问,存储层负责存储数据。这样的架构需要业务层或者是中间件去实现缓存和存储的双写、冷热数据的交换,同时还面临着缓存失效、缓存刷脏、数据不一致等问题。本文是对腾讯云数据库高级产品经理邹鹏老师在「云加社区沙龙online」的分享整理,希望与大家一同交流 点击此链接,查看完整直播回放 一、前言在互联网和移动互联网两波浪潮的推动下,存储技术有了飞速发展。移动互联网用户在过去十年增长了10倍,用户的增长带动了数据量的指数级增长,因为激烈的市场竞争,企业和用户对应用程序的响应性能要求越来越高,在完美应对庞大的用户规模和海量数据集的同时保证优秀的产品体验,是数据库面临的挑战。 在机械硬盘普及的时代,企业需要通过缓存技术加速数据的访问,在SSD存储介质普及后,企业需要缓存技术支撑高并发和大吞吐,通过引入分布式缓存方案,提升应用程序性能,消除数据库热点。 但是缓存技术的引入增加了业务架构的复杂度,降低了开发效率,同时还面临着缓存一致性、缓存击穿、缓存雪崩等挑战。腾讯云数据库团队推出的Redis混合存储产品,融合缓存和存储的统一架构,彻底解决了缓存难题,帮助企业的研发人员聚焦业务逻辑,提升生产效率。 二、缓存的三座大山 1. 缓存一致性缓存一致性是指业务在引入分布式缓存系统后,业务对数据的更新除了要更新存储以外还需要同时更新缓存,对两个系统进行数据更新就要先解决分布式系统中的隔离性和原子性难题。 目前大多数业务在引入分布式缓存后都是通过牺牲小概率的一致性来保障业务性能,因为要在业务层严格保障数据的一致性,代价非常高,业务引入分布式缓存主要是为了解决性能问题,所以在性能和一致性面前,通常选择牺牲小概率的一致性来保障业务性能。 2. 缓存击穿缓存击穿是指查询请求没有在缓存层命中而将查询透传到存储DB的问题,当大量的请求发生缓存击穿时,将给存储DB带来极大的访问压力,甚至导致DB过载拒绝服务。空数据查询(黑客攻击)和缓存污染(网络爬虫)是常见的引发缓存击穿的原因。 什么是空数据查询?空数据查询通常指攻击者伪造大量不存在的数据进行访问(比如不存在的商品信息、用户信息)。 缓存污染通常指在遍历数据等情况下冷数据把热数据驱逐出内存,导致缓存了大量冷数据而热数据被驱逐。缓存污染的场景我们目前还没有发现较好的解决方案,但是在空数据查询问题上我们可以改造业务,通过以下方式防止缓存击穿: 通过bloomfilter记录key是否存在,从而避免无效Key的查询;在Redis缓存不存在的Key,从而避免无效Key的查询。 3. 缓存雪崩缓存雪崩是指由于大量的热数据设置了相同或接近的过期时间,导致缓存在某一时刻密集失效,大量请求全部转发到DB,或者是某个冷数据瞬间涌入大量访问,这些查询在缓存MISS后,并发的将请求透传到DB,DB瞬时压力过载从而拒绝服务。 目前常见的预防缓存雪崩的解决方案,主要是通过对key的TTL时间加随机数,打散key的淘汰时间来尽量规避,但是不能彻底规避。 三、传统分布式缓存方案在引入分布式缓存后,我们的业务架构由原有两层架构(应用+数据库)变成了三层架构(应用+缓存+存储),缓存层缓存热数据,存储层负责全量数据持久化存储。 存储架构的变化要求业务对数据的存取逻辑进行相应调整,而且这个调整是巨大的。在缓存系统的选择上,常见的缓存数据库包括Memcached、Redis,目前使用最广泛的是Redis,存储数据常见的包括关系型数据库MySQL、PG、Oreacle、SQLServer等,NoSQL数据库MongoDB、Hbase等。 在引入分布式缓存后,业务逻辑需要做三个点的变化,缓存读取、缓存更新、缓存淘汰。 1. 缓存读取引入缓存层后,读数据就变得不是那么简单直接了,APP需要先去缓存读取数据,如果缓存MISS(数据没有被缓存),则需要从存储中读取数据,并将数据更新到缓存系统中,整个流程和代码如下所示: # Python示例代码 2. 缓存更新我们把常见的缓存更新方案总结为两大类,业务层更新和外部组件更新,比较常见的是通过业务更新的方案。 (1)业务层更新缓存a. 缓存更新的难点刚开始接触缓存方案的同学可能会纠结几个点,先更新缓存还是先更新存储,缓存的处理是通过删除来实现还是通过更新来实现。 这里我们面临的问题本质上是一个数据库的分布式事务的问题,需要处理数据可靠性的挑战,并发更新带来的隔离性挑战,和数据更新原子性的挑战。 数据可靠性:如果要保证数据的可靠性,在业务逻辑成功之前,必须保障有一份数据落地,我们有以下两个选择: 先更新成功存储,再更新缓存;先更新成功缓存,再更新存储,如果存储更新失败,删除缓存。操作隔离性:一条数据的更新涉及到存储和缓存两套系统,如果多个线程同时操作一条数据,并且没有方案保证多个操作之间的有序执行,就可能会发生更新顺序错乱导致数据不一致的问题。 更新原子性:引入缓存后,我们需要保证缓存和存储要么同时更新成功,要么同时更新失败,否则部分更新成功就会导致缓存和存储数据不一致的问题。 b. 业务层缓存更新方案我们看到大多数的常见是选择以下方案,保障数据可靠性,尽量减少数据不一致的出现,通过TTL超时机制在一定时间段后自动解决数据不一致现象。 Step1:更新存储,保证数据可靠性; Step2:更新缓存,2个策略怎么选: 惰性更新:删除缓存,等待下次读MISS再缓存(推荐方案);积极更新:将最新的值更新到缓存(不推荐)。 积极更新策略,缓存数据实时性更高,但是在缓存侧带来了更多的更新操作,这会提高更新冲突导致脏数据概率。 (2)外部组件更新缓存a. 缓存MISS处理方案在通过第三方组件更新的方案中,为了保障数据的一致性,避免对单条数据的并行更新,缓存的所有更新操作都需要交给同步组件,因此缓存MISS场景下的逻辑 b. 缓存更新方案先更新存储,由第三方组件异步更新缓存。该方案投入较大,只适合特定的场景,并且有以下3个难点: 需要监控存储的日志,或者通过Triger来监控存储数据的变更,需要对存储系统非常熟悉;需要对更新进行过滤,我们的目的是缓存热数据,但是像DDL、批量更新这一系列的操作是不需要更新缓存的,要把非业务更新操作过滤;同步组件需要理解数据,不通用。 c. 其他缓存更新方案在实际的生产中,我们还会看到很多先更新缓存,然后通过第三方组件更新存储的场景,但是这个方案也会面临数据一致性和数据可靠性的挑战,虽然不推荐,但是确实还是能看到有在使用这个方案的,我们拿出来探讨下。 这个场景数据可靠性,不及先更新存储的方案,但是写入性能高,延迟低;这个方案APP和第三方组件都会更新Cache,会存在数据一致性的问题,因为很难保障两个组件更新的时序。 3. 缓存淘汰缓存的作用是将热点数据缓存到内存实现加速,内存的成本要远高于磁盘,因此我们通常仅仅缓存热数据在内存,冷数据需要定期的从内存淘汰,数据的淘汰通常有两种方案: 主动淘汰:这是推荐的方式,我们通过对Key设置TTL的方式来让Key定期淘汰,以保障冷数据不会长久的占有内存。TTL的策略可以保证冷数据一定被淘汰,但是没有办法保障热数据始终在内存,这个我们在后面会展开; 被动淘汰:这个是保底方案,并不推荐,Redis提供了一系列的Maxmemory策略来对数据进行驱逐,触发的前提是内存要到达maxmemory(内存使用率100%),在maxmemory的场景下缓存的质量是不可控的,因为每次缓存一个Key都可能需要去淘汰一个Key。 四、腾讯云Redis混合产品介绍1. 产品简介腾讯云Redis混合存储版基于腾讯游戏线上运营多年的Tendis引擎打造。数据自动降冷,落盘压缩,最大可降低成本85%,100% 兼容Redis协议,可助力企业大幅提升生产效率,降低运营成本。 2. 产品特性(1)研发效率+++a. 混合存储解决方案同样的三层架构,业务仅需要访问统一的Redis接口,让企业重新聚焦业务逻辑;一套系统支撑,避免维护多套系统。b. 解决缓存三大难题一致性:通过内聚的设计,保障缓存和存储一致性缓存击穿:All Keys In Memory设计,避免缓存击穿;缓存持久:动态TTL设计,热数据即持久缓存。c. 100%兼容Redis协议100%兼容Redis协议,业务可顺畅接入。d. 超高读写性能高写入:为Redis定制的Rocksdb存储引擎,支持100万并发写入;高读取:只能热数据缓存方案,提供1000万并发读取。 ...

June 18, 2020 · 1 min · jiezi