Redis是目前最为流行的非关系型数据库,也是JAVAer们必须要接触的数据库之一,但是网上面的redis面试题总结大多数都是概念性的,一个redis使用经验不足的人肯定难以理解;这里阿伟就把我会结合使用场景去问面试者的那些题目总结出来,以供大家参考和学习;(关于redis,我一般喜欢问几个概念题之后就结合使用场景去问面试者,因为这样既能考验面试者的技能掌握程度,又能考验面试者的逻辑思维能力);
目前公司有十万员工,分成个部门,公司为员工制定了每日9点前和18点后网上签到的制度,签到之后可以及时查看自身签到状态,主管可以及时收到下属员工的签到状态,一整天未签到的员工自动补充旷工。
以上为场景;请用java+redis+队列+mysql实现此功能。
实现以上功能的方法有很多,这一题主要考察面试者对效率,并发,容错率等等各方面的思考能力,这里阿伟给大家提供一个单机redis实现此功能的简单方法;
首先我们先生成个map,map的key值是部门下员工的id,value值是员工今天签到的状态,每天还没人签到的时候map为空;这个map也都存放在redis里,key值是部门的id+签到常量+日期;
员工签到需要先登录,当他登陆时,把他的个人信息存放到redis里,key值是常量+员工id,在签到时,签到成功后,修改redis里的个人信息,并将员工所在的部门签到map从redis里取出,这时候要给这个部门签到map的key值上一个锁,避免被其他员工同事取到产生竞态,然后将个人的签到信息保存到部门签到map中,再放回redis,释放锁;此时将员工签到的状态放到消息队列中,推送给主管;
设置一个定时任务,在每天24点之后,将个部门签到map取出,进行遍历,然后在数据库中保存员工签到状态,如果保存失败,则放到失败队列中,遍历结束之后再次将失败队列中的信息保存,再次失败的数据将生成日志保存,然后定时任务再将一整天未签到的员工设置成旷工;
注意还要生成新的一天的部门签到map;
如果一个面试者能够回答出以上的答案,算上符合我的心理预期,如果有更高效的答案,那就是加分;然后我还会结合面试者的回答提问一些问题。
首先实现以上功能,redis需要的最小key的数量答:1个;key值无所谓,value值是一个map,map中再存放签到状态等数据。虽然如此做效率极低,但的确实现了最小key的数量(其实还可以0个,那就是不用mysql,不过那就不符合题目要求了);为什么都把签到状态放在员工信息里了,还要再生成多个部门签到map进行保存?答:因为redis的key值是有过期时间的,而且redis和map一样,key值越多,检索起来越慢;所以我们的员工信息肯定要设置过期时间,不可能永远放在reddis里,会极大的影响redis的效率,而问题中要求将一整天未签到的员工设置成旷工,所以我们肯定要检索员工当天的签到状态,这时候从员工信息里取很明显是不科学的,因为有的员工信息已经失效了。我们常说redis支持五种数据类型,String、List、Set、SortedSet(ZSet)、hash,所以员工信息应该怎样保存到redis里,你打算用那种数据类型进行存储答:redis支持自定义对象的存储,可以用序列化反序列化的方式或者转成json保存在redis中,没有必要非要设置成以上五种数据类型;为什么都生成部门签到map保存签到信息了,还要把签到状态保存到员工信息里答:因为问题中要求员工随时可以查看自身签到状态,如果每次查看的时候都去部门签到map中取的话,会导致可能随时都有人在竞争锁,效率低下;为什么要先生成部门签到map在redis里,就不能等第一个员工签到的时候再产生该部门的签到map吗答:因为同一时间可能会有多人去签到,如果那时再产生部门签到map,首先无法利用锁将该map锁定,这样会导致同一时间生成多个相同key值的部门签到map,会产生竞态,相互覆盖,出现bug网上的面试题中常说redis没有锁的概念,你上面所说的锁,用redis如何实现呢答:首先,redis的锁是利用过期时间等因素实现的锁,redis的确没有直接能用的锁;但是现在的redis提供的set方法可以简洁有效的设置一个你想要的锁;这里阿伟给大家写一个最简单的实现publicbooleangetLock(Stringkey,Stringvalue,longexpired){if(StringUtils.isEmpty(key)){returnfalse;}RedisConnectioncon=null;try{con=redisTemplate.getConnectionFactory().getConnection();booleanrs=con.setNX(key.getBytes(),value.getBytes());if(rs){con.expire(key.getBytes(),expired);}returnrs;}catch(Exceptione){e.printStackTrace();returnfalse;}finally{if(con!=null){con.close();}}}注意:在redis2.6.2版本后,可以直接set(key,value,NX,EX,sencods)实现redis锁以下是最简单的设置逻辑;首先我们redis中假设有一个key值叫a,我们有一堆线程想要去获取到a的value,这时候我们先尝试以b为key,设置一个锁,如果失败则代表已经有线程先一步设置了这个锁,sleep()后循环尝试。设置成功之后,我们再去取a的vulue进行我们想要的操作,操作结束后释放b。我们都知道redis有自带的订阅发布的功能,为什么我们不用这个而是使用消息队列呢答:因为redis无法对消息进行持久化处理,消息一旦发出,接收者不接收就会消失;而且redis订阅发布功能比较简单,支持的协议少;以上就是阿伟对redis结合使用场景进行面试的一个小总结,如果一个两年工作经验的javaer能够流畅并且条例清晰的回答以上问题,那么我会在心里给他大大的加分的,当然如果他回答的很好我会问的更深的(嘻嘻)。
如果觉得阿伟写的不错,请点赞、分享、