1. 신뢰성이 높을것
2. 처리 효율이 좋을 것.
3. 일반적일 것.
4. 확장성이 있을 것.
5. 알기 쉬울 것.
6. 이식성이 높을 것. 
기존 package\res\Value\strings.xml에서 선언된 문구에서 리소스에서
추가적으로 package\test_Model\res\Value\strings.xml를 이용하여 Resource Value의 값을 추가할 경우.

사용법
<add-resource type="string" name="no_action"></add-resource>
<string name="no_action">No</string> 

예>
======================================================= 
package\res\Value\strings.xml

<String id="test1"> test 1</String>
<String id="test2"> test 2</String>
<String id="test3"> test 3</String>
======================================================= 


======================================================= 
package\test_Model\res\Value\strings.xml
<add-resource type="string" name="test4"></add-resource>
<String id="test4"> test 4</String>

=======================================================

위와 같이 사용하게 되면 실질적으로는 다음과 같음.

======================================================= 

Resource : Strings.xml

<String id="test1"> test 1</String>
<String id="test2"> test 2</String>
<String id="test3"> test 3</String>
<String id="test4"> test 4</String>
=======================================================

안드로이드는 리눅스 커널을 이용한다. 따라서 리눅스의 시작 시퀀스 이후에 안드로이드 고유 시스템이 동작하게 된다.

기본적인 동작을 잘 설명한 곳( http://samse.tistory.com/112?srchid=BR1http%3A%2F%2Fsamse.tistory.com%2F112 )이 있어서 일부 발췌해 온다.


리눅스는 기본적으로 init이 가장먼저 실행된다. init.rc 라는 이름의 파일에는 init이 해야할 작업들이 기록되어 있다. 파일시스템 마운팅, 폴더 권한설정, 캐시폴더 삭제, 시작프로그램 동작 등이 기록되어 있다. 우선 데몬을 올린다. 데몬은 init에 의해 리눅스와 같이 시작되었다가 리눅스가 종료될 때 없어지는 프로그램으로서 데몬을 작성하는 규격에 따라 만들어져야 한다. Zygote가 Dalvik을 초기화 한다. C 밑에 있는 기본라이브러리들은 런타임을 통해 실행되고 상위 서비스들은 Dalvik을 통해 실행된다. 이러한 과정들을 위한 설정은 해당하는 config 파일을 수정하면 된다. 어떤 동작들을 바꾸고 싶으면 기본적으로 init.rc를 바꾸면 되고 Zygote를 바꾸고 싶으면 그 설정파일을 바꾸면 된다. 그리고 시스템서버, 서페이스매니저, 오디오매니저들이 올라간다. 그 다음에는 시스템 서비스들이 활성화 된다. 이들은 서비스이므로 서비스매니저에 등록된다.

init은 /system/core/init 디렉토리에 관련 소스가 있다. init.rc 파일은 /system/core/rootdir/에 있다.

init이 하는 일과 init.rc 파일을 작성하는 방법에 대해서는 여기(http://www.aesop.or.kr/?document_srl=46566 )를 참조한다.

init이 하는 일을 정리하면 다음과 같다.

1. log system 초기화

2. /init.rc와 /init.%hardware$.rc 파일 파싱

3. 2번 스텝의 2개 파일에서 초기 선동작(early-init action) 수행

4. 디바이스 종속적인 초기화 작업 (예를 들어 /dev 에 모든 디바이스 노드 만듬)

5. property system 초기화 (property system은 공유 메모리로 동작하며 윈도우의 레지스트리와 같은 역활이다)

6. 2번 스텝의 2개 파일에서 초기 동작(init action) 수행

7. property service 시작

8. 2번 스텝의 2개 파일에서 선부팅(early boot), 부팅(boot) 동작 수행

9. 2번 스텝의 2개 파일에서 property 동작 수행

10. device/property가 /child process를 셋할때까지 대기하면서 rc 파일에 정의된 명령어를 수행한다.

위의 설명처럼 /out/target/product/XXX/init.rc 를 먼저 살펴 보자. 위에서 설명한 것처럼 폴더 권한 설정 등의 초기화 작업을 하고 나면

Demon을 올리는 작업을 하도록 되어 있다. 이를 정리하면 다음과 같다.

1) console : shell 을 시작 (system/core/sh/)

## Daemon processes to be run by init.
##
service console /system/bin/sh
console

2) abdb : abdb daemon을 시작 (system/core/adb/)

# adbd on at boot in emulator
on property:ro.kernel.qemu=1
start adbd

on property:persist.service.adb.enable=1
start adbd

on property:persist.service.adb.enable=0
stop adbd

3) servicemanager : binder system을 시작 (framework/base/cmds/servicemanager/)

service servicemanager /system/bin/servicemanager
user system
critical
onrestart restart zygote
onrestart restart media

4) vold : volume daemon 시작 (system/core/vold/)

service vold /system/bin/vold
socket vold stream 0660 root mount

#service mountd /system/bin/mountd
# socket mountd stream 0660 root mount

5) debuggerd : debug system을 시작 (system/debuggerd/)

service debuggerd /system/bin/debuggerd

6) rild : radio interface layer daemon을 시작 (hardware/ril/rild/)

service ril-daemon /system/bin/rild
socket rild stream 660 root radio
socket rild-debug stream 660 radio system
user root
group radio cache inet misc

7) Zygote : (frameworks/base/cmds/app_process/)

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
socket zygote stream 666
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on

8) media : AudioFlinger, MediaPlayerService, CameraService를 시작 (frameworks/base/media/mediaserver/)

service media /system/bin/mediaserver
user media
group system audio camera graphics inet net_bt net_bt_admin

관련 코드를 보면 굉장히 간단하다. (framework/base/media/mediaserver/main_mediaserver.cpp)

int main(int argc, char** argv)
{
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
LOGI("ServiceManager: %p", sm.get());
AudioFlinger::instantiate();
MediaPlayerService::instantiate();
CameraService::instantiate();

ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}

9) bootsound : boot sound (system/media/audio/ui/boot.mp3)를 재생, (그런데 이 바이너리는 실제 찾을 수 없네...?. 그리고 샘플 파일도 안 만들어 지네)

service bootsound /system/bin/playmp3
user media
group audio
oneshot

10) dbus : dbus daemon 시작 (BlueZ 즉 Bluetooth 관련 서비스) (external/dbus/bus/)

service dbus /system/bin/dbus-daemon --system --nofork
socket dbus stream 660 bluetooth bluetooth
user bluetooth
group bluetooth net_bt_admin

11) hcid : hcid의 stdout, stdin을 안드로이드 logging sytstem으로 redirect (external/bluez/hcid/)

service hcid /system/bin/hcid -s -n -f /etc/bluez/hcid.conf
socket bluetooth stream 660 bluetooth bluetooth
socket dbus_bluetooth stream 660 bluetooth bluetooth
# init.rc does not yet support applying capabilities, so run as root and
# let hcid drop uid to bluetooth with the right linux capabilities
group bluetooth net_bt_admin misc
disabled

12) hfag : Bluetooth handsfree audio gateway 시작 (BlueZ 만 사용) (external/bluez/utils/tools/)

service hfag /system/bin/sdptool add --channel=10 HFAG
user bluetooth
group bluetooth net_bt_admin
disabled
oneshot

13) hsag : Bluetooth headset audio gateway 시작 (BlueZ 만 사용) (external/bluez/utils/tools/)

service hsag /system/bin/sdptool add --channel=11 HSAG
user bluetooth
group bluetooth net_bt_admin
disabled
oneshot

14) install : install package daemon 시작 (frameworks/cmds/installd/)

service installd /system/bin/installd
socket installd stream 600 system system

15) flash_recovery : /system/recovery.img를 로드 (bootable/recovery/mtdutils/)

service flash_recovery /system/bin/flash_image recovery /system/recovery.img
oneshot

강조한 것처럼 바와 같이 servicemanager을 이용해서 zygote와 media를 시작하고 SD 카드와 USB의 mount 등을 처리하는 vold 데몬을 올린다. 이후 부팅 사운드를 재생하거나 다른 필요한 데몬들을 올리게 된다. 이후 다시 강조한 부분을 보면 zygote를 이용해서 system server 를 시작한다. 이후 media를 다시 시작한다(?)

안드로이드의 system server는 Native Service와 여러가지 기본 서비스(core system service 라고도 한다)들로 이루어진다.

/framework/base/cmds/system_server/library/system_init.cpp 를 보면 system_init() 함수가 있다. 이 파일은 실제 사용한다기 보다는 초기화 흐름을 알게 해주는 코드로 보인다.

extern "C" status_t system_init()
{
LOGI("Entered system_init()");

sp<ProcessState> proc(ProcessState::self());

sp<IServiceManager> sm = defaultServiceManager();
LOGI("ServiceManager: %p\n", sm.get());

sp<GrimReaper> grim = new GrimReaper();
sm->asBinder()->linkToDeath(grim, grim.get(), 0);

char propBuf[PROPERTY_VALUE_MAX];
property_get("system_init.startsurfaceflinger", propBuf, "1");
if (strcmp(propBuf, "1") == 0) {
// Start the SurfaceFlinger
SurfaceFlinger::instantiate();
}

// On the simulator, audioflinger et al don't get started the
// same way as on the device, and we need to start them here
if (!proc->supportsProcesses()) {

// Start the AudioFlinger
AudioFlinger::instantiate();

// Start the media playback service
MediaPlayerService::instantiate();

// Start the camera service
CameraService::instantiate();
}

// And now start the Android runtime. We have to do this bit
// of nastiness because the Android runtime initialization requires
// some of the core system services to already be started.
// All other servers should just start the Android runtime at
// the beginning of their processes's main(), before calling
// the init function.
LOGI("System server: starting Android runtime.\n");

AndroidRuntime* runtime = AndroidRuntime::getRuntime();

LOGI("System server: starting Android services.\n");
runtime->callStatic("com/android/server/SystemServer", "init2");

// If running in our own process, just go into the thread
// pool. Otherwise, call the initialization finished
// func to let this process continue its initilization.
if (proc->supportsProcesses()) {
LOGI("System server: entering thread pool.\n");
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
LOGI("System server: exiting thread pool.\n");
}
return NO_ERROR;
}

코드를 보면 알겠지만 크게 다음 순서로 초기화 과정이 이루어진다.

1. Native Service 초기화

SurfaceFlinger

AudioFlinger

MediaPlayerService

CameraService

2. Android RunTime 시작

SystemServer

안드로이드가 동작하기 위한 여러가지 기본 서비스들(또는 JAVA 서비스)을 시작하는 곳이 SystemServer이다.

파일 위치는 다음과 같다.

/frameworks/base/services/java/com/android/server/SystemServer.java

SystemServer 클래스는 init2() 함수를 호출하게되면 ServerThread를 동작시키고 여기에서 다양한 서비스들을 시작한다.

서비스 시작하는 순서대로 대충 정리하면 다음과 같다.(run 함수의 내용을 그냥 정리한 것이며 * 표시 없는 것은 주서비스이고 * 표시 있는 것은 부가 서비스이다)

Power Manager 시작

Activity Manager 생성

ActivityManager를 생성해서 context를 받으면 이후 서비스들에는 context를 전달한다.

(아직 이 구조는 잘 모르겠지만 서비스들에서 설정이나 특정 이벤트들을 브로드 캐스팅하기 위해서는 context를 이용해야 하는 것 같다. ActivityManger에서 이를 위한 초기화 과정이 이루어지고 이후 서비스들은 context를 가지고 서로간의 통신이나 Focus 이동이 생길 수 있도록 하고 있는 것 같다. 이것은 아직은 어디까지나 추측이다.)

Telephony Registry 시작

Package Manager 시작

System Process로 Activity Manager 설정

Context Manager 시작

System Context Providers 시작

Battery Service 시작

* Hardware Service 시작

Alarm Manager 시작

Sensor Service 시작

Window Manager 시작

Bluetooth Service 시작

BluetoothDeviceService

BluetoothA2dpService

*Status Bar Service 시작

ClipboardService

InputMethodManagerService

*NetStat Service 시작

*Connectivity Service 시작

*Notification Manager 시작

Mount Service 시작

*DeviceStorageMonitor Service 시작

*Location Manager 시작

*SearchManager Service 시작

*Clipboard Service 시작

*Fallback Checkin Service 시작

*WallpaperService 시작

*Audio Service 시작

*Headset Observer 시작

(?)AppWidgetService

*AdbSettingsObserver 시작

(?)HDMISettingsObserver 시작


Key events executed on key-up

Android 2.0 is designed to run on devices that use virtual keys for HOME, MENU, BACK, and SEARCH, rather than physical keys. To support the best user experience on those devices, the Android platform now executes these buttons at key-up, for a key-down/key-up pair, rather than key-down. This helps prevent accidental button events and lets the user press the button area and then drag out of it without generating an event.

This change in behavior should only affect your application if it is intercepting button events and taking an action on key-down, rather than on key-up. Especially if your application is intercepting the BACK key, you should make sure that your application is handling the key events properly.

In general, intercepting the BACK key in an application is not recommended, however, if your application is doing so and it invokes some action on key-down, rather than key-up, you should modify your code.

If your application will use APIs introduced in Android 2.0 (API Level 5), you can take advantage of new APIs for managing key-event pairs:

  • If you are intercepting the BACK key in an activity or dialog, just implement the new onBackPressed() method.
  • If you are intercepting the BACK key in a view, you should track the key event on key-down (through the new startTracking() method), then invoke the action at key up. Here's a pattern you can use:

http://docs.androidside.com/docs/sdk/android-2.0.html
        public boolean onKeyDown(int keyCode, KeyEvent event) {
           
    if (keyCode == KeyEvent.KEYCODE_BACK
                   
    && event.getRepeatCount() == 0) {
               
    event.startTracking();
               
    return true;
           
    }
           
    return super.onKeyDown(keyCode, event);
       
    }

       
    public boolean onKeyUp(int keyCode, KeyEvent event) {
           
    if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
                   
    && !event.isCanceled()) {
               
    // *** DO ACTION HERE ***
               
    return true;
           
    }
           
    return super.onKeyUp(keyCode, event);
       
    }

If you want to update a legacy application so that its handling of the BACK key works properly for both Android 2.0 and older platform versions, you can use an approach similar to that shown above. Your code can catch the target button event on key-down, set a flag to track the key event, and then also catch the event on key-up, executing the desired action if the tracking flag is set. You'll also want to watch for focus changes and clear the tracking flag when gaining/losing focus.

Android 에서 key, touch, trackball 같은 event의 경우에는 WindowManagerService라는 서비스에서 Event Dispatch에 의해서 관리가 됩니다.
Event Dispatch 경로를 간단히 나타내면 아래와 같습니다.
 
문제는 이 WindowManagerService에서 이벤트를 Dispatch할 때
Home Key와 Power(Endcall) Key의 경우에는 Event를 받을 View에 Dispatch하기 전에
먼저 HomeKey와 Power(Endcall)Key의 작업을 수행한 후 Dispatch시키지 않는 다는 것입니다.
 
이는 안드로이드 키이벤트 정책 상의 이유로
PhoneWindowManager의 interceptKeyTi(HomeKey관련) interceptKeyTq(Power,Endcall Key관련)함수에
해당 작업이 정의되어 있습니다.
 
실제로 이 두 함수를 수정하면 Event를 해당 View까지 Dispatch 시킬 수 있지만…
권장되는 사항은 아닙니다.
 
이와 같은 이유로 HomeKey와 Power(Endcall)Key는 keydown,up 과 같은 함수로 인식 할 수 없는 것입니다.

출처 : http://blog.naver.com/realwind/20119124672 

'안드로이드' 카테고리의 다른 글

안드로이드 부팅 순서  (0) 2011.05.16
Key events executed on key-up  (0) 2011.05.12
Thread 우선순위 설정.  (0) 2011.05.03
Looper  (0) 2011.05.03
DIP를 px로 변환하기  (0) 2011.04.21

public class SchedulingTest4 {

    public static void main(String args[]) {
        Thread[] t = new RunThread4[3];
    
        t[0] = new RunThread4("☆");
        t[0].start(); 
        t[0].setPriority(1);  // ☆ 가장 느린 우선 순위
        
        t[1] = new RunThread4("◑");        
        t[1].start();
        t[1].setPriority(5);

        t[2] = new RunThread4("○");        
        t[2].start();        
        t[2].setPriority(10); // ○ 가장 빠른 우선 순위
        /*
        System.out.println("t[0]" + t[0].getPriority());
        System.out.println("t[1]" + t[1].getPriority());
        System.out.println("t[2]" + t[2].getPriority());
        */        
    } 
}

Android OS에서 Looper와 Handler는 Thread간의 메시지 교환 메카니즘에 관여하는 객체이다.

특정 Thread에서 looper 객체를 생성하고, loop를 돌리는 전형적인 구조는 아래와 같다.
  
class LooperThread extends Thread {
      public Handler mHandler;
      
      public void run() {
          Looper.prepare();
          
          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };
          
          Looper.loop();
      }
  }
  

Looper Class

 Looper 클래스는 Thread에 한개씩만 존재할 수 있으며, 이름에서 알수 있듯이 Event Loop 및 Message Queue를 가지는 Class 이다. Looper 클래스는 이 클래스를 생성한 Thread에 묶이게 되며, 이 관계가 성립되면 이후 바꾸거나 끊을 수 없다. 그리고, 생성된 Looper Class는 해당 Thead의 TLS(Thread Local Storage) 영역에 객체가 저장된다.  
 
 Looper의 생성은 prepare()라는 static 메소드에 의해 이루어진다. 
 아래는 Framework의 소스이다. 이미 Thread에 Looper가 만들어진 경우 Runtime Exception을 발생시킨다.
  
[frameworks/base/core/java/android/os/looper.java]
 
     public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

  Looper Class의 생성자를 보면 다음과 같다.     
     
private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
}
 
 MessageQueue를 생성해 mQueue에 저장해 두며, 현재 Thread 객체를 mThread에 저장해 둔다.
 
 이제 Message Queue를 검색하고 Event를 처리하는 looper() static 메소드를 살펴보자.
 전형적인 while 형태로, queue.next()를 통해 queue안에 처리할 Message가 있는지 검사한다. Message가 있을 경우 Messsage의 Target 객체로 Message를 dispatch시킨다. 이때 Target 객체의 Type은 Handler Class이며 뒤에서 살펴보자.
 
 [frameworks/base/core/java/android/os/looper.java]
   
 public static final void loop() {
        Looper me = myLooper();
        MessageQueue queue = me.mQueue;
        while (true) {
            Message msg = queue.next(); // might block
            //if (!me.mRun) {
            //    break;
            //}
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                msg.target.dispatchMessage(msg);
                if (me.mLogging!= null) me.mLogging.println(
                        "<<<<< Finished to    " + msg.target + " "
                        + msg.callback);
                msg.recycle();
            }
        }
  }
 

Handler Class

 Handler Class는 Looper와는 다르게 생성한 Thread에 여러개가 붙을 수 있으며, Message Queue에 Message를 Post하고, 또 Looper가 Dispatch해 준 Message를 처리하는 일을 하게 된다. 일반적으로 Handler는
    Handler mHandler = new Handler();

로 생성하게 되는데, 그 생성자 내부를 살펴보면 다음과 같다.

[frameworks/base/core/java/android/os/handler.java]
    public Handler() {
        [....]
        
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = null;
    } 

 
Looper 클래스의 myLooper() static 메소드를 호출하는데, 이 메소드는 현재 Thread의 TLS 영역에 저장되어 있던 Looper 객체를 return 해주는 메소드이다. 
그리고, 만약 현재 Thread에 Looper가 생성되어 있지 않다면, Runtime Exception을 던진다. 따라서, User Thread라면 Looper를 prepare 시킨후 Handler를 생성해 주어야 한다. 그리고, Looper의 Queue를 자신의 mQueue 멤버에 저장해 둔다. 이는 Handler.post(msg) 등의 메소드에서 Message를 Looper의 Queue에 쌓아둘때 참조하기 위해서 이다. mCallback의 Callback 클래스를 지정해 두는 것으로 기본값은 null로 설정된다. mCallback 등은 메시지를 dispatch 받았을때 사용되는데, Handler.dispachMessage 메소드를 살펴보면 다음과 같다.

[frameworks/base/core/java/android/os/handler.java]

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }


이 메소드는 앞선 Loooper 클래스의 loop() 메소드에서 호출됨 상기하자.
메시지를 받으면 처리되는 우선 순위가 있는데, Message의 callback 이 지정되어 있으면, 그것을 실행시킨다.

handleCallback 메소드는 아래와 같다. 
    private final void handleCallback(Message message) {
        message.callback.run();
    }

사실 Message의 callback 멤버 Type은 Runnable 클래스이고, 그것의 run() 메소드를 실행하는 것이다. 
참고로, Message Queue에는 Handler.Post(Runnable)계열의 메소드를 통해 Runnable을 넣거나(Post 함수 내부에서 Message 객체로 포장된다) Handler.sendMessage(Message) 메소드를 통해 Message 객체를 넣을 수 있다.

msg.callback이 null인 경우,
            
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
            
Handler의 Callback 객체가 지정되어 있다면, Callback 객체로 메시지를 넘기고, 어느 경우에도 해당하지 않는다면, Handler Class의 handleMeessage() 메소드로 메시지를 넘겨 처리한다. 기본 Handler 클래스의 handleMessage()는 비어있는데, Handler를 사용하는 곳에서 직접 구현을 해 주면 된다. 

참고로, HandlerThread 클래스라는 것이 있는데, 사용상의 편의를 위해 내부적으로 Looper를 생성해 가지고 있는 Thread이다.

Thread, Looper, Handler의 일반적인 동작 관계를 나타내면 아래의 그림과 같다.
※ 관련 링크:

이전에 SQL Server 2005에서의 대량의 데이터를 로딩한 방법 별 수행 성능을 비교한 글을 올린 적이 있습니다. OPENROWSET, BCP, BULK INSERT, SSIS를 이용하여 데이터를 로딩하는 속도 비교를 측정한 글입니다 (http://www.sqlleader.com/mboard.asp?exec=view&strBoardID=SS2005TSQL&intSeq=351)

 

 

이번 글은 BULK INSERT 명령을 이용할 때 여러 경우에 대한 성능 비교 결과를 정리한 것입니다.

 

다음과 같은 형태의 10,000,000 건의 텍스트 파일을 Bulk Insert 명령을 이용하여 테이블로 로딩할 때 옵션에 따른 수행 시간 비교입니다각 경우에 대해 3회 반복 수행을 한 후평균 소요 시간을 이용하여 비교하였습니다.

 

 

[테스트 파일 - TestFile.txt]

Int형 순번 열, 100자리 문자 열 : 10,000,000 

 

 

[저장 테이블 - TESTTABLE]

USE TEMPDB

GO

 

----------------------------------------

--TEST TABLE

-----------------------------------------

CREATE TABLE TESTTABLE

(

        SEQ INT,

        COL CHAR(100)

)

GO

   ※ DB File Size 증가로 인한 지연을 막기 위해 DB Size를 충분히 크게 늘린 후 수행함.

 

 

 

TEST 1) 인덱스가 없는 테이블에 로딩

--인덱스가 없는 테이블에 로딩

BULK INSERT TESTTABLE FROM 'D:\TestFile.txt'

WITH

      (

         FIELDTERMINATOR =',',

         ROWTERMINATOR ='\n'

      )

GO

처리 시간

              1회 00:01:48

              2회 00:01:50

              3회 00:01:49

             평균 00:01:49 (109)

 

 

 

TEST 2) 인덱스가 없는 테이블에 로딩 + TABLOCK 옵션 적용

--인덱스가 없는 테이블에 로딩 + TABLOCK 옵션

BULK INSERT TESTTABLE FROM 'D:\TestFile.txt'

WITH

      (

         FIELDTERMINATOR =',',

         ROWTERMINATOR ='\n',

         TABLOCK

      )

GO

처리 시간

              1회 00:00:34

              2회 00:00:33

              3회 00:00:34

             평균 00:00:34 (34)

 

 

 

TEST 3) 인덱스가 없는 테이블에 로딩 + TABLOCK 옵션 적용 + 일괄 처리 크기 100,000으로 설정

--인덱스가 없는 테이블에 로딩 + TABLOCK 옵션 + RPB 100,000

BULK INSERT TESTTABLE FROM 'D:\TestFile.txt'

WITH

      (

         FIELDTERMINATOR =',',

         ROWTERMINATOR ='\n',

         TABLOCK,

         ROWS_PER_BATCH = 100000

      )

GO

처리 시간

              1회 00:00:33

              2회 00:00:33

              3회 00:00:34

             평균 00:00:33 (33)

 

 

 

 

[Clustered Index 생성]

--Seq 컬럼에 Clustered Index 생성

CREATE CLUSTERED INDEX CIX_TESTTABLE ON TESTTABLE(SEQ)

GO

 

 

 

TEST 4) Clustered Index가 있는 테이블에 로딩

--Clustered Index 있는 테이블에 로딩

BULK INSERT TESTTABLE FROM 'D:\TestFile.txt'

WITH

      (

         FIELDTERMINATOR =',',

         ROWTERMINATOR ='\n'

      )

GO

처리 시간

              1회 00:03:40

              2회 00:03:39

              3회 00:03:39

             평균 00:03:39 (219)

 

 

 

TEST 5) Clustered Index가 있는 테이블에 로딩 + 일괄 처리 크기 100,000

-- Clustered Index 있는 테이블에 로딩

-- RPB 100,000

BULK INSERT TESTTABLE FROM 'D:\TestFile.txt'

WITH

      (

         FIELDTERMINATOR =',',

         ROWTERMINATOR ='\n',

         ROWS_PER_BATCH = 100000

      )

GO

처리 시간

              1회 00:02:49

              2회 00:02:47

              3회 00:02:48

             평균 00:02:48 (168)

 

 

 

TEST 6) Clustered Index가 있는 테이블에 로딩 + ORDER 옵션

-- Clustered Index 있는 테이블에 로딩

-- ORDER 옵션

BULK INSERT TESTTABLE FROM 'D:\TestFile.txt'

WITH

      (

         FIELDTERMINATOR =',',

         ROWTERMINATOR ='\n',

         ORDER(SEQ ASC)

      )

GO

처리 시간

              1회 00:02:21

              2회 00:02:21

              3회 00:02:20

             평균 00:02:21 (141)

 

 

 

TEST 7) Clustered Index가 있는 테이블에 로딩 + 일괄 처리 크기 100,000 + ORDER 옵션

-- Clustered Index 있는 테이블에 로딩

-- RPB 100,000 + ORDER 옵션

BULK INSERT TESTTABLE FROM 'D:\TestFile.txt'

WITH

      (

         FIELDTERMINATOR =',',

         ROWTERMINATOR ='\n',

         ROWS_PER_BATCH = 100000,

         ORDER(SEQ ASC)

      )

GO

처리 시간

              1회 00:02:20

              2회 00:02:21

              3회 00:02:21

             평균 00:02:21 (141)

 

 

 

TEST 8) Clustered Index가 있는 테이블에 로딩 + ORDER 옵션 + TABLOCK 옵션

-- Clustered Index 있는 테이블에 로딩

-- ORDER 옵션 + TABLOCK 옵션

BULK INSERT TESTTABLE FROM 'D:\TestFile.txt'

WITH

      (

         FIELDTERMINATOR =',',

         ROWTERMINATOR ='\n',

         ORDER(SEQ ASC),

         TABLOCK

      )

GO

처리 시간

              1회 00:00:37

              2회 00:00:37

              3회 00:00:37

             평균 00:00:37 (37)

 

 

 

TEST 9) Clustered Index가 있는 테이블에 로딩 + ORDER 옵션 + TABLOCK 옵션 + 일괄 처리 크기 100,000

-- Clustered Index 있는 테이블에 로딩

-- RPB 100,000 + ORDER 옵션 + TABLOCK 옵션

BULK INSERT TESTTABLE FROM 'D:\TestFile.txt'

WITH

      (

         FIELDTERMINATOR =',',

         ROWTERMINATOR ='\n',

         ROWS_PER_BATCH = 100000,

         ORDER(SEQ ASC),

         TABLOCK

      )

GO

처리 시간

              1회 00:00:36

              2회 00:00:37

              3회 00:00:37

             평균 00:00:37 (37)

 

 

 

Clustered Index가 없는 힙(Heap) 테이블에 Bulk Insert 명령을 이용하여 로딩할 경우, TABLOCK의 옵션 설정에 따라 3배 정도의 속도 차이가 있습니다하지만일괄 처리 크기(Rows Per Batch) 설정은 성능에 별 영향을 미치지 않습니다(34 : 33)

TABLOCK 옵션은 Bulk Insert 작업 수행 시 해당 테이블에 테이블 수준의 잠금을 설정함으로써하위 수준의 잠금 사용 시 발생하는 잠금 경합(Escalation)을 줄일 수 있을 뿐만 아니라 잠금을 설정하고 해제하는 단계를 줄여주기 때문에 처리 성능이 크게 향상될 수 있습니다(옵션 적용 전 - 109옵션 적용 - 34) 하지만이 옵션을 적용하여 로딩할 경우에는 WITH (NOLOCK) 옵션 또는 WITH (READUNCOMMITTED) 옵션을 사용하더라도 로딩 중에 해당 테이블의 데이터를 읽을 수 없습니다따라서 로딩 중에 다른 부분에서 사용되는 테이블인 경우에는 이 옵션을 적용할 경우 블로킹을 발생시킬 수 있기 때문에 주의해야 합니다.

 

 

 

 

 

 

Clustered Index가 설정되어 있는 테이블인 경우에는 Heap 테이블과는 달리 몇 가지 옵션을 더 설정할 수 있습니다.

우선 옵션 없이 단순히 Bulk Insert를 수행할 경우입력된 데이터에 대해 인덱스 구성 작업을 수행해야 하기 때문에 힙 테이블의 로딩과 비교했을 때 훨씬 더 많은 시간이 소요됩니다(Heap - 109, Clustered Index - 219)

일괄 처리 크기(Rows Per Batch) 100,000건으로 설정하게 되면 전체 건(10,000,000)의 인덱스를 한 번에 구성하는 대신 100,000건씩 나누어서 구성하기 때문에 처리 시간이 단축될 수 있습니다(일괄 처리 - 219, RPB 100,000 168)

만약 입력되는 데이터가 적재될 테이블의 Clustered 열과 동일한 순서로 정렬된 데이터인 경우에는ORDER(열 이름 [ASC | DESC]) 옵션을 이용하여 처리 시간을 단축 시킬 수 있습니다본 테스트에서는 텍스트 파일의 첫 번째 열로 정렬된 데이터이며이 데이터가 테이블의 SEQ열로 입력되기 때문에 ORDER 옵션을 적용할 경우처리 시간이 단축됩니다(옵션 미 적용 - 219, ORDER 옵션 적용 - 141)

또한 Heap에서와 마찬가지로 TABLOCK 옵션을 설정하여 처리할 경우처리 시간이 크게 단축됩니다. 10,000,000건의 텍스트 파일에 대해 아무런 옵션 없이 Bulk Insert 작업을 수행한 경우에 비해 ORDER, TABLOCK 옵션을 이용하여 처리한 수행 시간이 6배 정도 빠르게 나타났습니다(219 : 37)

 

 

참고로이러한 사항은 BULK INSERT 명령뿐만 아니라 SSIS(Integration Services) 패키지의 데이터 로딩 작업에서도 설정할 수 있는 사항입니다.

 

- OLE DB 대상에서 빠른 로드 설정 후고급 OLE DB 대상 편집기 중 FastLoadOptions 부분에서 설정

    

 

- SQL Server 대상의 고급 Server 대상 편집기 중BulkInsertOrder 부분에서 설정

   

 

 

 

 

본 게시판에 실린 글은 누구나 복사하셔서 이용하셔도 되지만반드시 출처(SQLLeader.com및 글의 링크를 밝혀주셔야 합니다. 

'SQL' 카테고리의 다른 글

Alias(별칭) "AS"  (0) 2011.05.02
Case When문  (0) 2011.05.02
virtual private network의 약자로, 우리말로 가상사설망이라고 한다.

VPN이란 인터넷망과 같은 공중망을 사설망처럼 이용해 회선 비용을 크게 절감할 수 있는 기업통신 서비스.

인터넷망을 전용선처럼 사용할 수 있도록 특수통신체계와 암호화기법을 제공하는 서비스로 기업 본사와 지사 또는 지사간에 전용망을 설치한 것과 같은 효과를 거둘 수 있으며, 전용선에 비해 20∼80% 이상의 비용을 줄일 수 있다. 

뿐만 아니라 사용자의 이동성 보장과 편리한 네트워크 구성 등이 장점이 있다. 그러나 VPN 구축을 위해서는 데이터암호화하는 보안기술이 뒷받침돼야 한다. 

가상사설망서비스는 미국에서 1980년 말부터 시작된 이후에 미국의 US Sprint와 AT&T에 의해 급속히 확산 보급되었다. 국내에 도입된 것은 지난 98년이며 VPN의 약점이던 보안문제가 해결되면서 서비스의 확산 속도가 더욱 빨라지고 있다.

VPN모든 회사들이 저마다 개별적으로 회선을 임차하는 것보다 공중망을 공유함으로써 비용은 낮추면서도 전용회선과 거의 동등한 서비스를 제공하려는 아이디어에서 출발하였다. 

VPN은 공중망을 통해 데이터를 송신하기 전에 데이터를 암호화하고, 수신측에서 이를 복호화한다.

마이크로소프트, 3Com 등 몇몇 회사들이 PPTP (Point-to-Point Tunneling Protocol)라는 표준 프로토콜을 제안하였으며, 마이크로소프트는 이 프로토콜을 윈도우NT 서버에 내장시켰다. 
출처 :  네이버 지식사전

'기타' 카테고리의 다른 글

넥서스 S 바이너리 올리기  (0) 2012.11.25
[Hudson] 자동 빌드 설정 방법  (0) 2012.08.10
SVN GUI TOOL[SVN Tool - RapidSVN 사용법]  (0) 2011.02.09
SVN  (0) 2011.02.09
MMAP  (0) 2011.01.27

'SQL' 카테고리의 다른 글

Bulk Insert 옵션에 따른 성능 비교  (0) 2011.05.03
Case When문  (0) 2011.05.02

+ Recent posts