#
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上傳和下載,這樣就可以利用這些命令在后臺上傳和下載文件了。
|