#
Facebook的新实时消息系统:Hbase——每月存储1350亿条消息
你或许已经知道,facebook已经介绍过全新的social inbox产品,集成了email,IM,短信,文本信息,facebook的在线消息。最为重要的是,他们每个月要存储超过1350亿条消息。他们如何存放这些信息呢?facebook的Kannan Muthukkaruppan在《邮件的底层技术:HBase》一文中给了一个十分意外的答案——HBase,打败了MySQL,Cassandra和其他一些技术,成为facebook的选择。
为什么说是一个意外的答案?facebook创造了Cassandra,并且其就是为邮件类型的应用而打造的,但是他们发现Cassandra的最终一致性模型并不适合他们的全新的实时邮件产品。Facebook同样拥有大量的MySQL架构,但是他们发现性能会随着数据和索引的增加变差。他们同样可以选择自己来开发一个新的存储模型,但是他们最终选择了HBase。
HBase是一个可扩展的、并且支持海量数据下的高并发记录级别更新操作的表存储产品——为邮件系统量身定做。HBase同样支持基于BigTable模型的key-value存储。这样能够很好的支持按key来查找记录以及按范围来搜寻或者过滤,这也是邮件系统的特性之一。然而,复杂一点的查询却并不被支持。查询是通过一个叫Hive的工具来进行分析的,这是facebook创造的用以处理他们几个P的数据仓库的,Hive是基于Hadoop文件系统HDFS,这也是HBase所采用的文件系统。
Facebook检视了他们的应用场景,指出他们为什么要选择HBase。他们所需要的系统应该能处理以下两种数据:
- 一个较小的临时数据集,是经常变化的。
- 一个不断增加的数据集,是很少被访问的。
有点意思哈。你阅读了收件箱里的邮件,以后就很少再去看它一眼了。这两种截然不同的数据使用方式,你可能会用两个系统来实现。但是显然HBase就能搞定这一切。目前尚不清楚它是如何(在两种数据集上)来实现通用的搜索功能的,尽管它集成了多种搜索引擎。
他们系统的一些关键特性:
·HBase:
·拥有一个比Cassandra更简答的一致性模型。
·非常好的可伸缩性和性能。
·大多数特性对他们的需求来说是足足有余的:自动负载平衡和故障转移,支持压缩,单机多个切片(multiple shards)。
·HDFS是HBase使用的文件系统,支持冗余复制,端到端的校验以及自动恢复平衡。
·facebook的运维团队在使用HDFS方面有丰富的经验,他们是Hadoop的大客户,Hadoop就是使用HDFS作为分布式文件系统的。
·Haystack用来做为存储附件用的。
·重头开始写了一个自定义的应用server,以便处理大量来自不同源的消息。
·在ZooKeeper的顶层实现了一个“用户发现服务”。
·使用了一系列的基础服务:email帐户验证,好友关系链,隐私控制,消息传送控制(消息是通过chat系统发送还是通过短信系统发送)。
·保持了他们一贯的作风,小团队做出令人惊讶的事情:15个工程师花了1年的时间发布了20个新的基础服务。
·facebook不打算只使用一个数据库平台并在这之上实现标准化应用,他们会针对不同的应用使用不同的平台。
Facebook在HDFS/Hadoop/Hive上有了丰富的经验,并且成为HBase的大客户,这让我夜不能寐。与一个十分流行的产品合作并成为其产业链的一部分是所有产品的梦想。这正是HBase所得到的。由于HBase涵盖了诸如持久性,实时性,分布式,线性扩展,健壮性,海量数据,开源,key-value,列导向(column-oriented)等热点。我们有理由相信它能变得更加流行,特别是基于它被facebook使用的事实。
(原文作者Todd Hoff,C++代码规范的作者)
Posted by kaoshijuan on 2010 年 11 月 23 日 at 上午 9:22 under 未分类.
1 Comment.
摘要: Scala近期正式发布了2.8版本,这门优秀的编程语言将简洁、清晰的语法与面向对象和函数式编程范式无缝融合起来,同时又完全兼容于Java,这样Scala就能使用Java开发者所熟知的Java API和众多的框架了。在这种情况下,我们可以通过Scala改进并简化现有的Java框架。此外,Scala的学习门槛也非常低,因为我们可以轻松将其集成到“众所周知的Java世界中”。
... 阅读全文
摘要: Sometimes, the simplest things are the most difficult to explain. Scala’s interoperability with Java is completely unparalleled, even including languages like Groovy which tout their tight... 阅读全文
Unit的结果类型指的是函数没有返回有用的值。Scala的Unit类型比较接近Java的void类型,而且实际上Java里每一个返回void的方法都被映射为Scala里返回Unit的方法。因此结果类型为Unit的方法,仅仅是为了它们的副作用而运行。
- package org.apache.pivot.scala.log
-
- import scala.reflect.BeanProperty
- import io.Source
- import org.apache.pivot.wtk.content.ListViewItemRenderer
- import java.lang.String
- import org.apache.pivot.wtkx.{WTKX, WTKXSerializer}
-
- /*为了避免和scala.Application的名称冲突,这里修改了别名*/
- import org.apache.pivot.wtk.{ Application => PivotApplication, _}
- import org.apache.pivot.collections.{ArrayList, Map}
-
- /**
- * Created by IntelliJ IDEA.
- * User: Administrator
- * Date: 2010-8-26
- * Time: 10:36:45
- * To change this template use File | Settings | File Templates.
- */
-
- /*日志记录Bean对象,由于Scala标准生成的字段不使用getter/setter的格式,
- 但是提供了注解 @scala.reflect.BeanProperty ,使得编译器可以生成标准的JavaBean对象的getter/setter接口
- val 只生成了getter
- var 同时生成了getter和setter */
- class LogRecord ( @BeanProperty val threadName : String,
- @BeanProperty val date : String,
- @BeanProperty val time : String,
- @BeanProperty val module : String,
- @BeanProperty val level : String,
- @BeanProperty val content : String){
-
- /*
- 重载了 Any的 toString接口
- override关键字是必须的,在java中使用的是注解 @override,但是java中@override并不是必须的。
- 省略了 函数还回值,由编译器进行类型推演得到
- */
- override def toString() = {
- threadName +" "+date +" "+ time +" "+module +" "+level+" "+content
- }
-
- }
-
- /*
- LogRecord 类的半生对象
- 定义了 scala中的魔术接口 apply ,使得可以使用 LogRecord("sting 文本") 可以创建一个class LogRecord对象。
- apply方法的调用由编译器自动调用,我们只负责定义即可
- 我们要解析的日志格式
-
- threadName date time module Level content
- DVOSMAIN 08/26/10 10:17:17 LOGINFO : INFO - Debug level: 2
- DVOSMAIN 08/26/10 10:17:17 LOGINFO : INFO - *=ERROR WARNING EXCEPT
- DVOSMAIN 08/26/10 10:17:17 LOGINFO : INFO - LM=*
-
- */
- object LogRecord {
- def apply( line : String ) : LogRecord = {
-
- /*生成一个 Regex对象,用于模式匹配*/
- val logRegex = """([A-Za-z0-9]+) +([0-9/]*) +([0-9:]*) +([A-Z]*) +: *([A-Z_]+).*""".r
-
- line match {
- /*如果模式匹配成功,threadName,date,time,module,level 分配按次序绑定到模式匹配表达式中()的内容 */
- case logRegex(threadName,date,time,module,level) =>
- val logRecord: LogRecord = new LogRecord( threadName, date, time, module, level,line)
- logRecord
- /*模式匹配通配符,在没有匹配到的情况下,做通用处理。如果不添加,在匹配不到时会抛出 MatchError异常*/
- case _ =>
- val logRecord: LogRecord = new LogRecord("N/A","N/A","N/A","N/A","N/A","N/A")
- logRecord
- }
-
- }
- }
-
- /*
- Apache Pivot ListView ItemRenderer
- 重新定义了如何显示 LogRecord对象的 列表项目的渲染。
- 如果使用默认的,那么ListView显示对象时,直接调用对象的toString获得其字符串表示
- */
- class LogListViewItemRenderer extends ListViewItemRenderer {
-
- imageView.setVisible(false)
-
- override def render(item: AnyRef, index: Int, listView: ListView, selected: Boolean, checked: Boolean, highlighted: Boolean, disabled: Boolean) = {
- if ( item != null && item.isInstanceOf[LogRecord])
- {
- val log = item.asInstanceOf[LogRecord]
- label.setText(log.content)
-
-
- }
- }
-
-
-
- }
-
- /**
- 定义主窗口界面代码,必须继承自 org.apache.pivot.Application
- 使用了 @WTKX注解,该注解属于 wtkx的命名对象的绑定语法,在从wtkx文件加载GUI时,
- 调用bind接口可以自动和wtkx文件中声明的wtkx:id对象进行绑定,无需在掉吗中调用 get("name")
- */
- class MainWindow extends PivotApplication {
- var window : Window = null
- @WTKX var textInputFilePath : TextInput = null
- @WTKX var browsePushButton : PushButton = null
- @WTKX var loadPushButton : PushButton = null
- @WTKX var textInputThreadName :TextInput = null
- @WTKX var textInputModule : TextInput = null
- @WTKX var textInputLevel : TextInput = null
- @WTKX var textInputContent : TextInput = null
- @WTKX var logListView : ListView = null
-
-
- def resume = {}
-
- def suspend = {}
-
- def shutdown(optional: Boolean) = {
- if ( window != null)
- {
- window.close
- true
- }
- false
- }
-
- def startup(display: Display, properties: Map[String, String]) = {
- val wtkxSerializer = new WTKXSerializer()
- var matchString : String = null
-
- /*从xml(wtkx)文件加载GUI*/
- window = wtkxSerializer.readObject(this,"MainWindow.xml").asInstanceOf[Window]
-
- wtkxSerializer.bind(this)
- if ( properties containsKey "logfile")
- {
- textInputFilePath setText ( properties get "logfile")
- }
-
- /*给 Button添加事件处理函数*/
- browsePushButton.getButtonPressListeners.add( function2Listener (browseButtonPressed ) )
- loadPushButton.getButtonPressListeners.add( function2Listener(loadButtonPressed ))
-
-
- window.open(display)
-
- }
-
- /*浏览按钮事件处理,打开一个文件浏览窗口,让用户选择文件,并在用户关闭对话框时,捕获用户选择的文件名*/
- def browseButtonPressed( button : Button ) : Unit = {
- val dialog : FileBrowserSheet = new FileBrowserSheet(FileBrowserSheet.Mode.OPEN)
-
- dialog.open( window, new SheetCloseListener() {
- def sheetClosed(sheet: Sheet) = {
- if ( sheet.getResult)
- {
- val fileBrowseSheet = sheet.asInstanceOf[FileBrowserSheet]
- textInputFilePath.setText( fileBrowseSheet.getSelectedFile.getPath.toString)
- }
- }
- })
- }
-
- /*从log文件加载内容,每一行是一个日志记录
- for中使用了 if过滤器,只有条件符合了,才会进入for循环体。
-
- scala没有提供continu,而是在 for中提供了条件过滤来替代
- */
- def loadButtonPressed( button : Button ) : Unit = {
- val logFile = Source.fromFile(textInputFilePath.getText)
- val list = new ArrayList[LogRecord]
- for ( line <- logFile.getLines ; logRecord = LogRecord(line.trim);
- if ( textInputThreadName.getText == "" || textInputThreadName.getText.contains(logRecord.threadName) );
- if ( textInputModule.getText == "" || textInputModule.getText.contains(logRecord.module));
- if ( textInputLevel.getText == "" || textInputLevel.getText.contains(logRecord.level))){
-
- list add logRecord
- }
-
- logListView.setListData( list)
-
- }
- /*按钮事件辅助接口,用于把一个 事件处理函数转换为ButtonPressListener对象,也可以采取更高级的内容,使用implicit,这里没有使用*/
- def function2Listener( fun : Button => Unit ) :ButtonPressListener = {
- val listener = new ButtonPressListener()
- {
- def buttonPressed(button: Button) = {
- fun(button)
- }
- }
-
- listener
- }
- }
-
- /*
- 主函数
- 符合Pivot主函数入口规则
- */
- object LogAnalyse {
- def main ( args : Array[String]) : Unit = {
-
-
- DesktopApplicationContext.main( classOf[MainWindow], args)
-
-
- }
- }
package org.apache.pivot.scala.log
import scala.reflect.BeanProperty
import io.Source
import org.apache.pivot.wtk.content.ListViewItemRenderer
import java.lang.String
import org.apache.pivot.wtkx.{WTKX, WTKXSerializer}
/*为了避免和scala.Application的名称冲突,这里修改了别名*/
import org.apache.pivot.wtk.{ Application => PivotApplication, _}
import org.apache.pivot.collections.{ArrayList, Map}
/**
* Created by IntelliJ IDEA.
* User: Administrator
* Date: 2010-8-26
* Time: 10:36:45
* To change this template use File | Settings | File Templates.
*/
/*日志记录Bean对象,由于Scala标准生成的字段不使用getter/setter的格式,
但是提供了注解 @scala.reflect.BeanProperty ,使得编译器可以生成标准的JavaBean对象的getter/setter接口
val 只生成了getter
var 同时生成了getter和setter */
class LogRecord ( @BeanProperty val threadName : String,
@BeanProperty val date : String,
@BeanProperty val time : String,
@BeanProperty val module : String,
@BeanProperty val level : String,
@BeanProperty val content : String){
/*
重载了 Any的 toString接口
override关键字是必须的,在java中使用的是注解 @override,但是java中@override并不是必须的。
省略了 函数还回值,由编译器进行类型推演得到
*/
override def toString() = {
threadName +" "+date +" "+ time +" "+module +" "+level+" "+content
}
}
/*
LogRecord 类的半生对象
定义了 scala中的魔术接口 apply ,使得可以使用 LogRecord("sting 文本") 可以创建一个class LogRecord对象。
apply方法的调用由编译器自动调用,我们只负责定义即可
我们要解析的日志格式
threadName date time module Level content
DVOSMAIN 08/26/10 10:17:17 LOGINFO : INFO - Debug level: 2
DVOSMAIN 08/26/10 10:17:17 LOGINFO : INFO - *=ERROR WARNING EXCEPT
DVOSMAIN 08/26/10 10:17:17 LOGINFO : INFO - LM=*
*/
object LogRecord {
def apply( line : String ) : LogRecord = {
/*生成一个 Regex对象,用于模式匹配*/
val logRegex = """([A-Za-z0-9]+) +([0-9/]*) +([0-9:]*) +([A-Z]*) +: *([A-Z_]+).*""".r
line match {
/*如果模式匹配成功,threadName,date,time,module,level 分配按次序绑定到模式匹配表达式中()的内容 */
case logRegex(threadName,date,time,module,level) =>
val logRecord: LogRecord = new LogRecord( threadName, date, time, module, level,line)
logRecord
/*模式匹配通配符,在没有匹配到的情况下,做通用处理。如果不添加,在匹配不到时会抛出 MatchError异常*/
case _ =>
val logRecord: LogRecord = new LogRecord("N/A","N/A","N/A","N/A","N/A","N/A")
logRecord
}
}
}
/*
Apache Pivot ListView ItemRenderer
重新定义了如何显示 LogRecord对象的 列表项目的渲染。
如果使用默认的,那么ListView显示对象时,直接调用对象的toString获得其字符串表示
*/
class LogListViewItemRenderer extends ListViewItemRenderer {
imageView.setVisible(false)
override def render(item: AnyRef, index: Int, listView: ListView, selected: Boolean, checked: Boolean, highlighted: Boolean, disabled: Boolean) = {
if ( item != null && item.isInstanceOf[LogRecord])
{
val log = item.asInstanceOf[LogRecord]
label.setText(log.content)
}
}
}
/**
定义主窗口界面代码,必须继承自 org.apache.pivot.Application
使用了 @WTKX注解,该注解属于 wtkx的命名对象的绑定语法,在从wtkx文件加载GUI时,
调用bind接口可以自动和wtkx文件中声明的wtkx:id对象进行绑定,无需在掉吗中调用 get("name")
*/
class MainWindow extends PivotApplication {
var window : Window = null
@WTKX var textInputFilePath : TextInput = null
@WTKX var browsePushButton : PushButton = null
@WTKX var loadPushButton : PushButton = null
@WTKX var textInputThreadName :TextInput = null
@WTKX var textInputModule : TextInput = null
@WTKX var textInputLevel : TextInput = null
@WTKX var textInputContent : TextInput = null
@WTKX var logListView : ListView = null
def resume = {}
def suspend = {}
def shutdown(optional: Boolean) = {
if ( window != null)
{
window.close
true
}
false
}
def startup(display: Display, properties: Map[String, String]) = {
val wtkxSerializer = new WTKXSerializer()
var matchString : String = null
/*从xml(wtkx)文件加载GUI*/
window = wtkxSerializer.readObject(this,"MainWindow.xml").asInstanceOf[Window]
wtkxSerializer.bind(this)
if ( properties containsKey "logfile")
{
textInputFilePath setText ( properties get "logfile")
}
/*给 Button添加事件处理函数*/
browsePushButton.getButtonPressListeners.add( function2Listener (browseButtonPressed ) )
loadPushButton.getButtonPressListeners.add( function2Listener(loadButtonPressed ))
window.open(display)
}
/*浏览按钮事件处理,打开一个文件浏览窗口,让用户选择文件,并在用户关闭对话框时,捕获用户选择的文件名*/
def browseButtonPressed( button : Button ) : Unit = {
val dialog : FileBrowserSheet = new FileBrowserSheet(FileBrowserSheet.Mode.OPEN)
dialog.open( window, new SheetCloseListener() {
def sheetClosed(sheet: Sheet) = {
if ( sheet.getResult)
{
val fileBrowseSheet = sheet.asInstanceOf[FileBrowserSheet]
textInputFilePath.setText( fileBrowseSheet.getSelectedFile.getPath.toString)
}
}
})
}
/*从log文件加载内容,每一行是一个日志记录
for中使用了 if过滤器,只有条件符合了,才会进入for循环体。
scala没有提供continu,而是在 for中提供了条件过滤来替代
*/
def loadButtonPressed( button : Button ) : Unit = {
val logFile = Source.fromFile(textInputFilePath.getText)
val list = new ArrayList[LogRecord]
for ( line <- logFile.getLines ; logRecord = LogRecord(line.trim);
if ( textInputThreadName.getText == "" || textInputThreadName.getText.contains(logRecord.threadName) );
if ( textInputModule.getText == "" || textInputModule.getText.contains(logRecord.module));
if ( textInputLevel.getText == "" || textInputLevel.getText.contains(logRecord.level))){
list add logRecord
}
logListView.setListData( list)
}
/*按钮事件辅助接口,用于把一个 事件处理函数转换为ButtonPressListener对象,也可以采取更高级的内容,使用implicit,这里没有使用*/
def function2Listener( fun : Button => Unit ) :ButtonPressListener = {
val listener = new ButtonPressListener()
{
def buttonPressed(button: Button) = {
fun(button)
}
}
listener
}
}
/*
主函数
符合Pivot主函数入口规则
*/
object LogAnalyse {
def main ( args : Array[String]) : Unit = {
DesktopApplicationContext.main( classOf[MainWindow], args)
}
}
run:
使用 java时:
java -classpath
scala-library.jar;pivot-core-1.5.jar;pivot-tools-1.5.jar;pivot-wtk-1.5.jar;pivot-wtk-terra-1.5.jar;.
org.apache.pivot.scala.log.LogAnalyse
使用 scala时
java -classpath
pivot-core-1.5.jar;pivot-tools-1.5.jar;pivot-wtk-1.5.jar;pivot-wtk-terra-1.5.jar;.
org.apache.pivot.scala.log.LogAnalyse
这个功能只能用powerdesigner 的脚本功能来实现,使用起来也简单
打开powerdesigner,shift + ctrl + X 打开脚本窗口
输入执行的脚本,点 run 即可。
简单的导入Excel脚本
'开始
Option Explicit
Dim mdl ' the current model
Set mdl = ActiveModel
If (mdl Is Nothing) Then
MsgBox "There is no Active Model"
End If
Dim HaveExcel
Dim RQ
RQ = vbYes 'MsgBox("Is Excel Installed on your machine ?", vbYesNo + vbInformation, "Confirmation")
If RQ = vbYes Then
HaveExcel = True
' Open & Create Excel Document
Dim x1 '
Set x1 = CreateObject("Excel.Application")
x1.Workbooks.Open "D:\11.xls" '指定excel文档路径
x1.Workbooks(1).Worksheets("Sheet1").Activate '指定要打开的sheet名称
Else
HaveExcel = False
End If
a x1, mdl
sub a(x1, mdl)
dim rwIndex
dim tableName
dim colname
dim table
dim col
dim count
on error Resume Next
set table = mdl.Tables.CreateNew '创建一个表实体
table.Name = "卡片信息表" '指定表名,如果在Excel文档里有,也可以 .Cells(rwIndex, 3).Value 这样指定
table.Code = "AM_CARDINFO" '指定表名
count = count + 1
For rwIndex = 2 To 1000 '指定要遍历的Excel行标 由于第1行是表头,从第2行开始
With x1.Workbooks(1).Worksheets("Sheet1")
If .Cells(rwIndex, 1).Value = "" Then
Exit For
End If
set col = table.Columns.CreateNew '创建一列/字段
'MsgBox .Cells(rwIndex, 1).Value, vbOK + vbInformation, "列"
If .Cells(rwIndex, 3).Value = "" Then
col.Name = .Cells(rwIndex, 1).Value '指定列名
Else
col.Name = .Cells(rwIndex, 3).Value
End If
'MsgBox col.Name, vbOK + vbInformation, "列"
col.Code = .Cells(rwIndex, 1).Value '指定列名
col.DataType = .Cells(rwIndex, 2).Value '指定列数据类型
col.Comment = .Cells(rwIndex, 5).Value '指定列说明
If .Cells(rwIndex, 4).Value = "否" Then
col.Mandatory = true '指定列是否可空 true 为不可空
End If
If rwIndex = 2 Then
col.Primary = true '指定主键
End If
End With
Next
MsgBox "生成数据表结构共计 " + CStr(count), vbOK + vbInformation, "表"
Exit Sub
End sub
提示里不区分大小写, 多个提示用空格分开, 如:select /*+ hint1(tab1) hint2(TAB1 idx1) */ col1, col2 from tab1 where col1='xxx';
如果表使用了别名, 那么提示里也必须使用别名, 如:select /*+ hint1(t1) */ col1, col2 from tab1 t1 where col1='xxx';
在SQL优化过程中常见HINT的用法(前10个比较常用, 前3个最常用):
1. /*+ INDEX */ 和 /*+ INDEX(TABLE INDEX1, index2) */ 和 /*+ INDEX(tab1.col1 tab2.col2) */ 和 /*+ NO_INDEX */ 和 /*+ NO_INDEX(TABLE INDEX1, index2) */
表明对表选择索引的扫描方法. 第一种不指定索引名是让Oracle对表中可用索引比较并选择某个最佳索引; 第二种是指定索引名且可指定多个索引; 第三种是10g开始有的, 指定列名, 且表名可不用别名; 第四种即全表扫描; 第五种表示禁用某个索引, 特别适合于准备删除某个索引前的评估操作. 如果同时使用了INDEX和NO_INDEX则两个提示都会被忽略掉.
例如:SELECT /*+ INDEX(BSEMPMS SEX_INDEX) USE SEX_INDEX BECAUSE THERE ARE FEWMALE BSEMPMS */ FROM BSEMPMS WHERE SEX='M';
2. /*+ ORDERED */
FROM子句中默认最后一个表是驱动表,ORDERED将from子句中第一个表作为驱动表. 特别适合于多表连接非常慢时尝试.
例如:SELECT /*+ ORDERED */ A.COL1,B.COL2,C.COL3 FROM TABLE1 A,TABLE2 B,TABLE3 C WHERE A.COL1=B.COL1 AND B.COL1=C.COL1;
3. /*+ PARALLEL(table1,DEGREE) */ 和 /*+ NO_PARALLEL(table1) */
该提示会将需要执行全表扫描的查询分成多个部分(并行度)执行, 然后在不同的操作系统进程中处理每个部分. 该提示还可用于DML语句. 如果SQL里还有排序操作, 进程数会翻倍,此外还有一个一个负责组合这些部分的进程,如下面的例子会产生9个进程. 如果在提示中没有指定DEGREE, 那么就会使用创建表时的默认值. 该提示在默认情况下会使用APPEND提示. NO_PARALLEL是禁止并行操作,否则语句会使用由于定义了并行对象而产生的并行处理.
例如:select /*+ PARALLEL(tab_test,4) */ col1, col2 from tab_test order by col2;
4. /*+ FIRST_ROWS */ 和 /*+ FIRST_ROWS(n) */
表示用最快速度获得第1/n行, 获得最佳响应时间, 使资源消耗最小化.
在update和delete语句里会被忽略, 使用分组语句如group by/distinct/intersect/minus/union时也会被忽略.
例如:SELECT /*+ FIRST_ROWS */ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
5. /*+ RULE */
表明对语句块选择基于规则的优化方法.
例如:SELECT /*+ RULE */ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
6. /*+ FULL(TABLE) */
表明对表选择全局扫描的方法.
例如:SELECT /*+ FULL(A) */ EMP_NO,EMP_NAM FROM BSEMPMS A WHERE EMP_NO='SCOTT';
7. /*+ LEADING(TABLE) */
类似于ORDERED提示, 将指定的表作为连接次序中的驱动表.
8. /*+ USE_NL(TABLE1,TABLE2) */
将指定表与嵌套的连接的行源进行连接,以最快速度返回第一行再连接,与USE_MERGE刚好相反.
例如:SELECT /*+ ORDERED USE_NL(BSEMPMS) */ BSDPTMS.DPT_NO,BSEMPMS.EMP_NO,BSEMPMS.EMP_NAM FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
9. /*+ APPEND */ 和 /*+ NOAPPEND */
直接插入到表的最后,该提示不会检查当前是否有插入操作所需的块空间而是直接添加到新块中, 所以可以提高速度. 当然也会浪费些空间, 因为它不会使用那些做了delete操作的块空间. NOAPPEND提示则相反,所以会取消PARALLEL提示的默认APPEND提示.
例如:insert /*+ append */ into test1 select * from test4;
insert /*+ parallel(test1) noappend */ into test1 select * from test4;
10. /*+ USE_HASH(TABLE1,table2) */
将指定的表与其它行源通过哈希连接方式连接起来.为较大的结果集提供最佳响应时间. 类似于在连接表的结果中遍历每个表上每个结果的嵌套循环, 指定的hash表将被放入内存, 所以需要有足够的内存(hash_area_size或pga_aggregate_target)才能保证语句正确执行, 否则将在磁盘里进行.
例如:SELECT /*+ USE_HASH(BSEMPMS,BSDPTMS) */ * FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
----------------------------------------------------------------------------------------------------
11. /*+ USE_MERGE(TABLE) */
将指定的表与其它行源通过合并排序连接方式连接起来.特别适合于那种在多个表大量行上进行集合操作的查询, 它会将指定表检索到的的所有行排序后再被合并, 与USE_NL刚好相反.
例如:SELECT /*+ USE_MERGE(BSEMPMS,BSDPTMS) */ * FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
12. /*+ ALL_ROWS */
表明对语句块选择基于开销的优化方法,并获得最佳吞吐量,使资源消耗最小化. 可能会限制某些索引的使用.
例如:SELECT /*+ ALL+_ROWS */ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
13. /*+ CLUSTER(TABLE) */
提示明确表明对指定表选择簇扫描的访问方法. 如果经常访问连接表但很少修改它, 那就使用集群提示.
例如:SELECT /*+ CLUSTER */ BSEMPMS.EMP_NO,DPT_NO FROM BSEMPMS,BSDPTMS WHERE DPT_NO='TEC304' AND BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
14. /*+ INDEX_ASC(TABLE INDEX1, INDEX2) */
表明对表选择索引升序的扫描方法. 从8i开始, 这个提示和INDEX提示功能一样, 因为默认oracle就是按照升序扫描索引的, 除非未来oracle还退出降序扫描索引.
例如:SELECT /*+ INDEX_ASC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO='SCOTT';
15. /*+ INDEX_COMBINE(TABLE INDEX1, INDEX2) */
指定多个位图索引, 对于B树索引则使用INDEX这个提示,如果INDEX_COMBINE中没有提供作为参数的索引,将选择出位图索引的布尔组合方式.
例如:SELECT /*+ INDEX_COMBINE(BSEMPMS SAL_BMI HIREDATE_BMI) */ * FROM BSEMPMS WHERE SAL<5000000 AND HIREDATE<SYSDATE;
16. /*+ INDEX_JOIN(TABLE INDEX1, INDEX2) */
合并索引, 所有数据都已经包含在这两个索引里, 不会再去访问表, 比使用索引并通过rowid去扫描表要快5倍.
例如:SELECT /*+ INDEX_JOIN(BSEMPMS SAL_HMI HIREDATE_BMI) */ SAL,HIREDATE FROM BSEMPMS WHERE SAL<60000;
17. /*+ INDEX_DESC(TABLE INDEX1, INDEX2) */
表明对表选择索引降序的扫描方法.
例如:SELECT /*+ INDEX_DESC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO='SCOTT';
18. /*+ INDEX_FFS(TABLE INDEX_NAME) */
对指定的表执行快速全索引扫描,而不是全表扫描的办法.要求要检索的列都在索引里, 如果表有很多列时特别适用该提示.
例如:SELECT /*+ INDEX_FFS(BSEMPMS IN_EMPNAM) */ * FROM BSEMPMS WHERE DPT_NO='TEC305';
19. /*+ NO_EXPAND */
对于WHERE后面的OR 或者IN-LIST的查询语句,NO_EXPAND将阻止其基于优化器对其进行扩展, 缩短解析时间.
例如:SELECT /*+ NO_EXPAND */ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND SEX='M';
20. /*+ DRIVING_SITE(TABLE) */
强制与ORACLE所选择的位置不同的表进行查询执行.特别适用于通过dblink连接的远程表.
例如:SELECT /*+ DRIVING_SITE(DEPT) */ * FROM BSEMPMS,DEPT@BSDPTMS DEPT WHERE BSEMPMS.DPT_NO=DEPT.DPT_NO;
21. /*+ CACHE(TABLE) */ 和 /*+ NOCACHE(TABLE) */
当进行全表扫描时,CACHE提示能够将表全部缓存到内存中,这样访问同一个表的用户可直接在内存中查找数据. 比较适合数据量小但常被访问的表, 也可以建表时指定cache选项这样在第一次访问时就可以对其缓存. NOCACHE则表示对已经指定了CACHE选项的表不进行缓存.
例如:SELECT /*+ FULL(BSEMPMS) CAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;
22. /*+ PUSH_SUBQ */
当SQL里用到了子查询且返回相对少的行时, 该提示可以尽可能早对子查询进行评估从而改善性能, 不适用于合并连接或带远程表的连接.
例如:select /*+ PUSH_SUBQ */ emp.empno, emp.ename, itemno from emp, orders where emp.empno = orders.empno and emp.deptno = (select deptno from dept where loc='XXX');
23. /*+ INDEX_SS(TABLE INDEX1,INDEX2) */
指示对特定表的索引使用跳跃扫描, 即当组合索引的第一列不在where子句中时, 让其使用该索引.
一、建立表空间
CREATE TABLESPACE test
DATAFILE 'c:/oracle/oradata/db/test01.dbf' SIZE 50M
UNIFORM SIZE 1M; #指定区尺寸为128k,如不指定,区尺寸默认为64k
或
CREATE TABLESPACE test
DATAFILE 'c:/oracle/oradata/db/test01.dbf' SIZE 50M
MINIMUM EXTENT 50K EXTENT MANAGEMENT LOCAL
DEFAULT STORAGE (INITIAL 50K NEXT 50K MAXEXTENTS 100 PCTINCREASE 0);
可从dba_tablespaces中查看刚创建的表空间的信息
二、建立UNDO表空间
CREATE UNDO TABLESPACE test_undo
DATAFILE 'c:/oracle/oradata/db/test_undo.dbf' SIZE 50M
UNDO表空间的EXTENT是由本地管理的,而且在创建时的SQL语句中只能使用DATAFILE和EXTENT MANAGEMENT子句。
ORACLE规定在任何时刻只能将一个还原表空间赋予数据库,即在一个实例中可以有多个还原表空间存在,但只能有一个为活动的。可以使用ALTER SYSTEM命令进行还原表空间的切换。
SQL> ALTER SYSTEM SET UNDO_TABLESPACE = test_undo;
三、建立临时表空间
CREATE TEMPORARY TABLESPACE test_temp
TEMPFILE '/oracle/oradata/db/test_temp.dbf' SIZE 50M
查看系统当前默认的临时表空间
select * from dba_properties where property_name like 'DEFAULT%'
改变系统默认临时表空间
alter database default temporary tablespace test_temp;
四、改变表空间状态
1.使表空间脱机
ALTER TABLESPACE test OFFLINE;
如果是意外删除了数据文件,则必须带有RECOVER选项
ALTER TABLESPACE game test FOR RECOVER;
2.使表空间联机
ALTER TABLESPACE test ONLINE;
3.使数据文件脱机
ALTER DATABASE DATAFILE 3 OFFLINE;
4.使数据文件联机
ALTER DATABASE DATAFILE 3 ONLINE;
5.使表空间只读
ALTER TABLESPACE test READ ONLY;
6.使表空间可读写
ALTER TABLESPACE test READ WRITE;
五、删除表空间
DROP TABLESPACE test INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS;
DROP TABLESPACE 表空间名 [INCLUDING CONTENTS [AND DATAFILES] [CASCADE CONSTRAINTS]]
1. INCLUDING CONTENTS 子句用来删除段
2. AND DATAFILES 子句用来删除数据文件
3. CASCADE CONSTRAINTS 子句用来删除所有的引用完整性约束
六、扩展表空间
首先查看表空间的名字和所属文件
select tablespace_name, file_id, file_name,
round(bytes/(1024*1024),0) total_space
from dba_data_files
order by tablespace_name;
1.增加数据文件
ALTER TABLESPACE test
ADD DATAFILE '/oracle/oradata/db/test02.dbf' SIZE 1000M;
2.手动增加数据文件尺寸
ALTER DATABASE DATAFILE 'c:/oracle/oradata/db/test01.dbf'
RESIZE 100M;
3.设定数据文件自动扩展
ALTER DATABASE DATAFILE 'c:/oracle/oradata/db/test01.dbf'
AUTOEXTEND ON NEXT 100M
MAXSIZE 200M;
设定后可从dba_tablespace中查看表空间信息,从v$datafile中查看对应的数据文件信息
经过长时间学习创建Oracle表空间,于是和大家分享一下,看完本文你肯定有不少收获,希望本文能教会你更多东西。
1、先查询空闲空间
- select tablespace_name,file_id,block_id,bytes,blocks from dba_free_space;
2、增加Oracle表空间
先查询数据文件名称、大小和路径的信息,语句如下:
- select tablespace_name,file_id,bytes,file_name from dba_data_files;
3、修改文件大小语句如下
- alter database datafile
- '需要增加的数据文件路径,即上面查询出来的路径
- 'resize 800M;
4、创建Oracle表空间
- create tablespace test
- datafile '/home/app/oracle/oradata/oracle8i/test01.dbf' size 8M
- autoextend on
- next 5M
- maxsize 10M;
-
- create tablespace sales
- datafile '/home/app/oracle/oradata/oracle8i/sales01.dbf' size 800M
- autoextend on
- next 50M
- maxsize unlimited
- maxsize unlimited 是大小不受限制
-
- create tablespace sales
- datafile '/home/app/oracle/oradata/oracle8i/sales01.dbf' size 800M
- autoextend on
- next 50M
- maxsize 1000M
- extent management local uniform;
- unform表示区的大小相同,默认为1M
-
- create tablespace sales
- datafile '/home/app/oracle/oradata/oracle8i/sales01.dbf' size 800M
- autoextend on
- next 50M
- maxsize 1000M
- extent management local uniform size 500K;
- unform size 500K表示区的大小相同,为500K
-
- create tablespace sales
- datafile '/home/app/oracle/oradata/oracle8i/sales01.dbf' size 800M
- autoextend on
- next 50M
- maxsize 1000M
- extent management local autoallocate;
- autoallocate表示区的大小由随表的大小自动动态改变,大表使用大区小表使用小区
-
- create tablespace sales
- datafile '/home/app/oracle/oradata/oracle8i/sales01.dbf' size 800M
- autoextend on
- next 50M
- maxsize 1000M
- temporary;
- temporary创建字典管理临时表空间
-
- create temporary tablespace sales
- tempfile '/home/app/oracle/oradata/oracle8i/sales01.dbf' size 800M
- autoextend on
- next 50M
- maxsize 1000M
- 创建本地管理临时表空间,如果是临时表空间,所有语句中的datafile都换为tempfile
-
- 8i系统默认创建字典管理临时表空间,要创建本地管理临时表空间要加temporary tablespace关键字
- 创建本地管理临时表空间时,不得使用atuoallocate参数,系统默认创建uniform管理方式
-
- 为表空间增加数据文件:
- alter tablespace sales add
- datafile '/home/app/oracle/oradata/oracle8i/sales02.dbf' size 800M
- autoextend on next 50M
- maxsize 1000M;
创建本地管理临时Oracle表空间,如果是临时表空间,所有语句中的datafile都换为tempfile8i系统默认创建字典管理临时表空
间,要创建本地管理临时表空间要加temporary
tablespace关键字创建本地管理临时表空间时,不得使用atuoallocate参数,系统默认创建uniform管理方式
为表空间增加数据文件:
- alter tablespace sales add
- datafile '/home/app/oracle/oradata/oracle8i/sales02.dbf' size 800M
- autoextend on next 50M
- maxsize 1000M;
5、更改自动扩展属性:
- alter database datafile
- '/home/app/oracle/oradata/oracle8i/sales01.dbf',
- '/home/app/oracle/oradata/oracle8i/sales02.dbf'
- '/home/app/oracle/oradata/oracle8i/sales01.dbf
- autoextend off;
以上介绍创建Oracle表空间,在这里拿出来和大家分享一下,希望对大家有用。
Unix/Linux下一般想让某个程序在后台运行,很多都是使用 & 在程序结尾来让程序自动运行。比如我们要运行 mysql在后台:
/usr/local/mysql/bin/mysqld_safe –user=mysql &
但是我们很多程序并不象mysqld一样可以做成守护进程,可能我们的程序只是普通程序而已,一般这种程序即使使用 & 结尾,如果终端关闭,那么程序也会被关闭。为了能够后台运行,我们需要使用nohup这个命令,比如我们有个start.sh需要在后台运行,并且希望在后台能够一直运行,那么就使用nohup:
nohup /root/start.sh &
在shell中回车后提示:
[~]$ appending output to nohup.out
原程序的的标准输出被自动改向到当前目录下的nohup.out文件,起到了log的作用。
但是有时候在这一步会有问题,当把终端关闭后,进程会自动被关闭,察看nohup.out可以看到在关闭终端瞬间服务自动关闭。
咨询红旗Linux工程师后,他也不得其解,在我的终端上执行后,他启动的进程竟然在关闭终端后依然运行。
在第二遍给我演示时,我才发现我和他操作终端时的一个细节不同:他是在当shell中提示了nohup成功后还需要按终端上键盘任意键退回到
shell输入命令窗口,然后通过在shell中输入exit来退出终端;而我是每次在nohup执行成功后直接点关闭程序按钮关闭终端.。所以这时候会
断掉该命令所对应的session,导致nohup对应的进程被通知需要一起shutdown。
这个细节有人和我一样没注意到,所以在这儿记录一下了。
附:nohup命令参考
nohup 命令
用途:不挂断地运行命令。
语法:nohup Command [ Arg … ] [ & ]
描述:nohup 命令运行由 Command 参数和任何相关的 Arg 参数指定的命令,忽略所有挂断(SIGHUP)信号。在注销后使用
nohup 命令运行后台中的程序。要运行后台中的 nohup 命令,添加 & ( 表示”and”的符号)到命令的尾部。
无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中。如果当前目录的
nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中。如果没有文件能创建或打开以用于追加,那么 Command
参数指定的命令不可调用。如果标准错误是一个终端,那么把指定的命令写给标准错误的所有输出作为标准输出重定向到相同的文件描述符。
退出状态:该命令返回下列出口值:
126 可以查找但不能调用 Command 参数指定的命令。
127 nohup 命令发生错误或不能查找由 Command 参数指定的命令。
否则,nohup 命令的退出状态是 Command 参数指定命令的退出状态。
nohup命令及其输出文件
nohup命令:如果你正在运行一个进程,而且你觉得在退出帐户时该进程还不会结束,那么可以使用nohup命令。该命令可以在你退出帐户/关闭终端之后继续运行相应的进程。nohup就是不挂起的意思( n ohang up)。
该命令的一般形式为:nohup command &
使用nohup命令提交作业
如果使用nohup命令提交作业,那么在缺省情况下该作业的所有输出都被重定向到一个名为nohup.out的文件中,除非另外指定了输出文件:
nohup command > myout.file 2>&1 &
在上面的例子中,输出被重定向到myout.file文件中。
使用 jobs 查看任务。
使用 fg %n 关闭。
另外有两个常用的ftp工具ncftpget和ncftpput,可以实现后台的ftp上传和下载,这样就可以利用这些命令在后台上传和下载文件了。
|