Paxos算法是莱斯利·兰伯特(Leslie Lamport)于1990年提出的一种基于消息传递且具有高度容错性的一致性算法,是目前公认的解决分布式一致性问题最有效的算法之一。
两篇经典论文
我主要针对《从Paxos到Zookeeper分布式一致性原理与实现
》书里第二章的内容和《Paxos Made Simple》论文的原文,对Paxos算法进行详细的阐述。
前游戏热爱者
Lambda的基本语法是
(parameters) -> expression
或者
(parameters) -> {expression;}
这里讨论一种异常情况。假设你用与Runnable同样的签名声明了一个函数接口,我们称之为Task。
1 | interface Task { |
如果我们用以前的匿名类是没问题的:1
2
3
4
5doSomething(new Task() {
void execute() {
System.out.println("Danger danger!!");
}
})
但是将这种匿名类转换为Lambda表达式时,就导致了一种晦涩的方法调用,因为Runnable和Task都是合法的目标类型:
1 | doSomething(() -> System.out.println("Danger danger!!")); |
这时候编译器会报一个编译的异常,Ambigulous method call
,这时候需要对Task尝试使用显示的类型转换来解决这种模棱两可的情况:
1 | doSomething((Task)() -> System.out.println("Danger danger!!")); |
Java8引入了一种新的机制。Java8中的接口现在支持在声明方法的同事提供实现。通过两种方式可以完成这种操作。
在新的Collection接口中的stream和List接口中的sort都是这样的。定义如下:
1 | default Stream<E> stream() { |
默认方法设计的目的是为了解决向接口添加方法。
那么既然接口也能实现方法,抽象类也能实现方法,区别如下:
先看下面的场景1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public interface A {
default void hello() {
System.out.println("Hello from A");
}
}
public interface B {
default void hello() {
System.out.println("Hello from B");
}
}
public class C implements B, A {
public static void main(String[] args) {
new C().hello();
}
}
###解决问题的三条规则
如果一个类使用相同的函数前面从多个地方继承了方法,通过三条规则可以进行判断。
Java8引入了一种新的语法X.super.m()
,其中X是你希望调用的m方法所在的父接口。
本文翻译自Index 1,600,000,000 Keys with Automata and Rust,所以标题也直译过来。
有限状态机(FSM, finite state machine)可以用来紧密地存储有序集合和有序键值对,并且可以实现快速搜索。本文中,我会表明怎样用FSM来作为数据结构存储这样的数据。
FSM是一个状态的集合和状态转移的集合。一个起始状态,0个或多个结束状态。一个FSM在同一时间只有一个状态。
FSM非常常见,并且可以用来描述一系列过程。比如我家猫咪Cauchy一天的日常生活:
里面有一些”asleep”或者”eating”的状态,一些转移”food is served”, “something moved”。这里没有结束状态,如果结束了,那真是太恐怖了!
FSM近似的表达了现实中的情况。Cauchy不可能同时吃饭和睡觉,这跟FSM中同一时刻只有一个状态是一样的。并且,从一个状态转移到另一个状态需要外部环境的一个输出。需要睡觉,可能是因为”吃饱了”, “累了”等等。不管他睡得多死,”听到外面的声音”,它总会醒过来。
有序集合里的键按照字典序排序。典型的应用是二叉查找树和B树。无序集合,典型应用就是哈希表。这里,我们先描述一个确定无环有限状态接收器(deterministic acyclic finite state acceptor),即FSA。
一个FSA需要满足以下条件:
那么,怎么用这些特性来表示一个集合呢。诀窍在于,key作为FSA的状态转移。这样,给定一个输入key,我们可以知道这个key这个key是否在FSA中。
假设一个集合,只有一个key”jul”。FSA就像下面这样:
这时候如果问FSA,是否包含”jul”。处理顺序如下:
输入结束,这时候判断一下FSA是否处在final状态(图中用双圈表示),表明jul确实在set中。
这时候如果问FSA,是否包含”jun”。处理顺序如下:
FSA不动,因为状态2只接收’l’的转移,但是当前输入为’n’。因此处理结束,也表明集合中不包含”jun”。
这时候如果问FSA,是否包含”ju”。处理顺序如下:
判断一下,此时是否处于final状态。
值得注意的是,判断一个key是否存在,受限于key的长度,而不是set的大小。
下面把key”mar”添加到FSA中去,这时候FSA的表现如下:
FSA变得稍微复杂一点,状态0可以有两个转移。如果起始输入mar,它会先转移到1状态。
还有一个需要注意的是,状态3被jul和mar两个key共享。即,状态3可以由l和r转移过来。这种共享的方式,可以用更少的空间保存更多的信息。
如果再加入jun,FSA表现如下:
看到变化了么?只有一点点变化。FSA看起来和之前的几乎没什么区别。唯一变化的地方在状态5多了一个转移。FSA其实没有新增状态节点,因为jun和jul共享了前缀ju
下面展示一个更复杂的FSA,包含三个key,october,november,december。
因为有相同的后缀ber,在FSA中只需要编码一次就行了。两个key有更大的相同的后缀,ember。
在介绍FST之前,我们先看看,如何来遍历FSA中所有的key呢?
为了阐述这个过程,还用一个之前的一个简单的图,有三个key,jul,jun和mar。
遍历方式如下:
这个算法直接应用一个栈存储访问过的状态,和一个栈存储相应的转移。时间复杂度为O(n),空间复杂度O(k),k是set中最长的键的大小。
和有序集合类似,只是多了一个输出。有序map常用在二叉查找树和b树,无序map就是hashtbale。这里我们介绍一个deterministic acyclic finite state transducer,确定无环有限状态转移器,FST。
FST满足以下特性:
FST和FSA很像,但是对于一个key,FSA只回答了”yer or no”,FST不仅回答”yes or no”,还好返回和这个key相关的一个值。
在有序集合中,只需要把key保存在转移时。但是在这里,还需返回与key对于的value。
一种方法是,在每次转移的时候添加一些值。当输入序列在状态之间转移的时候,输出序列也在慢慢增加。
还是看一个简单的例子吧。map中只包含一个数据jul,对应的value为7:
这和上面的集合差不多,只是在第一个转移状态j之后多了一个相关联的输出7.另外的转移u和l对应的输出都是0,所以图中就不显示了.
如果要判断,FST中是否存在key”jul”,并且需要对应的返回值,处理过程如下:
输入结束,状态3为final状态,因此key存在,value为7
下面把k-v,”mar 3”添加到FST中
在起始节点,多了一个新的转移m,对应输出为3.如果我们查询jul,那么应该和上面是一样的处理过程。
继续,当添加一个有相同前缀的key,会发生什么呢?
添加key jun,value 6
在状态5和状态3之间添加了一个转移n。但是还有另外两个变化
这个变化之后们可以正确查询jun和jul,并且返回正确的值。
这种key的属性确保了,即使共享前缀,对于每一个key,然后只有一个唯一的路径可以贯穿整个machine。因此,每个key也有唯一的value。我们要做的就是怎么把这些输出放在转移中去。
其实不仅可以共享前缀,还可以共享后缀。对于两个key tuesday和thursday,分别对于输出3和5.
这两个key有相同的前缀t,相同的后缀sday,按照图里的方式可以保证输出的正确性。
这里在描述输出的时候,其实有一点局限,如果输出不是整形。确实,在FST里用做输出的类型必须满足以下特性:
trie树,前缀树。和FSA的区别在于,FSA可以共享前缀和后缀。对于键mon,tues,thus来说,FSA如下:
而trie树只共享前缀,如下:
构建trie树很直接的,对于一个给定的输入,只需要去看看有没有相同的前缀。直到找出相同的前缀,余下的直接转移到一个final状态就可以了。
FSA和trie的区别在于,共享后缀。因此一个FSA的空间会比trie少很多,但是构建起来却更复杂,因此我们如果按照key的字典序插入的话,会好很多,还是用图片来说明。
对于三个key,mon,tues和thurs。按照字典序,插入顺序mon,thurs和tues。先插入mon:
下面插入thurs:
插入thurs的时候,会导致之前的mon被冻结。当FSA中一部分被冻结的时候,我们知道,它以后再也不会被更改了。因为按照字典序排序的,后面的key肯定都是大于等于thurs的。因此不会和mon有相同前缀的key插入了。蓝色的state代表被冻结住,以后不会被更改但是可以被复用。
虚线的状态表示thurs还没有被真正加入到FSA中去,下面插入tues:
在这一步里,我们可以确定hurs会被冻住。因为将会不会有和它有相同前缀的词插入进来了。因为thurs和mon可以有相同的final state了。
这里状态4仍然是虚线,因为还不能确定t开头的key还有没有了。如果下面插入zon:
看到,这时状态4已经被冻住了,因为不会在有t开头的key出现了,另外thurs和tues有一个共同的后缀s,因此状态7和状态9被合并了。
最后,在结束操作以后,把FSA的最后一部分冻住,一个完整的没有重复的结构如下:
因为mon和zon有相同的后缀,因此它们除了第一个状态转移不一样,剩下的可以重复利用。
下面快速说一下FST构建,插入键值对 mon-2,tues-3,thurs-5.
直接上图,插入mon-2
对于第一步,我们也可以这样分配输出的值
其实这样也是可以的,但是在算法上来说,把输出放在靠近初始状态的地方,代码写起来更简单。
插入thurs-5
插入tues-3
在把状态0-4之间的输出从5变为3的之后,需要把4之后所有的输出全部加2,除了新添加的key,这样就可以保持输出的平衡。
下面插入一个tye-99
最后的完全形态
转载一篇Google的Java编程风格指南,自己手打一遍,也算是阅读,顺带添加以下原文和自己的理解。
This document serves as the complete definition of Google’s coding standards for source code in the Java™ Programming Language. A Java source file is described as being in Google Style if and only if it adheres to the rules herein.
Like other programming style guides, the issues covered span not only aesthetic issues of formatting, but other types of conventions or coding standards as well. However, this document focuses primarily on the hard-and-fast rules that we follow universally, and avoids giving advice that isn’t clearly enforceable (whether by human or tool).
netstat -ano
查看端口占用情况前面陆续介绍了Redis用到的所有主要数据结构。但是Redis并没有直接使用这些数据结构来实现键值对数据库,而是给予这些数据结构创建了一个对象系统,这个系统包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象这五种类型的对象,每种对象都用到了至少一种我们前面所介绍的数据结构。
好处是:
Redis的对象系统实现了基于引用计数的的内存回收机制;还通过引用技术技术实现了对象共享机制,可以让多个数据库键共享同一个对象来节约内存。
Redis对象带有访问时间记录信息,该信息可以同于计算数据库键的空转时长,在服务器启用了maxmemory功能的情况下,空转时长较大的那些键可能会有点被服务器删除。
压缩列表(ziplist)是列表键和哈希键的底层实现之一。当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较段的字符串,那么Redis就会使用压缩列表来做列表键的底层实现。
1 | redis> RPUSH lst 1 3 5 10086 "hello" "world" |