240 发简信
IP属地:河北
  • 补充一下同步锁中几种锁的升级过程:

    在对资源(对象)进行上锁时,涉及到对象头信息的修改,这里简单介绍一下对象头中关于锁的部分——当前上锁的线程id,无锁状态、偏向锁标记,轻量锁标记,重量锁标记。

    当一个线程对资源进行上锁时,对象处于无锁状态,这时通过cas修改对象头,并将当前线程id放入对象头,这就是偏向锁,如果一直没有线程对这个资源竞争,那么这个偏向锁会一直保持,不会主动释放,随后当有线程与当前线程进行资源抢占时,首先查看当前线程是否还存活并且持有锁,如果持有,则撤销偏向锁,升级为轻量级锁,如果没有,则将对象头先调整为无锁状态,再加偏向锁。在轻量级锁的情况下,有线程还是在与当前持有锁的线程进行抢占资源并且不断自旋,当自旋达到一定次数的时候,轻量级锁便会升级成重量锁,并且将所有抢占该资源且没有获得锁的线程全部放入等待队列。

    高并发多线程总结

    1.多线程基本概念 首先,我们要理解多线程编程,必须清楚几个基本概念:进程——进程是操作系统层面的概念,它是操作系统进行资源分配和调度的基本单位线程——线程是进程内部的程序流...

  • 修正一下:zset使用的是跳表和压缩列表

    redis总结

    1.redis基础 今天我们来聊聊Redis缓存!说起redis,首先想到的肯定是速度极快,因为redis是基于内存的数据库嘛!那么,redis除了速度快,还存在其他优势吗?...

  • 补充一下redis的底层数据结构知识:
    大家在应用redis的时候会发现这么几种数据结构——String、Hash、set、zset,list以及位图bitmap等,刚刚说的这些都属于咱们应用层面的结构,我们今天的重点就应用层面走向底层原理,来深入剖析一下redis的几种底层数据结构。
    String——SDS(简单动态字符串)
    从结构说起,在老版本(3.x)的结构中包含len(字符串长度)、free(剩余可用长度)、buf[]数组(所存储的字符串),新版本(6.x)之后的版本做了一系列改变,结构包含len(已用长度,细分为多种长度结构)、alloc(总长度)、flags(一个char类型变量,低三位存储类型,高五位预留)、buf[]数组;
    第二种结构是在第一个的基础上进行优化的,所以我们就直接从第二种结构入手吧
    为什么选用SDS而不是C语言的字符串?
    1.二进制安全2.支持动态扩容3.惰性空间释放,减少内存重新分配次数4.获取字符串长度为O(1)
    >1二进制安全是因为在C语言中字符串是以“/0”结尾的,而在SDS中结尾可以直接通过长度len来判断,并不需要“/0”,所以SDS字符串中可以存在多个“/0”,并且可以支持存储二进制数据
    >2动态扩容是因为SDS的len中存在多种长度数据,在进行扩容时,首先判断可用空间alloc是否够用,如果够用则不新开辟空间,如果不够的话,判断新开辟空间是否大于1MB,如果不大于则将原空间进行两倍扩容,如果大于1MB,则扩容1MB。这里存在空间预留机制,即可用空间大小alloc,避免多次开辟内存;
    >3 惰性空间释放,当数据进行压缩后,SDS并不会直接修改空间大小,而是留待下一次使用,从而减少内存分配
    >获取字符串O(1) 不用像C语言一样去遍历字符串计数,直接通过len属性获取字符串长度
    list——双向链表和ziplist
    双向链表我们还是比较常见的(redis在传统双向链表上加了头尾指针和自定义的相关函数),这里我们直接讲压缩列表吧(数据节点较少的情况下使用,原因与entry中的prevlen有关);
    结构体包含:zlbytes、zllen、zltail、entry、zlend
    zlbatys:记录整个压缩列表占用内存字节数
    zllen:记录压缩列表节点个数
    zltail:记录压缩列表尾部节点与头部起始地址多少字节,方便直接访问尾部节点
    zlend:标记压缩列表的结束点,一般是255
    entry:三部分组成——prevlen前一个节点所占字节数、encoding记录当前节点实际数据类型以及长度,data记录当前节点的实际数据
    (连续更新):prevlen一般由一个字节或者五个字节构成,当前一个节点处于254节点的临界值附近时,此时插入一个新节点若超过254字节,那么就会使得后面节点中的prevlen全部需要更新,引起连锁反应,这是就需要内存重新分配空间,复制原先的数据;所以压缩列表一般是在数据节点较少的情况下使用
    skiplist-跳表:可以与树状数组进行类比,跳表就是一个改装的树状链表,相当于一个新的索引结构,查询时间复杂度为lgN;跳表的实现主要包含两个结构——zskiplistNode,zskiplist两部分
    zskiplistNode:数据ele、分数score、后退指针backward、层zskiplistLevel{前进指针forword 跨度span}
    zskiplist:表头指针hard与tail、节点数量length,节点中最大层数level
    简述过程:
    插入zskipNode时会随机一个层数level(范围在32以内),从最上层开始向下遍历寻找合适的位置,修改前后node节点指针,插入完成
    删除目标节点,和插入相同,从上往下遍历层数,直到找到目标节点,修改目标节点指针以及跨度span
    更新目标节点:先做删除操作,再做插入操作

    总结一下:字符串String使用SDS,list可以使用ziplist和双端队列、set使用哈希表和有序数组,zset使用跳表和哈希表,hashmap使用哈希表和压缩列表,由此可见ziplist使用非常频繁,这也是充分利用CPU缓存的最佳实现,因为ziplist是一片连续的内存空间;这也是redis快的原因之一;

    redis总结

    1.redis基础 今天我们来聊聊Redis缓存!说起redis,首先想到的肯定是速度极快,因为redis是基于内存的数据库嘛!那么,redis除了速度快,还存在其他优势吗?...

  • 120
    redis总结

    1.redis基础 今天我们来聊聊Redis缓存!说起redis,首先想到的肯定是速度极快,因为redis是基于内存的数据库嘛!那么,redis除了速度快,还存在其他优势吗?...

  • 120
    Spring总结

    1.Spring核心 今天给大家讲讲Spring吧,谈到Spring,学过的人估计第一反应就是IOC和AOP,的确,这两个就是Spring的核心知识,也可以说是一种思想理念。...

  • 面试问题:MySQL乱序insert和有序insert的效率比较?尝试分析原因
    解答:这个问题可以从索引结构B+树讲起。我们都知道不管是顺序插入还是乱序插入数据,当数据一旦超过缓存页大小(一般是16KB),必然会导致也分裂,这时相应的索引结构也会发生改变。顺序和乱序的区别就在于——顺序插入只需要维护索引页,而乱序插入可能发生数据迁移,当一页数据迁移量很大时,带来的磁盘IO成本是很高的;再者,从磁盘的角度来说,InnoDB会把叶子页按顺序在磁盘中存储,假设之前存在两个缓存页A,B已经在磁盘中有序了,这时插入几条新数据导致分裂的页C从逻辑上来说顺序应该处于A,B之间的,但这是放入磁盘并不会物理上有序,导致读取数据时,磁盘IO存在随机读取,降低速度。

    Mysql总结

    1.执行原理 今天我们来说说mysql,作为目前使用最为广泛的数据库,mysql中有很多值得我们深挖的技术和思想。首先,先来聊聊一条sql语句是如何运行的。 ①与MySQL建...

  • 120
    jVM总结

    1.类加载过程 话不多少,先上图! 首先在讲之前宏观上我们要理解一点,JVM本身是运行在操作系统上的,它在启动时会向操作系统申请一片内存空间供其自身使用,所以以下我说的内存都...

  • 120
    Mysql总结

    1.执行原理 今天我们来说说mysql,作为目前使用最为广泛的数据库,mysql中有很多值得我们深挖的技术和思想。首先,先来聊聊一条sql语句是如何运行的。 ①与MySQL建...

  • 120
    高并发多线程总结

    1.多线程基本概念 首先,我们要理解多线程编程,必须清楚几个基本概念:进程——进程是操作系统层面的概念,它是操作系统进行资源分配和调度的基本单位线程——线程是进程内部的程序流...

个人介绍
每个人都缺乏什么
我们才会瞬间不快乐
单纯很难 包袱很多
忘却一切 赢得片刻——安静!