基于Raft协议的NoSQL数据库的设计和实现-API 1. 设计展示 DistKV自己设计并实现了自己的好用的命令行语言,Demo如下:
1. String concept 1 2 3 4 5 6 7 8 9 10 11 distkv-cli > put "k1" "v1" distkv-cli > ok distkv-cli > str.put "k1" "v1" # the same as `put` distkv-cli > ok distkv-cli > get "k1" distkv-cli > "v1" distkv-cli > str.get "k1" # the same as `get` distkv-cli > "v1"
2. List concept 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 distkv-cli > list.put "k1" "v1" "v2" "v3" distkv-cli > ok distkv-cli > list.get "k1" distkv-cli > ["v1", "v2", "v3"] distkv-cli > list.lpush "k1" "v4" "v5" "v6" distkv-cli > ok distkv-cli > list.rpush "k1" "v7" distkv-cli > ok distkv-cli > list.lpop "k1" 2 distkv-cli > ok distkv-cli > list.get "k1" distkv-cli > ["v6", "v1", "v2", "v3", "v7"]
3. Set concept 1 2 3 4 5 6 7 8 9 10 11 distkv-cli > set.put "k1" "v1" "v2" "v3" distkv-cli > ok distkv-cli > set.get "k1" distkv-cli > {"v1", "v2", "v3"} distkv-cli > set.exists "k1" "v2" distkv-cli > true distkv-cli > set.exists "k1" "v4" distkv-cli > false
4. Dict concept 1 2 3 4 5 6 7 8 distkv-cli > dict.put "dict1" "k1" "v1" "k2" "v2" distkv-cli > ok distkv-cli > dict.get "dict1" distkv-cli > { "k1" : "v1", "k2" : "v2"} distkv-cli > dict.get "dict1" "k1" distkv-cli > "v1"
5. Table concept
Define your data structure in a schema file named mytables.sc
1 2 3 4 5 6 7 8 9 10 11 12 13 table TaskTable { [p]task_id: string; [i]driver_id: string; task_name: string; return_num: int; arguments: [string]; } table DriverTable { [p]driver_id: string; driver_name: string; actor_num: int; };
Start an distkv server and execute this command to create table:
1 2 > distkv-cli -p 12344 # connect to distkv server > create TaskTable, DriverTable from mytables.sc
Add data to the table:
1 2 3 4 5 6 7 8 > TaskTable.add "00001", "22222", "my_task", 3, ["1", "2"] < ok > TaskTable.add "00002", "99999", "my_task", 3, ["1", "2"] < ok > TaskTable.add "00003", "22222", "my_task", 3, ["1", "2"] < ok > DriverTable.add "22222", "my_driver", 10 < ok
Query all tasks by driver id:
1 2 3 4 5 6 > TaskTable.query (*) when driver_id == "22222" < < task_id driver_id task_name num_return arguments < "00001" "22222" "my_task" 3 ["1", "2"] < "00003" "22222" "my_task" 3 ["1", "2"] < 2 records
除此之外,我们还设计了JAVA端的调用语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 package com.distkv.client.example;import com.distkv.common.entity.sortedList.SlistEntity;import com.google.protobuf.InvalidProtocolBufferException;import java.util.LinkedList;import java.util.HashSet;import java.util.HashMap;import java.util.Arrays;import java.util.Map;import java.util.Set;import java.util.List;import java.util.ArrayList;import com.distkv.client.DefaultDistkvClient;public class DistkvUsageExample { public static void main (String[] args) throws InvalidProtocolBufferException { DefaultDistkvClient distkvClient = new DefaultDistkvClient ("distkv://127.0.0.1:8082" ); if (distkvClient.isConnected()) { distkvClient.strs().put("k1" , "v1" ); distkvClient.sets().put("k1" , new HashSet <>(Arrays.asList("v1" , "v2" , "v3" , "v3" ))); distkvClient.lists().put("k1" , new ArrayList <>(Arrays.asList("v1" , "v2" , "v3" ))); distkvClient.ints().put("k1" , 1 ); Map<String, String> map = new HashMap <>(); map.put("k1" , "v1" ); map.put("k2" , "v2" ); map.put("k3" , "v3" ); map.put("k4" , "v4" ); distkvClient.dicts().put("dict1" , map); LinkedList<SlistEntity> slist = new LinkedList <>(); slist.add(new SlistEntity ("a" , 10 )); slist.add(new SlistEntity ("b" , 8 )); slist.add(new SlistEntity ("c" , 6 )); slist.add(new SlistEntity ("d" , 4 )); distkvClient.slists().put("k1" , slist); distkvClient.slists().putMember("k1" , new SlistEntity ("s" ,100 )); String strResult = distkvClient.strs().get("k1" ); Set<String> setResult = distkvClient.sets().get("k1" ); List<String> listResult = distkvClient.lists().get("k1" ); Map<String, String> mapResult = distkvClient.dicts().get("dict1" ); LinkedList<SlistEntity> slistResult = distkvClient.slists().top("k1" , 3 ); int intResult = distkvClient.ints().get("k1" ); distkvClient.ints().incr("k1" , -2 ); int intResultAfterIncr = distkvClient.ints().get("k1" ); System.out.println("The result of distkvClient.strs().get(\"k1\") is: " + strResult); System.out.println("The result of distkvClient.sets().get(\"k1\") is: " + setResult); System.out.println("The result of distkvClient.lists().get(\"k1\") is: " + listResult); System.out.println("The result of distkvClient.dicts().get(\"dict1\") is: " + mapResult); System.out.println("The top3 entities in the \"k1\" of distkvClient.sortedLists() is: " + "{ First: " + slistResult.get(0 ).getMember() + "; Second: " + slistResult.get(1 ).getMember() + "; Third: " + slistResult.get(2 ).getMember() + "; }" ); System.out.println("In the key \"k1\" of distkvClient.sortedLists(), the member name is " + "\"a\", its rank is " + distkvClient.slists().getMember("k1" , "a" ).getSecond() + " and its score is " + distkvClient.slists().getMember("k1" , "a" ).getFirst()); System.out.println("The result of distkvClient.ints().get(\"k1\") is: " + intResult); System.out.println("The result of distkvClient.ints().get(\"k1\") " + "after increasing the value -2 is: " + intResultAfterIncr); distkvClient.disconnect(); } } }
1.2 错误码设计 Dst的错误码以特定的规则提供了用以明确用户命令语句在执行过程中发生的任何错误的信息。根据ErrorCode的规则,用户可以清楚的从中解读出错误的类信息。
1.2.1 ErrorCode设计思路: 1.错误码尽可能具有正则性。
我们希望dst的错误码应尽可能具有正则性,即不同错误码,同位的相同的码字应代表同性质的错误信息。
2.错误码的每一位都代表一级错误信息。
这意味着各种错误码在同一位上的不同码字代表着不同的错误类但这些错误类是同级别的。
3.错误码的低位代表的错误类是高位代表的错误类的概念上的子类。换言之,低位提供的错误信息是高位提供的错误信息的更具体错误。
关于“尽可能正则”的解释:
DistKV的错误信息有着一定的特殊性——每个数据类型下往往含有其独有的错误类,比如说Dict类下DictKeyNotFound错误类就是其所独有的。还有就是语法错误与解析错误应该是同级错误,但二者内含的子错误完全不同。这种特点使得Dst ErrorCode难以实现完全的正则化。
1.2.2 ErrorCode设计规则: Dst ErrorCode共4位,第一位为字母,后三位为数字。
第一位:错误发生的数据类/语法错误
对应关系:
Dict —— D
List —— L
Set —— S
String —— C
(SortedList —— O)
(Table —— T)
语法错误 —— X
第二位:通用高级错误类(待补充)
对应关系:
KeyNotFound —— 1
OutOfBounds —— 2
参数数量错误 —— 3
第三位:语法错误标识
对应关系:
无法理解的输入 —— 1
第四位:具体错误类(如果表错误包含在内的话,可能需要由第一位决定语义)
对应关系:
DictKeyNotFound —— 1
ListIndexOutOfBounds —— 2
SetNonExistentItem —— 3
SortedListNonFoundItem —— 4)
SortedListTopNumBePositive —— 5)
示例:
D100:
Dict类下发生的KeyNotFound,后两位为0是由于这不是语法错误,而且在发生KeyNotFound的情况下一般不会发生别的错误。
L202:
List类下发生的IndexOutOfBounds错误。
X010:
无法理解的输入错误。
Errorcode清单:
X010 —— 语法错误,无法理解的输入。请检查命令是否合法。
D100 —— Dict类命令,Key未找到。请检查命令中的Key内容是否正确。
D300 —— Dict类命令,参数数量错误。请确认命令的参数是否正确。(错误提示里提示所需的参数数量)
D001 —— Dict类命令,要查找的dict中的关键字未找到。请检查命令中的查找关键字是否正确。
…
L100 —— List类命令,Key未找到。请检查命令中的Key内容是否正确。
L202 —— List类命令,越界错误,List索引越界。请确认命令中的索引参数是否在List范围内。
L300 —— List类命令,参数数量错误。请确认命令的参数是否正确。(错误提示里提示所需的参数数量)
…
S100 —— Set类命令,Key未找到。请检查命令中的Key内容是否正确。
S300 —— Set类命令,参数数量错误。请确认命令的参数是否正确。(错误提示里提示所需的参数数量)
C100 —— String类命令,Key未找到。请检查命令中的Key内容是否正确。
C300 —— String类命令,参数数量错误。请确认命令的参数是否正确。(错误提示里提示所需的参数数量)