三级封锁协议两段锁以及隔离级别

并发控制的主要方法是封锁(Locking)。就是要用正确的方式调度并发操作,使一个用户的事务在执行过程中不受其他事务的干扰,从而避免造成数据的不一致性。
封锁是使事务对它要操作的数据有一定的控制能力。封锁通常具有3个环节:第一个环节是申请加锁,即事务在操作前要对它将使用的数据提出加锁申请;第二个环节是获得锁,即当条件成熟时,系统允许事务对数据进行加锁,从而事务获得数据的控制权;第三个环节是释放锁,即完成操作后事务放弃数据的控制权。
基本的封锁类型有以下两种: 锁是事务实现并发控制隔离级别的实现方法
1.排它锁(Exclusive Locks,简称X锁)
排它锁也称为独占锁或写锁。一旦事务T对数据对象A加上排它锁(X锁),则只允许T读取和修改A,其他任何事务既不能读取和修改A,也不能再对A加任何类型的锁,直到T释放A上的锁为止。
2.共享锁(Share Locks,简称S锁)
共享锁又称读锁。如果事务T对数据对象A加上共享锁(S锁),其他事务只能再对A加S锁,不能加X锁,直到事务T释放A上的S锁为止。
在对数据进行加锁时,另外需要约定并执行一些规则和协议,其中包括何时申请锁,保持锁的时间以及何时释放等,这些规则就称为封锁协议(Locking Protocol){谁定义的?-确实找不出来,也许就是理论基础},其总共分为以下三级:
(1)一级封锁协议。一级封锁协议是事务T在修改数据之前必须先对其加X锁,直到事务结束才释放。 防止丢失更新。
(2)二级封锁协议。二级封锁协议是事务T对要修改数据必须先加X锁,直到事务结束才释放X锁;对要读取的数据必须先加S锁,读完后即可释放S锁。 防止丢失更新和脏读

 T1T2 
 Slock A  
 读A=20
unlcok A 事务还没完成,但是读完了马上释放
  
  Xlock A 
  A = 60 
  Unclock A
事务提交
 
 SlockA  
 A=60
不可重复读
  
 T1事务造成了不可重复读  

(3)三级封锁协议。三级封锁协议是事务T在读取数据之前必须先对其加S锁,在要修改数据之前必须先对其加X锁,直到事务结束后才释放所有锁。 防止丢失更新,脏读,不可重复读

相对于二级锁,Slock的范围加长了,开销自然大了。

执行了封锁协议之后,就可以克服数据库操作中的数据不一致所引起的问题。

参看图4。

Snap2

从图4的情况我们看到事务T1在执行过程中独自占用并加X锁,直到处理完之后再释放锁,T2虽然也需要使用,但是在封锁协议的约束之下,T2所要求的X 锁就被拒绝,因此必须处于等待状态,直到T1释放之后,T2才获得使用的权利,这样就不会发生使用冲突,避免了数据的丢失。这里我们看到,此处实际上是执行了一级封锁协议。

下面我们看图5。

Snap3

通过图5,能够清楚的看到,由于施行了封锁协议,使事务T1使用了共享锁占用A,B两块数据,这样T2需要加上的X锁就无法实现,(如果是S锁,虽然可以加上,但也不能够随便修改数据,只是读取一下数据。)当T1释放锁之后,T2就可以得到并使用锁了,这样读取的数据B仍然还是100,不影响A+B的结果,这就是可重复读取。因此我们看到,其实这里用的就是三级封锁协议

 

参看图6,事务T1在对数据C修改之前,先加上了X锁,修改后写回数据库,这时T2请求在C上添加S锁,因为T1加了X锁,T2只好等待,当T1因为某种原因撤销了修改的数据后,C就恢复了原来的数据100,等T1释放 X锁后T2获得C上的S锁,读到的还是C=100,因此避免了读出“脏”数据。这里使用的其实就是二级封锁协议。

Snap1

 

两阶段封锁协议

对锁机制,保证事务可串行性的最常用协议是两阶段封锁协议。该协议要求每个事务分两个阶段提出加锁和解锁申请:

(1)增长阶段。事务可以获得锁,但不能释放锁。

(2)缩减阶段。事务可以释放锁,但不能获得锁。

一开始,事务处于增长阶段,事务根据需要获得锁。一旦该事务释放了锁,它就进入缩减阶段,不能再发出加锁请求。

两阶段封锁协议实现了事务集的串行化调度,但同时,一个事务的失败可能会引起一连串事务的回滚。为避免这种情况的发生,我们需要进一步加强对两阶段封锁协议的控制,这就是:严格两阶段封锁协议和强两阶段封锁协议。

严格两阶段封锁协议除了要求封锁是两阶段之外,还要求事务持有的所有排它锁必须在事务提交之后方可释放。这个要求保证未提交事务所写的任何数据,在该事务提交之前均以排它锁封锁,防止其他事务读取这些数据。

强两阶段封锁协议,要求事务提交之前不得释放任何锁。使用锁机制的数据库系统,要么使用严格两阶段封锁协议,要么使用强两阶段封锁协议。

两阶段封锁协议并不保证不会发生死锁,数据库系统必须采取其他的措施,预防和解决死锁问题。

 

数据库并发操作存在的异常情况:

1. 更新丢失(Lost update):事务 T1 读取数据 A,然后对 A 进行运算修改,最后写回数据库。如果在 T1 读取和写回数据库之间,有其他事务修改了 A 值,就造成了丢失更新,因为 T1 是在旧的数据上进行的运算。

第一类丢失更新(lost update): 在完全未隔离事务的情况下,两个事物更新同一条数据资源,某一事物异常终止,回滚造成第一个完成的更新也同时丢失。

第二类丢失更新(second lost updates):是不可重复读的特殊情况,如果两个事务都读取同一行,然后两个都进行写操作,并提交,第一个事务所做的改变就会丢失。

说他是第二类更新,又说他其实是不可重复读的特例,实际上在隔离级别上又和不可重复读相同,大部分分类根本不提这个第二类丢失更新。

2. 脏读取(Dirty Reads):一个事务读取了另一个未提交的并行事务写的数据。事务 T1 修改了数据 A,然后事务 T2 读取了数据 A,然后事务 T1 回滚了事务。则T2读的是错误的数据。

3. 不可重复读取(Non-repeatable Reads):一个事务对同一行数据重复读取两次但是却得到了不同结果。例如在两次读取中途有另外一个事务对该行数据进行了修改并提交

4. 幻读(Phantom Reads):也称为幻像(幻影,虚读)。事务在操作过程中进行两次查询,第二次查询结果包含了第一次查询中未出现的数据(这里并不要求两次查询SQL语句相同)这是因为在两次查询过程中有另外一个事务插入数据造成的。

幻读和不可重复读可认为是同类的,但是在控制上有区别。要控制不可重复读只需要控制记录的修改,而要控制幻读则要控制记录的添加和删除。所以,隔离级别可重复读取不能禁止幻读,而串行则可以。

 

事务隔离级别:

为了避免上面出现几种情况在标准SQL规范中定义了4个事务隔离级别,不同隔离级别对事务处理不同 。

1. 未授权读取(Read Uncommitted):也称未提交读。防止更新丢失(这不对应一级锁吗),如果一个事务已经开始写数据则另外一个数据则不允许同时进行写操作但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。事务隔离的最低级别,仅可保证不读取物理损坏的数据。与READ COMMITTED 隔离级相反,它允许读取已经被其它用户修改但尚未提交确定的数据。

2. 授权读取(Read Committed):也称提交读。1之上防止脏读取(这不对应二级锁吗)。这可以通过“瞬间共享读锁”和“排他写锁”实现,读取数据的事务允许其他事务继续访问该行数据,但是未提交写事务将会禁止其他事务访问该行。SQL Server 默认的级别。在此隔离级下,SELECT 命令不会返回尚未提交(Committed) 的数据,也不能返回脏数据。

3. 可重复读取(Repeatable Read):2之上防止不可重复读取(这不对应三级锁吗)。但是有时可能出现幻影数据,这可以通过“共享读锁”和“排他写锁”实现,读取数据事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。在此隔离级下,用SELECT 命令读取的数据在整个命令执行过程中不会被更改。此选项会影响系统的效能,非必要情况最好不用此隔离级。

三级封锁协议并不能阻止幻读,修改的不能再被读取,但是新增(删除)的记录数可以统计。

4. 串行(Serializable):也称可串行读(这不对应两段锁吗)。提供严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过 “行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作事务访问到。事务隔离的最高级别,事务之间完全隔离。如果事务在可串行读隔离级别上运行,则可以保证任何并发重叠事务均是串行的。

 

 LU丢失更新DR脏读NRR非重复读SLU二类丢失更新PR幻像读
未提交读 RUYYYYY
提交读 RCNNYYY
可重复读 RRNNNNY
串行读 SNNNNN

ORACLE的默认事务级别:READ COMMITTED

ORACLE支持的事务隔离级别:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET TRANSACTION ISOLATION LEVEL READ ONLY;

少数数据库默认的隔离级别为Repeatable Read, 如MySQL InnoDB存储引擎
即使是最低的级别,也不会出现 第一类 丢失 更新问题 .

查看InnoDB系统级别的事务隔离级别:

mysql> SELECT @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ       |
+-----------------------+
1 row in set (0.00 sec)

查看InnoDB会话级别的事务隔离级别:

mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

修改事务隔离级别:

mysql> set global transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)

InnoDB的可重复读隔离级别和其他数据库的可重复读是有区别的,不会造成幻象读(phantom read)。

 

Oracle对隔离级别的支持:

Oracle 明确地支持READ COMMITTED(读已提交)和SERIALIZABLE(可串行化)隔离级别,因为标准中定义了这两种隔离级别。不过,这还不是全部。SQL标准试图 建立多种隔离级别,从而允许在各个级别上完成的查询有不同程度的一致性。REPEATABLE READ(可重复读)也是SQL标准定义的一个隔离级别,可以保证由查询得到读一致的(read-consistent)结果。在SQL标准的定义 中,READ COMMITTED不能提供一致的结果,而READ UNCOMMITTED(读未提交)级别用来得到非阻塞读(non-blocking read)。

不 过,在Oracle中,READ COMMITTED则有得到读一致查询所需的所有属性。在其他数据库中,READ COMMITTED查询可能(而且将会)返回数据库中根本不存在的答案(即实际上任何时间点上都没有这样的结果)。另外,Oracle还秉承了READ UNCOMMITTED的“精神”。(有些数据库)提供脏读的目的是为了支持非阻塞读,也就是说,查询不会被同一个数据的更新所阻塞,也不会因为查询而阻 塞同一数据的更新。不过,Oracle不需要脏读来达到这个目的,而且也不支持脏读。但在其他数据库中必须实现脏读来提供非阻塞读。

        除 了4个已定义的SQL隔离级别外,Oracle还提供了另外一个级别,称为READ ONLY(只读)。READ ONLY事务相对于无法在SQL中完成任何修改的REPEATABLE READ或SERIALIZABLE事务。如果事务使用READ ONLY隔离级别,只能看到事务开始那一刻提交的修改,但是插入、更新和删除不允许采用这种模式(其他会话可以更新数据,但是READ ONLY事务不行)。如果使用这种模式,可以得到REPEATABLE READ和SERIALIZABLE级别的隔离性。
        所以,Oracle对隔离级别的支持如下:
1.SERIALIZABLE:支持
2.READ ONLY:Oracle特有的级别,利用它来实现对REPEATABLE READ的支持
3.READ COMMITTED:支持
4.READ UNCOMMITTED:不明确且不完全地支持

 

参见:

http://seaizon.iteye.com/blog/571139

http://wenku.baidu.com/view/2f89710879563c1ec5da7130.html

http://yjhexy.iteye.com/blog/658706

http://www.cnblogs.com/ityfei/articles/1502153.html

http://heysql.com/mysql/%E5%9F%BA%E7%A1%80%E7%9A%84%EF%BC%9A%E5%B0%81%E9%94%81%E5%8D%8F%E8%AE%AE%EF%BC%8C%E9%94%81%E7%B1%BB%E5%9E%8B%EF%BC%8C%E8%84%8F%E8%AF%BB%E3%80%81%E4%B8%8D%E5%8F%AF%E9%87%8D%E5%A4%8D%E8%AF%BB%E5%92%8C/

Continue reading 三级封锁协议两段锁以及隔离级别

Notepad++ 使用技巧

早就听说过Notepad++大名,最近用了下,觉得挺不错。

可以使用主题配色,看文本舒服多了。我最喜欢Rubyblue。

配合插件NppFTP: notepad++ ftp插件,查看服务端日志方便多了。修改文本文件自然不在话下。我想用这个修改服务器上的php文件岂不很好。

之前格式化个xml还要用专门的xml工具,使用nodepad++的xml tools插件,使用它的pretty print(line break)功能就能很好的格式化。

要格式化json或者javascript,可以使用jsmin插件中的jsformat功能。

常用快捷键:

Ctrl+L 删除行

Ctrl+D复制行

Ctrl+W关闭当前编辑页

Ctrl+Alt+鼠标 纵列选择

Ctrl+M批量修改文件

Continue reading Notepad++ 使用技巧

BCNF 示例

BCNF定义:

定义一:若关系模式R是第一范式,且每个属性都不传递依赖于R的候选键。这种关系模式就是BCNF模式。

定义二:若关系模式R∈1NF,且对于每一个非平凡的函数依赖X→Y,都有X 包含码,则R∈BCNF。

平凡函数依赖

当关系中属性集合Y是属性集合X的子集时(Y?X),存在函数依赖X→Y,即一组属性函数决定它的所有子集,这种函数依赖称为平凡函数依赖。

非平凡函数依赖

当关系中属性集合Y不是属性集合X的子集时,存在函数依赖X→Y,则称这种函数依赖为非平凡函数依赖。

第二个不好记,也不好理解,理解第一个就是了,两个是等同的。

举例 ①:

假设仓库管理关系表为StorehouseManage(仓库ID, 存储物品ID, 管理员ID, 数量),且有一个管理员只在一个仓库工作;一个仓库可以存储多种物品。这个数据库表中存在如下决定关系:

(仓库ID, 存储物品ID) →(管理员ID, 数量)

(管理员ID, 存储物品ID) → (仓库ID, 数量)

所以,(仓库ID, 存储物品ID)和(管理员ID, 存储物品ID)都是StorehouseManage的候选关键字,表中的唯一非关键字段为数量,它是符合第三范式的。但是,由于存在如下决定关系:

(仓库ID) → (管理员ID)

(管理员ID) → (仓库ID)

即存在关键字段决定关键字段的情况,所以其不符合BCNF范式。也就是存在循环传递依赖(仓库ID) → (管理员ID)  → (仓库ID)

 

它会出现如下异常情况:

(1) 删除异常:

当仓库被清空后,所有"存储物品ID"和"数量"信息被删除的同时,"仓库ID"和"管理员ID"信息也被删除了。

(2) 插入异常:

当仓库没有存储任何物品时,无法给仓库分配管理员。

(3) 更新异常:

如果仓库换了管理员,则表中所有行的管理员ID都要修改。

把仓库管理关系表分解为二个关系表:

仓库管理:StorehouseManage(仓库ID, 管理员ID);

仓库:Storehouse(仓库ID, 存储物品ID, 数量)。

这样的数据库表是符合BCNF范式的,消除了删除异常、插入异常和更新异常。

举例②:

CSZ(CITY 城市,ST 街道,ZIP 邮编),其属性组上的函数依赖集是 F={( CITY,ST) →ZIP,ZIP→CITY}。

存在两个候选码:

(CITY, ST)->ZIP

(ST, ZIP)->CITY

(CITY, ST)和(ST, ZIP)是两个候选码,没有非主属性,自然CSZE∈3NF。

但存在传递依赖(CITY, ST)->ZIP->CITY, 所以CSZ∉BCNF。
关系模式CSZ 也存在种删除,插入,更新异常:

若将CSZ 分解为两个关系模式ZC(ZIP, CITY)和SZ(ST,ZIP),就不再有
非平凡的函数依赖的决定因素中不包含码的情况,都是BCNF 的关系模式了。

 

可以看到上面两个例子都是存在循环依赖造成的主属性传递依赖于码,不知道这是必然还是有别的例子属于3NF不属于BCNF但是也不存在循环依赖的情况?

Continue reading BCNF 示例

IO 接口,设备

IO这个词出现太多了太多了,这里整理一下思路。

计算机由控制单元,运算单元,存储单元,输入设备输出设备组成。其中IO指的就是输入输出。

关于IO又有接口,设备,操作的概念: 参见http://book.51cto.com/art/200704/45417.htm

IO接口

IO接口是实现外部设备与主机之间的连接和信息交换的设备,也可称I/O适配器(Adapter)或适配卡。目前有:

① 总线系统

② 直接内存访问(DMA)

③ 通道

④ SCSI(Small Computer System Interface)

⑤ 并行口

⑥ RS-232C接口

⑦ USB(Universal Serial Bus,通用串行总线)接口

⑧ IEEE 1394接口

IO接口的控制方式

(1)程序查询方式
这种方式下,CPU通过I/O指令询问指定外设当前的状态,如果外设准备就绪,则进行数据的输入或输出,否则CPU等待,循环查询。
这种方式的优点是结构简单,只需要少量的硬件电路即可,缺点是由于CPU的速度远远高于外设,因此通常处于等待状态,工作效率很低

(2)中断处理方式
在这种方式下,CPU不再被动等待,而是可以执行其他程序,一旦外设为数据交换准备就绪,可以向CPU提出服务请求,CPU如果响应该请求,便暂时停止当前程序的执行,转去执行与该请求对应的服务程序,完成后,再继续执行原来被中断的程序。
中断处理方式的优点是显而易见的,它不但为CPU省去了查询外设状态和等待外设就绪所花费的时间,提高了CPU的工作效率,还满足了外设的实时要求。但需要为每个I/O设备分配一个中断请求号和相应的中断服务程序,此外还需要一个中断控制器(I/O接口芯片)管理I/O设备提出的中断请求,例如设置中断屏蔽、中断请求优先级等。
此外,中断处理方式的缺点是每传送一个字符都要进行中断,启动中断控制器,还要保留和恢复现场以便能继续原程序的执行,花费的工作量很大,这样如果需要大量数据交换,系统的性能会很低。

(3)DMA(直接存储器存取)传送方式
DMA最明显的一个特点是它不是用软件而是采用一个专门的控制器来控制内存与外设之间的数据交流,无须CPU介入,大大提高CPU的工作效率。
在进行DMA数据传送之前,DMA控制器会向CPU申请总线控制 权,CPU如果允许,则将控制权交出,因此,在数据交换时,总线控制权由DMA控制器掌握,在传输结束后,DMA控制器将总线控制权交还给CPU。

(4) 通道方式

(5) 外围处理机(输入输出处理机)方式

有专门的处理机负责IO,一般用于大型系统

IO设备

键盘,鼠标,硬盘,打印机,扫描仪,网络设备等……

 

说实话,我还是没搞清楚!有时候把接口当成设备,有时又要把它当成控制方式,下面显然是控制方式:

常见I/O接口方式的分类方式有:

①按数据传送格式分类:串行接口和并行接口

②按时序控制方式分类:同步接口和异步接口

③按传送控制方式分类:直接程序传送接口,中断接口,DMA接口.

网络IO

还有网络IO这个说法,对于web方面的来说,性能与网络IO操作有很大关系,参见http://blog.csdn.net/historyasamirror/article/details/5778378

对于一个network IO (这里我们以read举例),它会涉及到两个系统对象,一个是调用这个IO的process (or thread),另一个就是系统内核(kernel)。

当一个read操作发生时,它会经历两个阶段:
1 等待数据准备 (Waiting for the data to be ready)
2 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)记住这两点很重要,因为这些IO Model的区别就是在两个阶段上各有不同的情况。

阻塞IO:特点就是在IO执行的两个阶段都被block了,调用返回时数据一定是准备好了的。

非阻塞IO:调用马上返回,但是数据可能还没准备好,需要不断的主动询问kernel数据好了没有

多路IO(IO multiplexing):先选择,后获取。选择操作会阻塞知道其中一个有数据。

异步IO:调用马上返回,有数据后会发信号。(和ajax取数据有点像哈)

此文将多路分为同步IO,

这篇文章有另一种分法:http://blog.chinaunix.net/space.php?uid=20357359&do=blog&id=1963658

090830172605

将多路归类为异步阻塞,各有各道理,知道原理就好。不钻牛角尖!

显然非阻塞异步IO性能应该是最好的,Nodejs中的事件驱动非阻塞IO就是AIO。

Continue reading IO 接口,设备

【转】ThreadPoolExecutor使用和思考(上)-线程池大小设置与BlockingQueue的三种实现区别

 
转自:http://dongxuan.iteye.com/blog/901689

工作中多处接触到了ThreadPoolExecutor。趁着现在还算空,学习总结一下。

前记:

  1. jdk官方文档(javadoc)是学习的最好,最权威的参考。
  2. 文章分上中下。上篇中主要介绍ThreadPoolExecutor接受任务相关的两方面入参的意义和区别,池大小参数corePoolSize和maximumPoolSize,BlockingQueue选型(SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue);中篇中主要聊聊与keepAliveTime这个参数相关的话题;下片中介绍一下一些比较少用的该类的API,及他的近亲:ScheduledThreadPoolExecutor
  3. 如果理解错误,请直接指出。

查看JDK帮助文档,可以发现该类比较简单,继承自AbstractExecutorService,而AbstractExecutorService实现了ExecutorService接口。

ThreadPoolExecutor的完整构造方法的签名是:

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

先记着,后面慢慢解释。

===============================神奇分割线==================================

其实对于ThreadPoolExecutor的构造函数网上有N多的解释的,大多讲得都很好,不过我想先换个方式,从Executors这个类入手。因为他的几个构造工厂构造方法名字取得令人很容易了解有什么特点。但是其实Executors类的底层实现便是ThreadPoolExecutor!

ThreadPoolExecutor是Executors类的底层实现。

在JDK帮助文档中,有如此一段话:

“强烈建议程序员使用较为方便的 Executors 工厂方法 Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)和 Executors.newSingleThreadExecutor()(单个后台线程),它们均为大多数使用场景预定义了设置。”

可以推断出ThreadPoolExecutor与Executors类必然关系密切。

===============================神奇分割线==================================

OK,那就来看看源码吧,从newFixedThreadPool开始。

ExecutorService newFixedThreadPool(int nThreads):固定大小线程池。

可以看到,corePoolSize和maximumPoolSize的大小是一样的(实际上,后面会介绍,如果使用无界queue的话maximumPoolSize参数是没有意义的),keepAliveTime和unit的设值表名什么?-就是该实现不想keep alive!最后的BlockingQueue选择了LinkedBlockingQueue,该queue有一个特点,他是无界的。

Java代码 收藏代码

  1. public static ExecutorService newFixedThreadPool(int nThreads) { 
  2. return new ThreadPoolExecutor(nThreads, nThreads, 
  3.                                       0L, TimeUnit.MILLISECONDS, 
  4. new LinkedBlockingQueue<Runnable>()); 
  5.     } 

ExecutorService newSingleThreadExecutor():单线程。

可以看到,与fixedThreadPool很像,只不过fixedThreadPool中的入参直接退化为1

Java代码 收藏代码

  1. public static ExecutorService newSingleThreadExecutor() { 
  2. return new FinalizableDelegatedExecutorService 
  3.             (new ThreadPoolExecutor(1, 1, 
  4.                                     0L, TimeUnit.MILLISECONDS, 
  5. new LinkedBlockingQueue<Runnable>())); 
  6.     } 

ExecutorService newCachedThreadPool():无界线程池,可以进行自动线程回收。

这个实现就有意思了。首先是无界的线程池,所以我们可以发现maximumPoolSize为big big。其次BlockingQueue的选择上使用SynchronousQueue。可能对于该BlockingQueue有些陌生,简单说:该QUEUE中,每个插入操作必须等待另一个

线程的对应移除操作。比如,我先添加一个元素,接下来如果继续想尝试添加则会阻塞,直到另一个线程取走一个元素,反之亦然。(想到什么?就是缓冲区为1的生产者消费者模式^_^)

注意到介绍中的自动回收线程的特性吗,为什么呢?先不说,但注意到该实现中corePoolSize和maximumPoolSize的大小不同。

Java代码 收藏代码

  1. public static ExecutorService newCachedThreadPool() { 
  2. return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 
  3.                                       60L, TimeUnit.SECONDS, 
  4. new SynchronousQueue<Runnable>()); 
  5.     } 

===============================神奇分割线==================================

到此如果有很多疑问,那是必然了(除非你也很了解了)

先从BlockingQueue<Runnable> workQueue这个入参开始说起。在JDK中,其实已经说得很清楚了,一共有三种类型的queue。以下为引用:(我会稍微修改一下,并用红色突出显示)

所有 BlockingQueue 都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:
  • 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。(什么意思?如果当前运行的线程小于corePoolSize,则任务根本不会存放,添加到queue中,而是直接抄家伙(thread)开始运行)
  • 如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程
  • 如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

先不着急举例子,因为首先需要知道queue上的三种类型。

排队有三种通用策略:

  1. 直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
  2. 无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
  3. 有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。  

===============================神奇分割线==================================

到这里,该了解的理论已经够多了,可以调节的就是corePoolSize和maximumPoolSizes 这对参数还有就是BlockingQueue的选择。

例子一:使用直接提交策略,也即SynchronousQueue。

首先SynchronousQueue是无界的,也就是说他存数任务的能力是没有限制的,但是由于该Queue本身的特性,在某次添加元素后必须等待其他线程取走后才能继续添加。在这里不是核心线程便是新创建的线程,但是我们试想一样下,下面的场景。

我们使用一下参数构造ThreadPoolExecutor:

Java代码 收藏代码

  1. new ThreadPoolExecutor( 
  2. 2, 3, 30, TimeUnit.SECONDS,  
  3. new <span style="white-space: normal;">SynchronousQueue</span><Runnable>(),  
  4. new RecorderThreadFactory("CookieRecorderPool"),  
  5. new ThreadPoolExecutor.CallerRunsPolicy()); 

当核心线程已经有2个正在运行.

  1. 此时继续来了一个任务(A),根据前面介绍的“如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。”,所以A被添加到queue中。
  2. 又来了一个任务(B),且核心2个线程还没有忙完,OK,接下来首先尝试1中描述,但是由于使用的SynchronousQueue,所以一定无法加入进去。
  3. 此时便满足了上面提到的“如果无法将请求加入队列,则创建新的线程,除非创建此线程超出maximumPoolSize,在这种情况下,任务将被拒绝。”,所以必然会新建一个线程来运行这个任务。
  4. 暂时还可以,但是如果这三个任务都还没完成,连续来了两个任务,第一个添加入queue中,后一个呢?queue中无法插入,而线程数达到了maximumPoolSize,所以只好执行异常策略了。

所以在使用SynchronousQueue通常要求maximumPoolSize是无界的,这样就可以避免上述情况发生(如果希望限制就直接使用有界队列)。对于使用SynchronousQueue的作用jdk中写的很清楚:此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。

什么意思?如果你的任务A1,A2有内部关联,A1需要先运行,那么先提交A1,再提交A2,当使用SynchronousQueue我们可以保证,A1必定先被执行,在A1么有被执行前,A2不可能添加入queue中

例子二:使用无界队列策略,即LinkedBlockingQueue

这个就拿newFixedThreadPool来说,根据前文提到的规则:

写道

如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。

那么当任务继续增加,会发生什么呢?

写道

如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。

OK,此时任务变加入队列之中了,那什么时候才会添加新线程呢?

写道

如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

这里就很有意思了,可能会出现无法加入队列吗?不像SynchronousQueue那样有其自身的特点,对于无界队列来说,总是可以加入的(资源耗尽,当然另当别论)。换句说,永远也不会触发产生新的线程!corePoolSize大小的线程数会一直运行,忙完当前的,就从队列中拿任务开始运行。所以要防止任务疯长,比如任务运行的实行比较长,而添加任务的速度远远超过处理任务的时间,而且还不断增加,如果任务内存大一些,不一会儿就爆了,呵呵。

可以仔细想想哈。

例子三:有界队列,使用ArrayBlockingQueue。

这个是最为复杂的使用,所以JDK不推荐使用也有些道理。与上面的相比,最大的特点便是可以防止资源耗尽的情况发生。

举例来说,请看如下构造方法:

Java代码 收藏代码

  1. new ThreadPoolExecutor( 
  2. 2, 4, 30, TimeUnit.SECONDS,  
  3. new ArrayBlockingQueue<Runnable>(2),  
  4. new RecorderThreadFactory("CookieRecorderPool"),  
  5. new ThreadPoolExecutor.CallerRunsPolicy()); 

假设,所有的任务都永远无法执行完。

对于首先来的A,B来说直接运行,接下来,如果来了C,D,他们会被放到queu中,如果接下来再来E,F,则增加线程运行E,F。但是如果再来任务,队列无法再接受了,线程数也到达最大的限制了,所以就会使用拒绝策略来处理。

总结:

  1. ThreadPoolExecutor的使用还是很有技巧的。
  2. 使用无界queue可能会耗尽系统资源。
  3. 使用有界queue可能不能很好的满足性能,需要调节线程数和queue大小
  4. 线程数自然也有开销,所以需要根据不同应用进行调节。

通常来说对于静态任务可以归为:

  1. 数量大,但是执行时间很短
  2. 数量小,但是执行时间较长
  3. 数量又大执行时间又长
  4. 除了以上特点外,任务间还有些内在关系

看完这篇问文章后,希望能够可以选择合适的类型了

Continue reading 【转】ThreadPoolExecutor使用和思考(上)-线程池大小设置与BlockingQueue的三种实现区别

DSSA(Domain Specific Software Architecture) 特定领域软件架构

与软件体系结构风格的区别

DSSA与软件体系结构风格是从不同角度出发研究问题的两种结果,前者从问题域出发,后者从解决域出发。

DSSA只对某个领域进行专家知识的提取、存储、组织,但可以同时使用多种软件体系结构风格;而在某个软件体系结构风格中进行专家知识组织时,可以将提取的公共结构和设计方法扩展到多个应用领域。

DSSA通常选用一个或多个适合所研究的领域的体系结构风格,并设计一个该领域专用的体系结构分析设计工具。但该方法提取的专家知识只能应用于一个较小的范围--所在领域。一个领域的DSSA及其工具在另一个领域中是不适应的,或不可以重用的。

体系结构风格避免设计到特定的应用领域或背景,所提取的知识比DSSA提取的知识应用范围要广。但在一个特定领域中,正是由于体系结构风格对领域知识、领域背景的忽略,使其在一个具体领域的开发中的作用并不比DSSA要大。

DSSA与体系结构风格是互为补充的两种技术。

http://iknow.seforge.org/sewiki/DSSA_e6_96_b9_e6_b3_95

作为DARPA的DSSA计划的一部分,Will Tracz在DSSA-ADAGE项目中提出了DSSA领域工程方法,与基于构架的系统开发过程相配合,应用于航空电子设备自动导航领域。

在DSSA方法中,进行领域工程的主要方式是领域工程师与领域专家的会谈,其中领域专家要就领域工程师提出的一系列问题进行报告,领域工程师对这些报告进行综合和整理,然后与领域专家一起对结果进行复审。

DSSA的领域工程过程是并发的 (concurrent)、递归的(recursive)和迭代的(iterative)。或者可以说,它是螺旋型的(spiral)。完成这个过程可能需要对每个阶段都经历几遍,每次增加更多的细节。对每个阶段的描述中包括一组需要回答的问题,一组需要的输入,一组将产生的输出和验证准则。

该方法分为五个主要阶段,在此之前还需要进行一些准备工作。其中前三个阶段集中于领域分析,即显式地把握领域特定的知识,这些知识时常被领域专家看作普通的知识。后两个阶段处理DSSA的设计、分析以及实现。

准备工作

在与领域专家会谈之前,领域工程师应尽可能熟悉该领域。理想情况下,领域工程师应对该领域具有一定经验。领域工程师应对DSSA方法指南中的问题回答有一定的想法,以便在会谈过程中诱导领域专家的答案,或者由领域专家对这些想法进行确认。领域工程师要确定DSSA方法的指南文档中哪些问题是与本次领域工程相关的,并对指南中的领域工程过程进行相应的裁剪。领域工程师还要理解本次领域工程所要达到的目标和实施这项工作的原因。

第一个阶段――定义领域分析的范围

本阶段集中于在感兴趣的领域之内有哪些事物。本步骤的输出包括感兴趣的领域与其它领域间的关系图、领域中应用的用户的需求列表、作为信息源的人的列表、作为信息源的项目的列表。

第二个阶段――定义特定于领域的元素并限制范围

本阶段的目标是编辑特定于领域的术语字典和同义词词典。在高级别块图的基础上,增加更多的细节,其中重点是在领域中的应用之间识别共同性,分离差异性。应特别重视对领域中的基本元素进行标准化和分类。

第三个阶段――定义和精化特定于领域的设计和实现约束

DSSA方法的一个特色是区别“需求”和“约束”。在DSSA方法中,“需求”描述一组在问题空间中的特性。“约束”描述一组在解空间中的特性。本阶段的目标是描述解空间中的特征。不但要识别约束,而且要记录它们对设计和实现决定的影响,以及对处理它们时产生的问题的讨论。

第四个阶段――开发领域构架/模型 领域设计

本阶段的目标是提出一般的构架,并说明构成构架的模块或构件的语法和语义。为满足前面识别的需求和约束,在一个应用领域中可能需要设计几个DSSA。

第五个阶段――产生或搜集复用产品 领域实现

本阶段的目标是为DSSA充实构件使得它可以被用来产生问题域中的新应用。参与这个阶段的领域专家是开发过这个领域中应用的软件工程师。他们最适合识别现有的可复用构件,或可以作为产生可复用构件的基础的构件。

Snap1

参见:

http://read.chaoxing.com/ebook/read_11401987.html P93

Continue reading DSSA(Domain Specific Software Architecture) 特定领域软件架构

正交软件体系结构

正交软件体系结构由组织层和线索的构件构成。层是由一组具有相同抽象级别的构件构成。线索是子系统的特例,它是由完成不同层次功能的构件组成(通过相互调用来关联),每一条线索完成整个系统中相对独立的一部分功能。每一条线索的实现与其他线索的实现无关或关联很少,在同一层中的构件之间是不存在相互调用的。

如果线索是相互独立的,即不同线索中的构件之间没有相互调用,那么这个结构就是完全正交的。从以上定义,我们可以看出,正交软件体系结构是一种以垂直线索构件族为基础的层次化结构,其基本思想是把应用系统的结构按功能的正交相关性,垂直分割为若干个线索(子系统),线索又分为几个层次,每个线索由多个具有不同层次功能和不同抽象级别的构件构成。各线索的相同层次的构件具有相同的抽象级别。因此,我们可以归纳正交软件体系结构的主要特征如下:

(1)正交软件体系结构由完成不同功能的n(n > 1)个线索(子系统)组成;

(2)系统具有m(m > 1)个不同抽象级别的层;

(3)线索之间是相互独立的(正交的);

(4)系统有一个公共驱动层(一般为最高层,下图的第一层)和公共数据结构(一般为最低层,下图第五层)。

对于大型的和复杂的软件系统,其子线索(一级子线索)还可以划分为更低一级的子线索(二级子线索),形成多级正交结构。正交软件体系结构的框架如图1所示。

xjj

图1是一个三级线索、五层结构的正交软件体系结构框架图,在该图中,ABDFK组成了一条线索,ACEJK也是一条线索。因为B、C处于同一层次中,所以不允许进行互相调用;H、J处于同一层次中,也不允许进行互相调用。一般来讲,第五层是一个物理数据库连接构件或设备构件,供整个系统公用。

在软件进化过程中,系统需求会不断发生变化。在正交软件体系结构中,因线索的正交性,每一个需求变动仅影响某一条线索,而不会涉及到其他线索。这样,就把软件需求的变动局部化了,产生的影响也被限制在一定范围内,因此实现容易。

正交软件体系结构具有以下优点:

(1)结构清晰,易于理解。正交软件体系结构的形式有利于理解。由于线索功能相互独立,不进行互相调用,结构简单、清晰,构件在结构图中的位置已经说明它所实现的是哪一级抽象,担负的是什么功能。

(2)易修改,可维护性强。由于线索之间是相互独立的,所以对一个线索的修改不会影响到其他线索。因此,当软件需求发生变化时,可以将新需求分解为独立的子需求,然后以线索和其中的构件为主要对象分别对各个子需求进行处理,这样软件修改就很容易实现。系统功能的增加或减少,只需相应的增删线索构件族,而不影响整个正交体系结构,因此能方便地实现结构调整。

(3)可移植性强,重用粒度大。因为正交结构可以为一个领域内的所有应用程序所共享,这些软件有着相同或类似的层次和线索,可以实现体系结构级的重用。

 

正交软件体系结构设计过程
正交软件体系结构设计过程分为2 个阶段:原型阶段和演化阶段。开发原型的主要目的是为了确认需求,设计体系结构基线。软件开发小组需要建立一系列原型,与用户一起讨论和评审。原型有助于需求的清晰化,同时也有助于进一步分析系统的可行性,更清楚地认识系统,完善对系统的理解。
原型开发的第1 个迭代周期的目标并不明确具体。为了提高开发效率,缩短开发周期,可将开发人员分为2 组,一组负责界面原型;一组负责业务模型。界面原型是界面层次的水平模型, 没有真正地实现系统功能。业务模型可用UML(统一建模语言)图表述,最终生成系统的SRS(软件需求说明书)。原型开发的第2 个迭代周期的任务是设计和建立正交软件体系结构。本次迭代大致分为6 个阶段[5]:
(1)标识构件:为系统生成逻辑结构,比如生成类图、包、构件等。

(2)提出软件体系结构模型:选择合适的软件体系结构风格是必要的,在此基础上,开发人员通过软件体系结构模型,熟悉了软件体系结构属性等方面的要求,虽然这个模型可能存在错误,但它为整个系统的演化确立了目标。
(3)把已标识的构件映射到软件体系结构中:把第(1)阶段标识的构件映射到体系结构中。
(4)分析构件,建立连接件:为了把已标识的构件集成到体系结构中,必须分析所有构件的关系,可结合UML 图表述,比如活动图、时序图等。
(5)生成软件体系结构。关键构件决定软件体系结构,主要是在第(4)阶段的基础上做精化。
(6)正交化。通过以上几个阶段产生的软件体系结构不一定满足正交性(同一层次的构件之间可能存在相互调)。通过从左至右、自顶向下地增加、删除、拆分合并构件,把不满足正交性的线索和构件正交化。

正交软件体系结构的演化控制

正交软件体系结构的重用粒度大。在演化控制和新系统开发过程中,逐渐建立线索库和构件库,如果有新需求,可以重用库中的线索和构件,以提高开发效率。因此,对新需求可以通过在原软件体系结构基础之上新
增、修改、删除线索来完成。对线索中的构件进行修改和添加时按照从左到右、自顶向下的原则,先修改高级构件,然后根据需要修改被其调用的构件。基于正交软件体系结构开发的软件,必须按照正交的演化步骤修改。其实演化过程也是正交软件体系结构的验证和完善过程。

(1)变动需求归类。将变动对应到相应的线索和构件上,对于新需求需要建立的新的线索和构件,都先作好标记。
(2)制定演进计划。在对原有模型改动前,需要制定一个周密的演进计划,作为后续开发工作的指南。

(3)增加、删除和修改构件。在第(1)步的基础上,开发人员依据演进计划对线索和构件进行增删改。
(4)更新构件关系。参照正交化简易算法对构件关系做必要调整,使线索和构件满足正交性。
(5)产生演化后的新软件体系结构。在原有系统上的修改必须映射到原有体系结构上,作为后续开发的基础。
(6)对以上修改作阶段性技术评审。
(7)迭代演进。迭代地进行第(3)步~第(6)步,直到新体系结构足够详细。

 

参考:

http://se.csai.cn/NewTech/No046.htm 张友生

http://read.chaoxing.com/ebook/read_11401987.html 张友生 p72

http://ishare.iask.sina.com.cn/f/8438626.html 基于正交软件体系结构的CRM 系统 汪保杰,王如龙 (湖南大学软件学院,长沙 410082)

Continue reading 正交软件体系结构

过程控制(Process Control),开环,闭环

适用情况

对被控制目标的检测、计算和控制的不断循环执行,以实时地对环境的变化做出反应。

目的

将系统的指定输出值保持在set points(设定点)上

概念

过程变量(Process variable):反映系统运行状态
输入变量(Input variable):系统输入的变量
控制变量(Controlled variable):系统输出的变量
设定点(Set point):系统运行所设定的目标。

Open-loop system 开环

输出的控制只考虑目标设定,既不考虑输入,也不考虑输出变化的控制

Snap3

这个开环加热系统会使温度不断升高

Closed-loop system 闭环(control loop控制环路)

把输出执行的状况经检测后,作为输入,经过与设定目标的组合,将其值经过计算后产生输出控制信息的系统;

Snap4

这个闭环加热系统会使温度最终保持到一定温度。

 

闭环系统分为反馈控制和前馈控制:

反馈控制:feedback

负反馈:目标与被测输入的值作为控制依据,使得输出稳定在设定目标上;
正反馈:目标与被测输入的作为控制依据,无法使输出稳定在设定目标上;

Snap5>负反馈:反馈信息与控制信息的作用方向相反,因而可以纠正控制信息的效应。

Continue reading 过程控制(Process Control),开环,闭环

黑板系统Blackboard

适用问题:

没有直接的算法,多种方法都可能解决问题;
找不到确定的求解策略(先做什么?后做什么?);
问题没有唯一的解答;
每个求解步骤中都可能产生多个可能的解,需要寻求最佳或可接受解。
需要多个领域的专门知识协作解决。
例如:自然语言处理、语音处理、模式识别、图像处理等

 

基本思想

独立程序(知识源)集合
对公共数据结构进行协同操作;
每个程序专门解决一个子问题;
不存在互相调用;
不存在事先确定的操作顺序;
中心控制部件
反映整体问题求解过程的状态;
状态变化时,中心控制部件对信息进行评价,协调各程序工作;
可以试探调用各个可能的求解算法;

 

结构:

Snap1

Snap2

黑板部件

中心共享数据;

存储系统的初始输入、问题求解的局部和中间结果,以及反映问题求解状态的数据;

为知识源访问这些元素提供读和写的操作;

知识源:独立的求解程序

独立, 依赖于应用的问题求解子系统;

它们之间的交互仅能通过读写黑板的数据和状态来实现(不直接交互);

包含条件和操作两个部分:条件部分评估求解过程的当前状态,即黑板上的当前内容。当条件满足时,执行相应的操作;

操作引起黑板数据状态的变化;

控制组件

监视黑板上信息和状态的变化,并根据变化决定采取的行动。

根据知识调度策略,调度知识源对条件的匹配评价,并采取相应的操作。

 

应用:

起源于人工智能(Artificial Intelligence, AI)领域;
典型应用领域:自然语言处理、语音处理、模式识别、图像处理等;
HEARSAY-II (自然语言处理系统,系统输入是自然语言的语音信号,经过语音音节、词汇、句法和语义分析后,获得用户对数据库的查询请求)
HASP/SIAP(在特定海域根据声纳阵列信号探测敌方潜艇出没的系统)
CRYALIS(根据X射线探测数据推测蛋白质分子三维结构的系统)
TRICERO(在分布环境下监视飞机活动的系统)

 

优点:

应用于没有确定解方法的问题:用不同的算法进行试验;
支持可更改性和可维护性(知识源、控制算法、中心数据分离);
知识可重用:知识来自某类任务的独立专家;
支持容错性和健壮性:中间结论只是下一步的假设条件;

缺点:

测试困难(没有一个确定的算法);
难以保证最优的解决方案;
效率低;
开发工作昂贵;
缺少对并行机制的支持(黑板上数据的访问必须同步);

Continue reading 黑板系统Blackboard

各Nosql数据库特点

下面从http://www.cnw.com.cn/software-database/htm2011/20110808_230968.shtml 摘抄。自己补充了些!

Cassandra

Facebook需要一个更快、更廉价的方式处理数以亿计的状态更新。因此,他们启动了这一项目,并最终将其移植到了Apache上,这就是 Cassandra。在Apache上,它能够得到许多社区的帮助。目前Cassandra已经不再仅仅用于Facebook,许多为该项目工作的程序员来自其他公司。如今DataStax.com公司正致力于为Cassandra提供商业支持。

Cassandra是一个跟踪如Facebook上的状态更新等大量数据的优秀工具。这一工具可以帮助创建一个计算机网络,网络上的所有计算都拥有相同的数据。这意味着每台机器都可以被相互替代。一旦数据通过P2P网络节点,它们的一致性就会丧失。关键是“最终一致”,而不是“时刻一致”。如果你发现你的状态更新在Facebook消失一下,然后又重新出现,你就明白这意味着什么。

CouchDB

CouchDB被用于存储文档,最大的变化在于查询。取代一些基本查询结构的是,CouchDB通过两种功能来搜索文档,以导航并减少数据。一个编排文档格式,另一个确定包括哪些文档。一名清楚存储程序的、熟练的甲骨文数据库操作人员会做同样的事情。但是,导航和减少结构将让基层程序员大开眼界。目前,AJAX开发人员已经能够编写出相当复杂的搜索程序,这些程序可以写入一些更为复杂的逻辑。

CouchDB的核心由Erlang编写。但是API和界面却是JavaScript或是JSON。

JavaScript API仅仅是加强CouchDB对普通Web开发人员的吸引力。这些开发人员可以将文档,甚至整个网站存储在数据库中。

Restful的方式确实很有特点,可以直接使用javascript调用了。但不知会不会有人这样用?注意json解析的开销。

参见:

http://www.oschina.net/question/5189_3939 

MongoDB

MongoDB是一个关于JavaScript如何掌握世界的典型。程序获取格式化为JSON格式的数据,然后将它们存储起来。查询是 JavaScript的基础功能,这与使用浏览器控制台没有太大的差别,只是简化了一些东西。最大的区别是,MongoDB会为你的数据库创建索引,如果索引被正确地创建,那么反馈查询结果的速度将会很快。另外,该数据库可以与大量的其他工具协同工作。

Redis

与CouchDB和MongoDB一样,Redis用于存储文档和由键值对组织的文件。与其他的NoSQL数据库不同的是,其存储的不仅仅是字符串或是数字,其中还包括分类和未分类的字符串集合作为与键关联的值。这一特点使其可以为用户提供更为复杂的集合操作。用户不再需要下载数据计算交集,因为 Redis能够在服务器上做这一工作。

Redis催生了一些没有过多编码的简单结构。Luke Melia通过创建一个全新集合追踪其网站上的访问者。最后五个集合的并集可确定那些当时在线的访问者。这一带有好友列表的并集的交集可以生成在线好友列表。这类集操作拥有许多应用。Redis集群为我们揭示了其强大的功能。

Redis将数据存储在内存中,仅记录下每次变化的列表。由于其具有功能强大,可写入硬盘中写入的缓存,许多人甚至并不将Redis称之为数据库。由于Redis只需要在数据读入内存之前进行等待,因此速度要比传统数据库快很多,但是不适时机的断电导致其存在潜在的应用风险。

Redis 对内存很依赖,但也提供dump操作,然而使用要小心。

参见:

http://gly199.iteye.com/blog/1050606

http://zhaohaolin.iteye.com/blog/1122876

http://www.iteye.com/topic/808293

 

Riak

Riak是设计最为精巧的数据存储,其拥有其他产品的绝大多数功能,并且对副本有着更多的控制。尽管基本结构存储着多对键值,但是恢复它们和确保它们的一致性的选项很多。比如写入操作包括了要求Riak确认数据何时被成功传输到集群其他机器上的参数。如果你不希望仅信任一台机器,在发送确认信息前,你可以要求其等待,直至两台、三台或是54台机器写入了数据。这也是该团队能够打出“最终一致性不是数据遗失的借口”这一口号的原因。

数据自身并不仅仅写在硬盘中。这只是其中的一个选项,但是并不是主要的。Riak使用的是插件式存储引擎(默认为Bitcask)。该引擎用它们自己的内部格式将数据写入硬盘中。此外,它还有多种选项,包括MySQL使用的InnoDB版本。Riak的集群能力可以确保所有一切都万无一失。

在抓取数据时,Riak会消除任何可能出现的错误。如果在两个节点的目标版本不同,那么Riak会选择最新升级版本,或是将两个目标版本都反馈回来,交由你的客户端代码做决定。对于发现数据中存在的潜在错误,这是一个非常有用的选项。

Neo4J

在我们所提到的几个应用之中,Neo4J是最具特色一个。它可以用于存储图而不是数据。它对图数据是以节点和边(关系)模式进行存储。社交网络应用是它的强项。Neo4J非常的新,开发人员仍然在寻找更好的算法。在新版本中,开发人员开始尝试新的缓存策略:由于Neo4J能够缓存节点信息,因此搜索算法运行速度很快。开发人员还为其增加了类似XSL模式匹配的新查询语言。

Neo4J受到了Neo Technology公司的支持。该公司推出的商业用版本数据库拥有备份、故障恢复和复杂监视功能。

数据以图结构存储在硬盘中,而不是表的形式。图是指数学图论中的那个图,不是指多媒体中的图形图像。

它是支持事务的!

 

FlockDB

有些人抱怨代码过于复杂,他们认为Neo4J过于复杂,超出了他们的需求。那么他们不妨尝试一下FlockDB。FlockDB是一个实时的、分布式的数据库,是Twitter网站基础设施的核心组件。Twitter在一年多以前推出了基于Apache许可证的开源项目FlockDB。如果你想建立起自己的Twitter,那么你需要下载Gizzard工具,其作用是分割跨多个Flock的数据。由于FlockDB存储两个节点之间的关联,我们中的许多人将FlockDB称为“图数据库”。不过,也有人认为这一称呼仅适用于像Neo4J这类复杂的工具。

如何选择NoSQL数据库?

关于如何选择NoSQL数据库这一问题并不好回答。许多IT部门会随便选一个,有时候他们选择的数据库并不能满足他们的需求。由于优秀的开发人员希望能够平衡项目的优势、商业支持的可获得性以及文档质量,因此选择一个最佳数据库是十分困难的。

这些数据库都存储了大堆的键和值,但是真正的问题是如何在服务器中合理分配负载,如何将变更传递给它们。另一个问题是托管。云服务能够替你完成所有的维护,这一点非常具有吸引力。与SQL数据库相比,NoSQL数据库的数据交换更为困难。目前全球还没有一个标准的查询语言,也没有一个像JDBC一样的大型虚拟层。尽管如此,NoSQL数据库已经对我们具备了足够的吸引力。

 

各nosql比较:

http://www.distream.org/?p=10

Continue reading 各Nosql数据库特点

Pagination


Total views.

© 2013 - 2024. All rights reserved.

Powered by Hydejack v6.6.1