BerkeleyDB原理及其对应API
BerkeleyDB(简称为BDB)是一种以key-value为结构的嵌入式数据库引擎:
- 嵌入式:bdb提供了一系列应用程序接口(API),调用这些接口很简单,应用程序和bdb所提供的库一起编译/链接成为可执行程序;
- NOSQL:bdb不支持SQL语言,它对数据的管理很简单,bdb数据库包含若干条记录,每条记录由关键字和数据(key-value)两部分构成。数据可以是简单的数据类型,也可以是复杂的数据类型,例如C语言的结构体,bdb对数据类型不做任何解释,完全由程序员自行处理,典型的C语言指针的自由风格;
DB的设计思想是简单、小巧、可靠、高性能。如果说一些主流数据库系统是大而全的话,那么DB就可称为小而精。DB提供了一系列应用程序接口(API),调用本身很简单,应用程序和DB所提供的库在一起编译成为可执行程序。这种方式从两方面极大提高了DB的效率。第一:DB库和应用程序运行在同一个地址空间,没有客户端程序和数据库服务器之间昂贵的网络通讯开销,也没有本地主机进程之间的通讯;第二:不需要对SQL代码解码,对数据的访问直截了当。
DB对需要管理的数据看法很简单,DB数据库包含若干条记录,每一个记录由关键字和数据(KEY/VALUE)构成。数据可以是简单的数据类型,也可以是复杂的数据类型,例如C语言中结构。DB对数据类型不做任何解释, 完全由程序员自行处理,典型的C语言指针的"自由"风格。如果把记录看成一个有n个字段的表,那么第1个字段为表的主键,第2--n个字段对应了其它数据。DB应用程序通常使用多个DB数据库,从某种意义上看,也就是关系数据库中的多个表。DB库非常紧凑,不超过500K,但可以管理大至256T的数据量。
DB的设计充分体现了UNIX的基于工具的哲学,即若干简单工具的组合可以实现强大的功能。DB的每一个基础功能模块都被设计为独立的,也即意味着其使用领域并不局限于DB本身。例如加锁子系统可以用于非DB应用程序的通用操作,内存共享缓冲池子系统可以用于在内存中基于页面的文件缓冲。
BDB可以分为几个子系统:
- 存储管理子系统 (Storage Subsystem)
- 内存池管理子系统 (Memory Pool Subsystem)
- 事务子系统 (Transaction Subsystem)
- 锁子系统 (Locking Subsystem)
- 日志子系统 (Logging Subsystem)
BDB的每一个基础功能模块都被设计为独立的,也即意味着其使用领域并不局限于BDB本身,例如加锁子系统可以用于非BDB应用程序的通用操作,内存共享缓冲池子系统可以用于在内存中基于页面的文件缓冲。
BDB库的安装方法:从官网下载、解压后执行下面的命令
cd build_unix
../dist/configure
make
make install
DB缺省把库和头文件安装在目录 /usr/local/BerkeleyDB.6.1/ 下,使用下面的命令就可正确编译程序:
gcc test.c -I/usr/local/BerkeleyDB.6.1/include/ -L/usr/local/BerkeleyDB.6.1/lib/ -ldb -lpthread
下面是一个BDB API使用的例子:
#include <db.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h> typedef struct customer {
int c_id;
char name[10];
char address[20];
int age;
} CUSTOMER; /* 数据结构DBT在使用前,应首先初始化,否则编译可通过但运行时报参数错误 */
void init_DBT(DBT * key, DBT * data)
{
memset(key, 0, sizeof(DBT));
memset(data, 0, sizeof(DBT));
} int main(void)
{
DB_ENV *dbenv;
DB *dbp;
DBT key, data; int ret = 0;
int key_cust_c_id = 1;
CUSTOMER cust = {1, "chenqi", "beijing", 30}; /* initialize env handler */
if (ret = db_env_create(&dbenv, 0)) {
printf("db_env_create ERROR: %s\n", db_strerror(ret));
goto failed;
} u_int32_t flags = DB_CREATE | DB_INIT_MPOOL | DB_INIT_CDB | DB_THREAD;; if (ret = dbenv->open(dbenv, "/data0/bdb_test", flags, 0)) {
printf("dbenv->open ERROR: %s\n", db_strerror(ret));
goto failed;
} /* initialize db handler */
if (ret = db_create(&dbp, dbenv, 0)) {
printf("db_create ERROR: %s\n", db_strerror(ret));
goto failed;
} flags = DB_CREATE | DB_THREAD; if (ret = dbp->open(dbp, NULL, "single.db", NULL, DB_BTREE, flags, 0664)) {
printf("dbp->open ERROR: %s\n", db_strerror(ret));
goto failed;
} /* write record */
/* initialize DBT */
init_DBT(&key, &data);
key.data = &key_cust_c_id;
key.size = sizeof(key_cust_c_id);
data.data = &cust;
data.size = sizeof(CUSTOMER); if (ret = dbp->put(dbp, NULL, &key, &data, DB_NOOVERWRITE)) {
printf("dbp->put ERROR: %s\n", db_strerror(ret));
goto failed;
} /* flush to disk */
dbp->sync(dbp, 0); /* get record */
init_DBT(&key, &data);
key.data = &key_cust_c_id;
key.size = sizeof(key_cust_c_id);
data.flags = DB_DBT_MALLOC; if (ret = dbp->get(dbp, NULL, &key, &data, 0)) {
printf("dbp->get ERROR: %s\n", db_strerror(ret));
goto failed;
} CUSTOMER *info = data.data; printf("id = %d\nname=%s\naddress=%s\nage=%d\n",
info->c_id,
info->name,
info->address,
info->age); /* free */
free(data.data); if(dbp) {
dbp->close(dbp, 0);
} if (dbenv) {
dbenv->close(dbenv, 0);
} return 0; failed: if(dbp) {
dbp->close(dbp, 0);
} if (dbenv) {
dbenv->close(dbenv, 0);
} return -1;
}
上面的例子中使用了很多BDB库中的API,在下面会再具体介绍它们。
访问方法
访问方法对应了数据在硬盘上的存储格式和操作方法。在编写应用程序时,选择合适的算法可能会在运算速度上提高1个甚至多个数量级。大多数数据库都选用B+树算法,DB也不例外,同时还支持HASH算法、Recno算法和Queue算法。接下来,我们将讨论这些算法的特点以及如何根据需要存储数据的特点进行选择。
- BTree:有序平衡树结构;
- Hash:扩展线性哈希表结构(extended linear hashing);
- Queue:由有固定长度的记录组成的队列结构,每个记录使用一个逻辑序列号作为键值,逻辑纪录号由算法本身生成,这和关系型数据库中逻辑主键通常定义为int AUTO型是同一个概念;支持在队尾快速插入,和从队首取出(或删除)记录;并提供记录级别的加锁操作,从而支持对队列的并发访问。
- Recno:同时支持固定长度的记录和变长记录,并且提供支持flat text file的永久存储和数据在读时提供一个快速的临时存储空间;
说明:
BTree和Hash的key和value都支持任意复杂类型,并且也允许存在key重复的记录;
Queue和Recno的key只能是逻辑序列号,两者基本上都是建立在Btree算法之上,提供存储有序数据的接口。前者的序列号是不可变的,后者的序列号可以是可变,也可以是不变;
可变,指的是当记录被删除或者插入时,编号改变;不变,指的是不管数据库如何操作,编号都不改变。在Queue算法中编号总被不变的。在Recno算法中编号是可变的,即当记录被删除或者插入时,数据库里的其他记录的编号也可能会改变。
另外,Queue的value为定长结构,而Recno的value可以为定长,也可以为变长结构;
对算法的选择首先要看关键字的类型,如果为复杂类型,则只能选择BTree或HASH算法,如果关键字为逻辑记录号,则应该选择Recno或Queue算法。
当工作集key有序时,BTree算法比较合适;如果工作集比较大且基本上关键字为随机分布时,选择HASH算法。
Queue算法只能存储定长的记录,在高的并发处理情况下,Queue算法效率较高;如果是其它情况,则选择Recno算法,Recno算法把数据存储为flat text file。
Access Method |
Description |
Choosing Occasion |
BTree |
关键字有序存储,并且其结构能随数据的插入和删除进行动态调整。为了代码的简单,Berkeley DB没有实现对关键字的前缀码压缩。B+树支持对数据查询、插入、删除的常数级速度。关键字可以为任意的数据结构。 |
1、 当Key为复杂类型时。 2、 当Key有序时。 |
Hash |
DB中实际使用的是扩展线性HASH算法(extended linear hashing),可以根据HASH表的增长进行适当的调整。关键字可以为任意的数据结构。 |
1、 当Key为复杂类型。 2、 当数据较大且key随机分布时。 |
Recno |
要求每一个记录都有一个逻辑纪录号,逻辑纪录号由算法本身生成。相当于关系数据库中的自动增长字段。Recho建立在B+树算法之上,提供了一个存储有序数据的接口。记录的长度可以为定长或不定长。 |
1、 当key为逻辑记录号时。 2、 当非高并发的情况下。 |
Queue |
和Recno方式接近, 只不过记录的长度为定长。数据以定长记录方式存储在队列中,插入操作把记录插入到队列的尾部,相比之下插入速度是最快的。 |
1、当key为逻辑记录号时。 2、定长记录。 3、 高并发的情况下。 |
数据结构
数据库环境句柄结构DB_ENV:环境在DB中属于高级特性,本质上看,环境是多个数据库的包装器。当一个或多个数据库在环境中打开后,环境可以为这些数据库提供多种子系统服务,例如多线/进程处理支持、事务处理支持、高性能支持、日志恢复支持等。
数据库句柄结构DB:包含了若干描述数据库属性的参数,如数据库访问方法类型、逻辑页面大小、数据库名称等;同时,DB结构中包含了大量的数据库处理函数指针,大多数形式为 (*dosomething)(DB *, arg1, arg2, …),其中最重要的有open、close、put、get等函数。
数据库记录结构DBT:DB中的记录由关键字和数据构成,关键字和数据都用结构DBT表示。实际上完全可以把关键字看成特殊的数据。结构中最重要的两个字段是 void * data和u_int32_t size,分别对应数据本身和数据的长度。
数据库游标结构DBC:游标(cursor)是数据库应用中常见概念,其本质上就是一个关于特定记录的遍历器。注意到DB支持多重记录(duplicate records),即多条记录有相同关键字,在对多重记录的处理中,使用游标是最容易的方式。
DB中核心数据结构在使用前都要初始化,随后可以调用结构中的函数(指针)完成各种操作,最后必须关闭数据结构。从设计思想的层面上看,这种设计方法是利用面向过程语言实现面对对象编程的一个典范。
DB_ENV *dbenv; // 环境句柄
DB *dbp; // 数据库句柄
DBT key, value; // 纪录结构
DBC *cur; // 游标结构
数据库每条记录包含两个DBT结构,一个是key,一个是value。
typedef struct {
void *data; // 数据buf
u_int32_t size; // 数据大小
u_int32_t ulen; //
u_int32_t dlen; // 数据长度
u_int32_t doff; // 数据开始处
u_int32_t flags;
} DBT;
数据库环境
- 在一个磁盘文件中包含多个数据库;
- 多进程和多线程支持;
- 事务处理;
- 高可用支持(主从库复制);
- 日志系统(可用于数据库异常恢复);
DB_ENV *dbenv;
db_env_create(&dbenv, 0); // 创建数据库环境句柄
dbenv->open(dbenv, path, flags, 0); // 打开数据库环境, path是环境的目录路径, flag参数参考下面介绍
dbenv->close(dbenv, 0); // 关闭数据库环境
dbenv->err(dbenv, ret, formart, ...); // 错误调试
DB_CREATE // 打开的环境不存在的话就创建它
DB_THREAD // 支持线程
DB_INIT_MPOOL // 初始化内存中的cache
DB_INIT_CDB
BDB 环境的使用例子:
/* 定义一个环境变量,并创建 */
DB_ENV *dbenv;
db_env_create(&dbenv, 0); /* 在环境打开之前,可调用形式为dbenv->set_XXX()的若干函数设置环境 */ /* 通知DB使用Rijndael加密算法(参考资料>)对数据进行处理 */
dbenv->set_encrypt(dbenv, "encrypt_string", DB_ENCRYPT_AES); /* 设置DB的缓存为5M */
dbenv->set_cachesize(dbenv, 0, 5 * 1024 * 1024, 0); /* 设置DB查找数据库文件的目录 */
dbenv->set_data_dir(dbenv, "/usr/javer/work_db"); /* 设置出错时的回调函数 */
dbenv->set_errcall(dbenv, callback); /* 将错误信息写到指定文件 */
dbenv->set_errfile(dbenv, file); /* 打开数据库环境,注意后四个标志分别指示DB启动日志、加锁、缓存、事务处理子系统 */
dbenv->open(dbenv,home,DB_CREATE|DB_INIT_LOG|DB_INIT_LOCK| DB_INIT_MPOOL |DB_INIT_TXN, 0); /* 在环境打开后,则可以打开若干个数据库,所有数据库的处理都在环境的控制和保护中。
注意db_create函数的第二个参数是环境变量 */
db_create(&dbp1, dbenv, 0);
dbp1->open(dbp1, ……);
db_create(&dbp2, dbenv, 0);
dbp1->open(dbp2, ……); /* do something with the database */
/* 最后首先关闭打开的数据库,再关闭环境 */
dbp2->close(dbp2, 0);
dbp1->close(dbp1, 0);
dbenv->close(dbenv, 0);
数据库操作
DB数据库是一组K-V记录的集合,key和value都是DBT结构存储的,与数据库操作有关的API:
DB* dbp;
db_create(&dbp, dbenv, 0); // 获取数据库句柄
dbp->open(dbp, NULL, filename, NULL, DB_BTREE, flags, 0);
dbp->close(&dbp, 0); // 在关闭数据库前,先关闭所有打开的游标
dbp->sync(dbp, 0) // 刷新cache,同步到磁盘,close操作会隐含调用该过程
dbp->remove(dbp, filename, NULL, 0) // 移除数据库,不要移除已打开的数据库
dbp->rename(dbp, oldname, NULL, newname, 0) // 数据库重命名,不要重命名已打开的数据库 dbp->put(dbp, NULL, &key, &data, DB_NOOVERWRITE); // DB_NOOVERWRITE不允许重写已存在的key
dbp->get(dbp, NULL, &key, &data, flags); // 如果存在key重复的记录,只返回第一个,或者使用游标
dbp->del(dbp, NULL, &key, 0); // 删除指定key的记录
dbp->truncate(dbp, NULL, u_int32_t* count, 0); // 删除所有记录,count中返回被删除的记录个数
dbp->get_open_flags(dbp, &open_flags); // 获取打开的flags,仅对已打开的数据库才有意义
dbp->set_flags(dbp, flags); // 设置打开的flags
DB_CREATE //如果打开的数据库不存在,就创建它;不指定这个标志,如果数据库不存在,打开失败!
DB_EXC //与DB_CREATE一起使用,如果打开的数据库已经存在,则打开失败;不存在,则创建它;
DB_RDONLY //只读的方式打开,随后的任何写操作都会失败;
DB_TRUNCATE //清空对应的数据库磁盘文件;
DB_DUPSORT //
get方法返回DB_NOTFOUND时表示没有匹配记录,其最后一个参数flags:
DB_GET_BOTH // get方法默认只匹配key,该flag将返回key和data都匹配的第一条记录
DB_MULTIPLE // get方法默认只返回匹配的第一条记录,该flag返回所有匹配记录
使用get方法时,data参数是DBT结构,该DBT的flags参数可以定义为:
DB_DBT_USERMEM // 使用自己的内存存储检索的data
DB_DBT_MALLOC // 使用DB分配的内存,用完后要手动free
DB提供的内存对齐方式可能不符合用户数据结构的需求,所以尽量使用我们自己的内存。
用DB_DBT_USERMEM方式改写前面的例子:
/* get record */
CUSTOMER info;
init_DBT(&key, &data); key.data = &key_cust_c_id;
key.size = sizeof(key_cust_c_id); data.data = &info;
data.ulen = sizeof(CUSTOMER);
data.flags = DB_DBT_USERMEM; if (ret = dbp->get(dbp, NULL, &key, &data, 0)) {
printf("dbp->get ERROR: %s\n", db_strerror(ret));
goto failed;
} printf("id = %d\nname=%s\naddress=%s\nage=%d\n",
info.c_id,
info.name,
info.address,
info.age);
db_strerror(errno) // 将错误编码映射成一个字符串
dbp->set_errfile(dbp, FILE*) // 设置错误文件
dbp->set_errcall(dbp, void(*)(const DB_ENV *dbenv, const char* err_pfx, const char* msg)) // 定义错误处理的回调函数
dbp->set_errpfx(dbp, format...) // 加上错误消息前缀
dbp->err(dbp, ret, format...) // 生成错误消息,并按优先级发给set_errcall定义的错误处理回调函数、set_errfile定义的文件、stderr;
dbp->errx(dbp, format...) // 与dbp->err类似,但没有返回值ret这个额外参数
错误消息由一个前缀(由set_errpfx定义)、消息本身(由err或errx定义)和一个换行符组成。
游标
DBC *cur;
dbp->cursor(dbp, NULL, &cur, 0); // 初始化游标对象
cur->close(cur); // 关闭游标
cur->get(cur, &key, &data, flags); // 迭代记录,当没有可迭代的记录时,返回DB_NOTFOUND
cur->put(cur, &key, &data, flags);
cur->del(cur, 0); // 删除游标指向的记录
DB_NEXT // 从第一条纪录遍历到最后一条纪录;
DB_PREV // 逆序遍历,从最后一条纪录开始;
DB_SET // 移动游标到键值等于给定值的第一条纪录;
DB_SET_RANGE // 如果数据库使用BTREE的算法,移动游标到键值大于或等于给定值的纪录集合;
DB_GET_BOTH // 移动游标到键值和数据项均等于给定值的第一条记录;
DB_GET_BOTH_RAGNE // 移动游标到键值等于给定值,数据项大于或等于给定值的纪录集合; DB_NEXT_DUP // 获取下一个key重复的记录;
DB_PREV_DUP // 获取上一个key重复的记录;
DB_NEXT_NODUP // 获取下一个key不重复的记录;
DB_PREV_NODUP // 获取上一个key不重复的记录;
DB_NODUPDATA // 如果插入的key已存在,返回DB_KEYEXIST,如果不存在,则记录的插入顺序由其在数据库的插入顺序决定;
DB_KEYFIRST // 在key重复的集合里面放在第一个位置;
DB_KEYLAST // 在key重复的集合里面放在最后一个位置;
DB_CURRENT // replace
secondary数据库
建立secondary database之后,如果在primary database中新增或删除记录,会触发对secondary database的更新。
注意:我们不能直接更新secondary database,任何写secondary database的操作都会失败,secondary database的变更需要通过修改primary database实现。但这里有一个例外,允许在secondary database中删除记录。
primary_dbp->associate(primary_dbp, NULL, second_dbp, key_creator, 0);
int key_creator(DB* dbp, const DBT* pkey, const DBT* pdata, DBT* skey);
一个例子:
DB *dbp, *sdbp; /* Primary and secondary DB handles */
u_int32_t flags; /* Primary database open flags */
int ret; /* Function return value */ typedef struct vendor {
char name[MAXFIELD]; /* Vendor name */
char street[MAXFIELD]; /* Street name and number */
char city[MAXFIELD]; /* City */
char state[3]; /* Two-digit US state code */
char zipcode[6]; /* US zipcode */
char phone_number[13]; /* Vendor phone number */
char sales_rep[MAXFIELD]; /* Name of sales representative */
char sales_rep_phone[MAXFIELD]; /* Sales rep's phone number */
} VENDOR; /* Primary */
ret = db_create(&dbp, NULL, 0);
if (ret != 0) {
/* Error handling goes here */
} /* Secondary */
ret = db_create(&sdbp, NULL, 0);
if (ret != 0) {
/* Error handling goes here */
} /* Usually we want to support duplicates for secondary databases */
ret = sdbp->set_flags(sdbp, DB_DUPSORT);
if (ret != 0) {
/* Error handling goes here */
} /* Database open flags */
flags = DB_CREATE; /* If the database does not exist, create it.*/ /* open the primary database */
ret = dbp->open(dbp, NULL, "my_db.db", NULL, DB_BTREE, flags, 0);
if (ret != 0) {
/* Error handling goes here */
} /* open the secondary database */
ret = sdbp->open(sdbp, NULL, "my_secdb.db", NULL, DB_BTREE, flags, 0);
if (ret != 0) {
/* Error handling goes here */
} /* Callback used for key creation. Not defined in this example. See the next section. */
int get_sales_rep(DB *sdbp, /* secondary db handle */
const DBT *pkey, /* primary db record's key */
const DBT *pdata, /* primary db record's data */
DBT *skey) /* secondary db record's key */
{
VENDOR *vendor;
/* First, extract the structure contained in the primary's data */
vendor = pdata->data;
/* Now set the secondary key's data to be the representative's name */
memset(skey, 0, sizeof(DBT));
skey->data = vendor->sales_rep;
skey->size = strlen(vendor->sales_rep) + 1;
/* Return 0 to indicate that the record can be created/updated. */
return (0);
} /* Now associate the secondary to the primary */
dbp->associate(dbp, NULL, sdbp, get_sales_rep, 0);
页面大小
DB* dbp;
dbp->set_pagesize() // 设置page size
dbp->stat() // 查看page size
缓存
DB可以将那些经常访问到记录cache 到内存里面,从而加快读写速度。
dbp->set_cachesize(dbp, gbytes, bytes, ncache); // 通过数据库句柄设置cache大小
dbenv->set_cachesize(dbp, gbytes, bytes, ncache); // 通过环境句柄设置cache大小(全局)
from:https://www.cnblogs.com/chenny7/p/4864547.html
BerkeleyDB原理及其对应API的更多相关文章
- Android Animation学习(一) Property Animation原理介绍和API简介
Android Animation学习(一) Property Animation介绍 Android Animation Android framework提供了两种动画系统: property a ...
- kafka系列九、kafka事务原理、事务API和使用场景
一.事务场景 最简单的需求是producer发的多条消息组成一个事务这些消息需要对consumer同时可见或者同时不可见 . producer可能会给多个topic,多个partition发消息,这些 ...
- Elasticsearch由浅入深(六)批量操作:mget批量查询、bulk批量增删改、路由原理、增删改内部原理、document查询内部原理、bulk api的奇特json格式
mget批量查询 批量查询的好处就是一条一条的查询,比如说要查询100条数据,那么就要发送100次网络请求,这个开销还是很大的如果进行批量查询的话,查询100条数据,就只要发送1次网络请求,网络请求的 ...
- [原创]java WEB学习笔记44:Filter 简介,模型,创建,工作原理,相关API,过滤器的部署及映射的方式,Demo
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
- Selenium 定位元素原理,基本API,显示等待,隐式等待,重试机制等等
Selenium 如何定位动态元素: 测试的时候会遇到元素每次变动的情况,例如: <div id="btn-attention_2030295">...</di ...
- Web API 处理机制剖析 --- 拨开迷雾看本质
前言 最近开发了几个项目,用到了web api,也通过项目加深了对web api的理解.本文试图从内部原理讲解web api的本质.透过重重迷雾,看清本质,就能更好的把握和利用好web api. 1 ...
- Python API:openstack
OpenStack 是一个越来越流行的.用于部署基础架构即服务 (IaaS) 云的开源解决方案.OpenStack 附带了一个仪表板 Web 应用程序,非常适合执行手动任务,比如启动单个虚拟机 (VM ...
- 028.核心组件-API Server
一 Kubernetes API Server原理 1.1 API Server功能 Kubernetes API Server的核心功能是提供Kubernetes各类资源对象(如Pod.RC.Ser ...
- Kubernetes API作为权威接口,Kubernetes将成为软件的通用控制平面
1创新之处在于API这是有关 Kubernetes的两部分系列中的第一篇.第一部分是一个答案:影响Kubernetes设计的关键思想是什么?Kubernetes会将它与其他平台区分开来吗?第二部分是关 ...
随机推荐
- mini2440移植uboot 2014.04(四)
我修改的代码已经上传到github上,地址:https://github.com/qiaoyuguo/u-boot-2014.04-mini2440.git 参考文章: <mini2440移植u ...
- apache下配置多域名多目录的应用
引言:阿里云centos apache web服务器中配置不同域名访问不同的目录,达到类似增加虚拟主机的效果: 案例: 如有2个www.a.com ,www.b.com 域名, 访问www.a.com ...
- linux设置防火墙
这样其他主机还是无法访问我们的数据库,linux关闭防火墙,其他主机就可以访问了. 关闭防火墙:/etc/init.d/iptables stop 开启防火墙:/etc/init.d/iptable ...
- 深入理解JVM - 早期(编译期)优化
Java“编译期”是一段“不确定”的操作过程:可能是指一个前端编译器(编译器的前端)把*.java文件转变为*.class文件的过程:可能是指虚拟机的后端运行期编译器(JIT编译器,Just In T ...
- pow,sqrt使用时需注意
使用时注意类型,可见两者皆不可以用int 1.pow 函数声明: double pow (double base , double exponent); float pow (float base , ...
- Python set运算 集合差集,并集,交集,list去重复
在没有发现方便的set运算之前,都是用遍历list查找两个集合的差别. 比如, 找list1和list2的差集 for i in list1: if not i in list2: print i 现 ...
- 【leetcode刷题笔记】Reverse Linked List II
Reverse a linked list from position m to n. Do it in-place and in one-pass. For example:Given 1-> ...
- ACM学习历程—UESTC 1215 Secrete Master Plan(矩阵旋转)(2015CCPC A)
题目链接:http://acm.uestc.edu.cn/#/problem/show/1215 题目大意就是问一个2*2的矩阵能否通过旋转得到另一个. 代码: #include <iostre ...
- 洛谷【P1898】缘分计算
我对模拟的理解:http://www.cnblogs.com/AKMer/p/9064018.html 题目传送门:https://www.luogu.org/problemnew/show/P189 ...
- swift-get-nodes简单使用
在参考http://blog.csdn.net/cywosp/article/details/12850645文章对对象的具体物理磁盘位置进行查找时,发现两个问题: 1. 在使用swift+keyst ...