https://redisbook.readthedocs.io/en/latest/internal-datastruct/adlist.html
B+树的节点长什么样子

注意和B树区别
- 有n棵子树的非叶子结点中含有n个关键字(b树是n-1个),这些关键字不保存数据,只用来索引,所有数据都保存在叶子节点(b树是每个关键字都保存数据)。
- 所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
- 所有的非叶子结点可以看成是索引部分,结点中仅含其子树中的最大(或最小)关键字。
- 通常在b+树上有两个头指针,一个指向根结点,一个指向关键字最小的叶子结点。
B+树是二叉树吗
不是,是多叉树。m叉的多路平衡查找树
b+和红黑树的区别
差别有些大。红黑树是二叉树的变种, b树一个节点代表数据的集合或者范围。从应用看,红黑树适合小数据范围内的快速查找(比如map当量的容器)。b树适合大范围数据查找(db场合)
写个单例吧
记住构造函数是private
懒汉式,线程不安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
懒汉式,线程安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() { //加锁synchronized
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
饿汉式,线程安全
public class Singleton {
private static Singleton instance = new Singleton(); //instance 在类装载时就实例化
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
双锁机制,线程安全
public class Singleton {
private volatile static Singleton singleton; //volatile 内存可见
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) { //为空创建的时候加锁
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
登记式/静态内部类,线程安全
public class Singleton {
private static class SingletonHolder { //新建内部类来创建外部全局静态类
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() { //得到的时候注意是全局静态变量
return SingletonHolder.INSTANCE;
}
}
说说Synchronized
1.在多个线程操作共享数据的时候,保证对共享数据访问的线程安全性时使用。
2.悲观锁
3.它可以把任意一个非NULL的对象当作锁
4.将synchronized修饰在普通同步方法,那么该锁的作用域是在当前实例对象范围内,也就是说对于
public synchronized void access(){
//
}
Sync Demosd=new SyncDemo()
;这一个实例对象sd来说,多个线程访问access方法会有锁的限制。如果access已经有线程持有了锁,那这个线程会独占锁,直到锁释放完毕之前,其他线程都会被阻塞
5.修饰静态同步方法或者静态对象、类,那么这个锁的作用范围是类级别
SyncDemo sd=new SyncDemo();
SyncDemo sd2=new SyncDemo();
两个不同的实例sd和sd2, 如果sd这个实例访问access方法并且成功持有了锁,那么sd2这个对象如果同样来访问access方法,那么它必须要等待sd这个对象的锁释放以后,sd2这个对象的线程才能访问该方法,这就是类锁
- 同步方法块
public SyncDemo{
Object lock=new Object();
public void access(){
//do something
synchronized(lock){
//
}
}
}
数据库隔离级别
√表示可能出现的问题
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
Read uncommitted读未提交 | √ | √ | √ |
Read committed读提交 | × | √ | √ |
Repeatable read重复读 | × | × | √ |
Serializable序列化 | × | × | × |
脏读:
一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据
不可重复读:
在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的
幻读:
如果事务A在一个事务中运行SELECT count(1) from TABLE_X ,然后事务B在 TABLE_X 加入一条新数据并提交,当事务A再运行一次。count(1)结果不会是一样的。
Redis的五种对象类型
字符串对象
列表对象
哈希对象
集合对象
有序集合对象
redis List底层数据实现
1.双端链表
2.压缩列表
因为双端链表占用的内存比压缩列表要多, 所以当创建新的列表键时, 列表会优先考虑使用压缩列表作为底层实现, 并且在有需要的时候, 才从压缩列表实现转换到双端链表实现。
1.双端链表的实现由 listNode 和 list 两个数据结构构成, 下图展示了由这两个结构组成的一个双端链表实例:

其中, listNode 是双端链表的节点:
typedef struct listNode {
// 前驱节点
struct listNode *prev;
// 后继节点
struct listNode *next;
// 值
void *value;
} listNode
而 list 则是双端链表本身:
typedef struct list {
// 表头指针
listNode *head;
// 表尾指针
listNode *tail;
// 节点数量
unsigned long len;
// 复制函数
void *(*dup)(void *ptr);
// 释放函数
void (*free)(void *ptr);
// 比对函数
int (*match)(void *ptr, void *key);
} list;
listNode 的 value 属性的类型是 void * ,说明这个双端链表对节点所保存的值的类型不做限制
2.压缩列表
将一系列数据与其编码信息存储在一块连续的内存区域,这块内存物理上是连续的,逻辑上被分为多个组成部分,其目的是在一定可控的时间复杂读条件下尽可能的减少不必要的内存开销
什么时候使用压缩列表?
1)当list中的只包含少量列表项,每个列表项要么只包含小整数,要么就是长度比较短的字符串。
2)当hash里包含的kv都是小整数或者短字符串的话。
网友评论