新浪一面

1 redis如何设计的

redis的键值对的值:字符串、列表、哈希、集合、有序集合。

字符串对象底层实现SDS:len记录长度,查询长度时间复杂度O(1)。len和free避免缓冲区溢出。自动扩容和惰性空间释放减小内存重分配次数。len保证二进制安全。兼容c字符串函数。

链表:列表键使用。使用双向链表。节点可以保存任意类型的值。

字典:数据库、哈希键使用。字典中包含哈希表、dictype、privatedata,哈希表中包含哈希表节点指针、哈希表大小、掩码、已用数量。

跳跃表:有序集合键使用。zskiplist包含header、tail、length、level。header和tail指向zskiplistNode,zskiplistNode包含score、obj、level[]、backward,level包含forward、span。

整数集合:集合键底层实现之一。当一个集合只包含整数值元素, 并且这个集合的元素数量不多时, Redis 就会使用整数集合作为集合键的底层实现。intset包含encoding、length、contents[]。

压缩列表:列表键和哈希键底层实现之一。当一个列表键只包含少量列表项, 并且每个列表项要么就是小整数值, 要么就是长度比较短的字符串, 那么 Redis 就会使用压缩列表来做列表键的底层实现。

字符串对象:int编码,embstr编码,字节数小于等于39,一次分配函数创建redisobject、sdshdr,raw编码,两次分配函数创建redisobject、sdshdr

2 java的垃圾回收机制

垃圾回收机制是针对java堆和方法区的。

java中是用可达性分析算法判定对象是否存活,算法基本思想通过称为GC Roots的对象作为起始点,从这些节点往下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链存在时,则证明这个对象不可用。GC Roots的对象有4种:虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中Native方法引用的对象。

枚举根节点:HotSpot中,使用一组称为OopMap的数据结构达到虚拟机直接得知哪些地方存放着对象引用。类加载完成时,HotSpot就把对象内什么偏移量是什么类型的数据计算出来,在JIT编译过程中,也会在特定的位置记录下栈和寄存器哪些位置是引用。

安全点:HotSpot中,安全点选定在是否具有让程序长时间执行的特征,一般在方法调用、循环跳转、异常跳转的指令。

主动式中断:HotSpot中,GC需要中断时,设置一个标志,各个线程执行时主动去轮询这个标志。轮询点和中断点是重合的,例如test指令可以产生一个自陷异常信号。

HotSpot中,jdk1.7,Serial收集器用一条收集线程完成垃圾收集工作,并且会stop the world。client模式下的虚拟机默认选择,适用于单cpu环境。

ParNew是Serial多线程版本,Server模式下虚拟机首选的新生代收集器,与CMS配合适用,默认开启的收集线程数和CPU数量相同。

Parallel Scavenge目的达到一个可控制的吞吐量,吞吐量为运行用户代码时间与CPU总耗时之比,适合后台运算而不需要太多交互的任务。可以设置最大垃圾收集停顿时间和直接设置吞吐量大小。可以启动自适应调节策略,以提供最合适的停顿时间或者最大的吞吐量。

Serial Old主要用于client模式下,server模式下作为CMS的后备方案,标记整理算法。CMS发生Concurrent Mode Failure的后备。

Parallel Old搭配Parallel Scavenge使用,用于注重吞吐量的场合,标记整理算法。

CMS,基于标记清除算法,初始标记标记一下GC Roots能直接关联到的对象,并发标记进行GC Roots Tracing的过程,重新标记修正并发标记期间因用户程序运行而导致标记变动的对象的标记记录,并发清除。3个缺点:CMS对CPU资源敏感;会产生浮动垃圾;Concurrent Mode Failure。获取最短停顿时间为目的。

G1,整体看基于标记整理算法,从局部看基于复制算法,不会产生碎片。除了追求低停顿,还可以建立可预测的停顿时间模型。划分为Region,新生代和老年代不再是物理隔离,是一部分Region的集合。跟踪每个Region里面垃圾堆积的价值大小(回收所获得的空间大小和回收所需时间),后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。G1中每个Region都有一个与之对应的RememberedSet ,在各个 Region 上记录自家的对象被外面对象引用的情况。当进行内存回收时,在GC根节点的枚举范围中加入RememberedSet 即可保证不对全堆扫描也不会有遗漏。四个阶段:初始标记标记一下GCRoots直接关联到的对象,并且修改TAMS的值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建新对象,这阶段需要停顿线程。并发标记阶段是从GC Root开始对堆中对象进行可达性分析,找出存活的对象,可并发执行。最终标记阶段是为了修正在并发标记阶段因用户程序继续运行而导致标记产生变动的那一部分标记记录,虚拟机将这一段时间对象变化记录在Remembered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这阶段需要停顿线程,但可并行执行。筛选回收阶段对各个Region的回收价值和成本进行排序,根据用户所期望GC停顿时间来制定回收计划。

3 如何保证并发

HashMap<Long, Boolean> localOverMap初始存放着goodsid和false,首先判断对应的货物是否销售完了,减少redis访问。

redis中存放每种goodsid的库存,decr去减库存,原子操作,如果库存减为1,设置localOverMap中对应的值为true。

判断是否已经秒杀到了。用rabbitmq传送秒杀消息,作用为异步处理,应用解耦,流量削锋和消息通讯。直接返回方法,前端也反复查询是否生成订单,是否货物数量已经为0。

接收端执行减库存,下订单。

缓存更新策略:更新数据库然后删除缓存。不是更新缓存是怕两个并发写操作导致脏数据。cache aside pattern有并发问题的,一个读操作读缓存失效,就到数据库中去数据,此时来一个写操作,写完数据库后让缓存失效,然后读操作再把老数据放入缓存。不过出现的概率非常低。

4 线程池的作用

降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

提高线程的可管理性。线程池可以进行统一分配、调优和监控线程。

猜你喜欢

转载自blog.csdn.net/wuyifan1115/article/details/83005254