ORA-00001: Unique constraint (OWNER.CONSTRAINT_NAME) violated
EXP-00002: error in writing to export file
IMP-00005: All allowable logon attempts failed
EXP-00009: No privilege to export %s's table %s
EXP-00011: %s.%s does not exist
EXP-00019: failed to process parameters, type 'EXP HELP=Y' for help
ORA-00020: Maximum number of processes (%s) exceeded
EXP-00028: Failed to open %s for write
EXP-00032: Non-DBAs may not export other users
ORA-00054: Resource busy and acquire with NOWAIT specified
EXP-00056: ORACLE error %lud encountered
IMP-00058: ORACLE error %lu encountered
ORA-00060: deadlock detected while waiting for resource
ORA-00100: No data found
ORA-00160: Global transaction length %s is greater than maximum (%s)
ORA-00257: Archiver error. Connect internal only, until freed
ORA-00376: file %s cannot be read at this time
ORA-00439: Feature not enabled: %s
TNS-00505: Operation timed out
TNS-00530: Protocol Adapter Error
ORA-00600: Internal error code, arguments: [%s], [%s], [%s], [%s], [%s], [%s]
ORA-00601: Cleanup lock conflict
ORA-00604: error occurred at recursive SQL level x
ORA-00900: Invalid SQL statement
ORA-00903: Invalid table name
ORA-00904: Invalid identifier
ORA-00905: Missing keyword
ORA-00907: Missing right parenthesis
ORA-00910: specified length too long for its datatype
ORA-00911: Invalid character
ORA-00917: Missing comma
ORA-00918: Column ambiguously defined
ORA-00920: Invalid relational operator
ORA-00921: Unexpected end of SQL command
ORA-00922: Missing or invalid option
ORA-00923: FROM keyword not found where expected
ORA-00924: Missing BY keyword
ORA-00927: missing equal sign
ORA-00932: inconsistent datatypes
ORA-00933: SQL command not properly ended
ORA-00936: Missing expression
ORA-00937: not a single-group group function
ORA-00942: Table or view does not exist
ORA-00947: Not enough values
ORA-00955: Name is already used by an existing object
ORA-00972: identifier is too long
ORA-00979: not a GROUP BY expression
ORA-00980: synonym translation is no longer valid
ORA-00984: Column not allowed here
ORA-00988: Missing or invalid password(s)
ORA-00995: Missing or invalid synonym identifier
ORA-00997: Illegal use of LONG datatype
ORA-00999: Invalid view name
ORA-01000: Maximum open cursors exceeded
ORA-01001: Invalid cursor
ORA-01008: not all variables bound
ORA-01012: Not logged on
ORA-01013: user requested cancel of current operation
ORA-01014: ORACLE shutdown in progress
ORA-01015: Logon called recursively
ORA-01017: invalid username/password; logon denied
ORA-01031: Insufficient privileges
ORA-01033: ORACLE initialization or shutdown in progress
ORA-01034: ORACLE not available
ORA-01077: Background process initialization failure
ORA-01078: failure in processing system parameters
ORA-01089: Immediate shutdown in progress - no operations are permitted
ORA-01090: Shutdown in progress - connection is not permitted
ORA-01092: ORACLE instance terminated. Disconnection forced
ORA-01109: Database not open
ORA-01110: data file %s: '%s'
ORA-01113: File %s needs media recovery
ORA-01119: Error in creating database file
ORA-01219: Database not open: queries allowed on fixed tables/views only
ORA-01254: Cannot end online backup - file %s in recovery manager backup
ORA-01340: NLS error
ORA-01400: Cannot insert NULL into (%s)
ORA-01401: Inserted value too large for column
ORA-01403: No data found
ORA-01406: Fetched column value was truncated
ORA-01410: invalid ROWID
ORA-01422: exact fetch returns more than requested number of rows
ORA-01427: Single-row subquery returns more than one row
ORA-01430: column being added already exists in table
ORA-01435: User does not exist
ORA-01438: Value larger than specified precision allows for this column
ORA-01457: Converting column overflows decimal datatype
ORA-01460: unimplemented or unreasonable conversion requested
ORA-01476: Divisor is equal to zero
ORA-01480: trailing null missing from STR bind value
ORA-01501: CREATE DATABASE failed
ORA-01547: Warning: RECOVER succeeded but OPEN RESETLOGS would get error below
ORA-01551: Extended rollback segment, pinned blocks released
ORA-01555: snapshot too old: rollback segment number %s with name \"%s\" too small
ORA-01562: Failed to extend rollback segment number %s
ORA-01591: Lock held by in-doubt distributed transaction 99.99.99999
ORA-01650: Unable to extend rollback segment %s by %s in tablespace %s
ORA-01652: unable to extend temp segment by %s in tablespace %s
ORA-01653: Unable to extend table %s.%s by %s in tablespace %s
ORA-01654: Unable to extend index %s.%s by %s in tablespace %s
ORA-01658: Unable to create INITIAL extent for segment in tablespace %s
ORA-01683: Unable to extend index %s.%s partition %s by %s in tablespace %s
ORA-01685: Max # extents (%s) reached in index %s.%s partition %s
ORA-01722: Invalid number
ORA-01727: Numeric precision specifier is out of range (1 to 38)
ORA-01732: Data manipulation operation not legal on this view
ORA-01741: Illegal zero-length identifier
ORA-01772: Must specify a value for LEVEL
ORA-01791: not a SELECTed expression
ORA-01810: Format code appears twice
ORA-01830: Date format picture ends before converting entire input string
ORA-01841: (full) year must be between -4713 and +9999, and not be 0
ORA-01843: Not a valid month
ORA-01847: Day of month must be between 1 and last day of month
ORA-01858: A non-numeric character was found where a numeric was expected
ORA-01860: week of year must be between 1 and 53
ORA-01861: Literal does not match format string
ORA-01918: user '%s' does not exist
ORA-02000: missing SHARED_POOL keyword
ORA-02019: connection description for remote database not found
ORA-02049: timeout: distributed transaction waiting for lock
ORA-02050: Transaction %s rolled back, some remote DBs may be in-doubt
ORA-02063: preceding %s%s from %s%s
ORA-02068: Following severe error from %s%s
ORA-02085: database link %s connects to %s
ORA-02248: Invalid option for ALTER SESSION
ORA-02289: Sequence does not exist
ORA-02290: Check constraint (%s.%s) violated
ORA-02291: integrity constraint (%s.%s) violated - parent key not found
ORA-02292: integrity constraint (%s.%s) violated - child record
ORA-02421: Missing or invalid schema authorization identifier
ORA-03113: End-of-file on communication channel
ORA-03114: not connected to ORACLE
ORA-03115: Unsupported network datatype or representation
ORA-03121: no interface driver connected - function not performed
ORA-03134: Connections to this server version are no longer supported.
ORA-03135: connection lost contact
TNS-03505: Failed to resolve name
ORA-04020: deadlock detected while trying to lock object %s%s%s%s%s
ORA-04030: Out of processes memory when trying to allocate %s bytes (%s, %s)
ORA-04031: Unable to allocate %s bytes of shared memory (\"%s\",\"%s\",\"%s\",\"%s\")
ORA-04062: %s of %s has been changed
ORA-04063: %s has errors
ORA-04067: Not executed, string does not exist
ORA-04068: Existing state of packages has been discarded
ORA-04088: Error during execution of trigger '%s.%s'
ORA-04098: Trigger '%s.%s' is invalid and failed re-validation
ORA-06502: PL/SQL: numeric or value error
ORA-06508: PL/SQL: could not find program unit being called
ORA-06509: PL/SQL: ICD vector missing for this package
ORA-06512: at line x
ORA-06519: active autonomous transaction detected and rolled back
ORA-06540: PL/SQL: compilation error
ORA-06550: line %s, column %s:\n%s
ORA-06552: PL/SQL: string
ORA-06553: PLS-string: string
ORA-07445: Exception encountered: core dump [string] [string] [string] [string] [string] [string]
ORA-12154: TNS:could not resolve service name
ORA-12162: TNS:service name is incorrectly specified
ORA-12170: TNS:Connect timeout occurred
ORA-12203: TNS:unable to connect to destination
ORA-12222: TNS:no support is available for the protocol indicated
ORA-12224: TNS:no listener
ORA-12451: Label not designated as USER or DATA
ORA-12500: TNS:listener failed to start a dedicated server process
ORA-12504: TNS:listener was not given the SID in CONNECT_DATA
ORA-12505: TNS:listener could not resolve SID given in connect descriptor
ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
ORA-12516: TNS:listener could not find available handler with matching protocol stack
ORA-12518: TNS:listener could not hand off client connection
ORA-12519: TNS:no appropriate service handler found
ORA-12520: TNS:listener could not find available handler for requested type of server
ORA-12523: TNS:listener could not find instance appropriate for the client connection
ORA-12528: TNS:listener: all appropriate instances are blocking new connections
ORA-12533: TNS:illegal ADDRESS parameters
ORA-12534: TNS:operation not supported
ORA-12535: TNS:operation timed out
ORA-12537: TNS:connection closed
ORA-12538: TNS:no such protocol adapter
ORA-12540: TNS:internal limit restriction exceeded
ORA-12541: TNS:no listener
ORA-12542: TNS:address already in use
ORA-12543: TNS:destination host unreachable
ORA-12545: Connect failed because target host or object does not exist
ORA-12546: TNS:permission denied
ORA-12547: TNS:lost contact
ORA-12552: TNS:operation was interrupted
ORA-12554: TNS:current operation is still in progress
ORA-12557: TNS:protocol adapter not loadable
ORA-12560: TNS:protocol adapter error
ORA-12564: TNS:connection refused
ORA-12570: TNS:packet reader failure
ORA-12571: TNS:packet writer failure
ORA-12638: Credential retrieval failed
ORA-12702: invalid NLS parameter string used in SQL function
ORA-12705: Invalid or unknown NLS parameter value specified
ORA-12801: Error signaled in parallel query server %s
ORA-12899: Value too large for column "%s"."%s"."%s" (actual: %d, maximum: %d)
ORA-17001: Internal Error
ORA-17002: Io exception: %s
ORA-17003: Invalid column index
ORA-17004: Invalid column type
ORA-17005: Unsupported column type
ORA-17006: Invalid column name
ORA-17007: Invalid dynamic column
ORA-17008: Closed Connection
ORA-17009: Closed Statement
ORA-17010: Closed Resultset
ORA-17011: Exhausted Resultset
ORA-17012: Parameter Type Conflict
ORA-17014: ResultSet.next was not called
ORA-17015: Statement was cancelled
ORA-17016: Statement timed out
ORA-17017: Cursor already initialized
ORA-17018: Invalid cursor
ORA-17019: Can only describe a query
ORA-17020: Invalid row prefetch
ORA-17022: Missing defines at index
ORA-17023: Unsupported feature
ORA-17024: No data read
ORA-17025: Error in defines.isNull ()
ORA-17026: Numeric Overflow
ORA-17027: Stream has already been closed
ORA-17028: Can not do new defines until the current ResultSet is closed
ORA-17029: setReadOnly: Read-only connections not supported
ORA-17030: READ_COMMITTED and SERIALIZABLE are the only valid transaction levels
ORA-17031: setAutoClose: Only support auto close mode on
ORA-17032: cannot set row prefetch to zero
ORA-17033: Malformed SQL92 string at position
ORA-17034: Non supported SQL92 token at position
ORA-17035: Character Set Not Supported !!
ORA-17036: exception in OracleNumber
ORA-17037: Fail to convert between UTF8 and UCS2
ORA-17038: Byte array not long enough
ORA-17039: Char array not long enough
ORA-17040: Sub Protocol must be specified in connection URL
ORA-17041: Missing IN or OUT parameter at index:
ORA-17042: Invalid Batch Value
ORA-17043: Invalid stream maximum size
ORA-17044: Internal error: Data array not allocated
ORA-17045: Internal error: Attempt to access bind values beyond the batch value
ORA-17046: Internal error: Invalid index for data access
ORA-17047: Error in Type Descriptor parse
ORA-17048: Undefined type
ORA-17049: Inconsistent java and sql object types
ORA-17050: no such element in vector
ORA-17051: This API cannot be be used for non-UDT types
ORA-17052: This ref is not valid
ORA-17053: The size is not valid
ORA-17054: The LOB locator is not valid
ORA-17056: Non supported character set (add orai18n.jar in your classpath)
ORA-17057: Closed LOB
ORA-17058: Internal error: Invalid NLS Conversion ratio
ORA-17059: Fail to convert to internal representation
ORA-17060: Fail to construct descriptor
ORA-17061: Missing descriptor
ORA-17062: Ref cursor is invalid
ORA-17063: Not in a transaction
ORA-17064: Invalid Sytnax or Database name is null
ORA-17065: Conversion class is null
ORA-17066: Access layer specific implementation needed
ORA-17067: Invalid Oracle URL specified
ORA-17068: Invalid argument(s) in call
ORA-17069: Use explicit XA call
ORA-17070: Data size bigger than max size for this type
ORA-17071: Exceeded maximum VARRAY limit
ORA-17072: Inserted value too large for column
ORA-17073: Logical handle no longer valid
ORA-17074: invalid name pattern
ORA-17075: Invalid operation for forward only resultset
ORA-17076: Invalid operation for read only resultset
ORA-17077: Fail to set REF value
ORA-17078: Cannot do the operation as connections are already opened
ORA-17079: User credentials doesn't match the existing ones
ORA-17080: invalid batch command
ORA-17081: Error occurred during batching
ORA-17082: No current row
ORA-17083: Not on the insert row
ORA-17084: Called on the insert row
ORA-17085: Value conflicts occurs
ORA-17086: Undefined column value on the insert row
ORA-17087: Ignored performance hint: setFetchDirection()
ORA-17088: Unsupported syntax for requested resultset type and concurrency level
ORA-17089: internal error
ORA-17090: operation not allowed
ORA-17091: Unable to create resultset at the requested type and/or concurrency level
ORA-17092: JDBC statements cannot be created or executed at end of call processing
ORA-17093: OCI operation returned OCI_SUCCESS_WITH_INFO
ORA-17094: Object type version mismatched
ORA-17095: Statement Cache size has not been set
ORA-17096: Statement Caching cannot be enabled for this logical connection
ORA-17097: Invalid PL/SQL Index Table element type
ORA-17098: Invalid empty lob operation
ORA-17099: Invalid PL/SQL Index Table array length
ORA-17100: Invalid database Java Object
ORA-17101: Invalid properties in OCI Connection Pool Object
ORA-17102: Bfile is read only
ORA-17103: invalid connection type to return via getConnection. Use getJavaSqlConnection instead
ORA-17104: SQL statement to execute cannot be empty or null
ORA-17105: connection session time zone was not set
ORA-17106: invalid combination of connections specified
ORA-17107: invalid proxy type specified
ORA-17108: No max length specified in defineColumnType
ORA-17109: standard Java character encoding not found
ORA-17110: execution completed with warning
ORA-17111: Invalid connection cache TTL timeout specified
ORA-17112: Invalid thread interval specified
ORA-17113: Thread interval value is more than the cache timeout value
ORA-17114: could not use local transaction commit in a global transaction
ORA-17115: could not use local transaction rollback in a global transaction
ORA-17116: could not turn on auto-commit in an active global transaction
ORA-17117: could not set savepoint in an active global transaction
ORA-17118: could not obtain ID for a named Savepoint
ORA-17119: could not obtain name for an un-named Savepoint
ORA-17120: could not set a Savepoint with auto-commit on
ORA-17121: could not rollback to a Savepoint with auto-commit on
ORA-17122: could not rollback to a local txn Savepoint in a global transaction
ORA-17123: Invalid statement cache size specified
ORA-17124: Invalid connection cache Inactivity timeout specified
ORA-17125: Improper statement type returned by explicit cache
ORA-17126: Fixed Wait timeout elapsed
ORA-17127: Invalid Fixed Wait timeout specified
ORA-17200: Unable to properly convert XA open string from Java to C
ORA-17201: Unable to properly convert XA close string from Java to C
ORA-17202: Unable to properly convert RM name from Java to C
ORA-17203: Could not cast pointer type to jlong
ORA-17204: Input array too short to hold OCI handles
ORA-17205: Failed to obtain OCISvcCtx handle from C-XA using xaoSvcCtx
ORA-17206: Failed to obtain OCIEnv handle from C-XA using xaoEnv
ORA-17207: The tnsEntry property was not set in DataSource
ORA-17213: C-XA returned XAER_RMERR during xa_open
ORA-17215: C-XA returned XAER_INVAL during xa_open
ORA-17216: C-XA returned XAER_PROTO during xa_open
ORA-17233: C-XA returned XAER_RMERR during xa_close
ORA-17235: C-XA returned XAER_INVAL during xa_close
ORA-17236: C-XA returned XAER_PROTO during xa_close
ORA-17401: Protocol violation
ORA-17402: Only one RPA message is expected
ORA-17403: Only one RXH message is expected
ORA-17404: Received more RXDs than expected
ORA-17405: UAC length is not zero
ORA-17406: Exceeding maximum buffer length
ORA-17407: invalid Type Representation(setRep)
ORA-17408: invalid Type Representation(getRep)
ORA-17409: invalid buffer length
ORA-17410: No more data to read from socket
ORA-17411: Data Type representations mismatch
ORA-17412: Bigger type length than Maximum
ORA-17413: Exceding key size
ORA-17414: Insufficient Buffer size to store Columns Names
ORA-17415: This type hasn't been handled
ORA-17416: FATAL
ORA-17417: NLS Problem, failed to decode column names
ORA-17418: Internal structure's field length error
ORA-17419: Invalid number of columns returned
ORA-17420: Oracle Version not defined
ORA-17421: Types or Connection not defined
ORA-17422: Invalid class in factory
ORA-17423: Using a PLSQL block without an IOV defined
ORA-17424: Attempting different marshaling operation
ORA-17425: Returning a stream in PLSQL block
ORA-17426: Both IN and OUT binds are NULL
ORA-17427: Using Uninitialized OAC
ORA-17428: Logon must be called after connect
ORA-17429: Must be at least connected to server
ORA-17430: Must be logged on to server
ORA-17431: SQL Statement to parse is null
ORA-17432: invalid options in all7
ORA-17433: invalid arguments in call
ORA-17434: not in streaming mode
ORA-17435: invalid number of in_out_binds in IOV
ORA-17436: invalid number of outbinds
ORA-17437: Error in PLSQL block IN/OUT argument(s)
ORA-17438: Internal - Unexpected value
ORA-17439: Invalid SQL type
ORA-17440: DBItem/DBType is null
ORA-17441: Oracle Version not supported. Minimum supported version is 7.2.3.
ORA-17442: Refcursor value is invalid
ORA-17443: Null user or password not supported in THIN driver
ORA-17444: TTC Protocol version received from server not supported
ORA-20000: A user specified error message
ORA-20001: A user specified error message
ORA-20002: A user specified error message
ORA-20003: A user specified error message
ORA-20004: A user specified error message
ORA-20005: A user specified error message
ORA-20006: A user specified error message
ORA-20007: A user specified error message
ORA-20008: A user specified error message
ORA-20009: A user specified error message
ORA-20010: A user specified error message
ORA-20011: A user specified error message
ORA-20012: A user specified error message
ORA-20013: A user specified error message
ORA-20014: A user specified error message
ORA-20015: A user specified error message
ORA-20016: A user specified error message
ORA-20017: A user specified error message
ORA-20018: A user specified error message
ORA-20019: A user specified error message
ORA-20020: A user specified error message
ORA-20021: A user specified error message
ORA-20022: A user specified error message
ORA-20023: A user specified error message
ORA-20024: A user specified error message
ORA-20025: A user specified error message
ORA-20026: A user specified error message
ORA-20027: A user specified error message
ORA-20028: A user specified error message
ORA-20029: A user specified error message
ORA-20030: A user specified error message
ORA-20031: A user specified error message
ORA-20032: A user specified error message
ORA-20033: A user specified error message
ORA-20034: A user specified error message
ORA-20035: A user specified error message
ORA-20036: A user specified error message
ORA-20037: A user specified error message
ORA-20038: A user specified error message
ORA-20039: A user specified error message
ORA-20040: A user specified error message
ORA-20041: A user specified error message
ORA-20042: A user specified error message
ORA-20043: A user specified error message
ORA-20044: A user specified error message
ORA-20045: A user specified error message
ORA-20046: A user specified error message
ORA-20047: A user specified error message
ORA-20048: A user specified error message
ORA-20049: A user specified error message
ORA-20050: A user specified error message
ORA-20051: A user specified error message
ORA-20052: A user specified error message
ORA-20053: A user specified error message
ORA-20054: A user specified error message
ORA-20055: A user specified error message
ORA-20056: A user specified error message
ORA-20057: A user specified error message
ORA-20058: A user specified error message
ORA-20059: A user specified error message
ORA-20060: A user specified error message
ORA-20061: A user specified error message
ORA-20062: A user specified error message
ORA-20063: A user specified error message
ORA-20064: A user specified error message
ORA-20065: A user specified error message
ORA-20066: A user specified error message
ORA-20067: A user specified error message
ORA-20068: A user specified error message
ORA-20069: A user specified error message
ORA-20070: A user specified error message
ORA-20071: A user specified error message
ORA-20072: A user specified error message
ORA-20073: A user specified error message
ORA-20074: A user specified error message
ORA-20075: A user specified error message
ORA-20076: A user specified error message
ORA-20077: A user specified error message
ORA-20078: A user specified error message
ORA-20079: A user specified error message
ORA-20080: A user specified error message
ORA-20081: A user specified error message
ORA-20082: A user specified error message
ORA-20083: A user specified error message
ORA-20084: A user specified error message
ORA-20085: A user specified error message
ORA-20086: A user specified error message
ORA-20087: A user specified error message
ORA-20088: A user specified error message
ORA-20089: A user specified error message
ORA-20090: A user specified error message
ORA-20091: A user specified error message
ORA-20092: A user specified error message
ORA-20093: A user specified error message
ORA-20094: A user specified error message
ORA-20095: A user specified error message
ORA-20096: A user specified error message
ORA-20097: A user specified error message
ORA-20098: A user specified error message
ORA-20099: A user specified error message
ORA-20100: A user specified error message
ORA-20101: A user specified error message
ORA-20102: A user specified error message
ORA-20103: A user specified error message
ORA-20104: A user specified error message
ORA-20105: A user specified error message
ORA-20106: A user specified error message
ORA-20107: A user specified error message
ORA-20108: A user specified error message
ORA-20109: A user specified error message
ORA-20110: A user specified error message
ORA-20111: A user specified error message
ORA-20112: A user specified error message
ORA-20113: A user specified error message
ORA-20114: A user specified error message
ORA-20115: A user specified error message
ORA-20116: A user specified error message
ORA-20117: A user specified error message
ORA-20118: A user specified error message
ORA-20119: A user specified error message
ORA-20120: A user specified error message
ORA-20121: A user specified error message
ORA-20122: A user specified error message
ORA-20123: A user specified error message
ORA-20124: A user specified error message
ORA-20125: A user specified error message
ORA-20126: A user specified error message
ORA-20127: A user specified error message
ORA-20128: A user specified error message
ORA-20129: A user specified error message
ORA-20130: A user specified error message
ORA-20131: A user specified error message
ORA-20132: A user specified error message
ORA-20133: A user specified error message
ORA-20134: A user specified error message
ORA-20135: A user specified error message
ORA-20136: A user specified error message
ORA-20137: A user specified error message
ORA-20138: A user specified error message
ORA-20139: A user specified error message
ORA-20140: A user specified error message
ORA-20141: A user specified error message
ORA-20142: A user specified error message
ORA-20143: A user specified error message
ORA-20144: A user specified error message
ORA-20145: A user specified error message
ORA-20146: A user specified error message
ORA-20147: A user specified error message
ORA-20148: A user specified error message
ORA-20149: A user specified error message
ORA-20150: A user specified error message
ORA-20151: A user specified error message
ORA-20152: A user specified error message
ORA-20153: A user specified error message
ORA-20154: A user specified error message
ORA-20155: A user specified error message
ORA-20156: A user specified error message
ORA-20157: A user specified error message
ORA-20158: A user specified error message
ORA-20159: A user specified error message
ORA-20160: A user specified error message
ORA-20161: A user specified error message
ORA-20162: A user specified error message
ORA-20163: A user specified error message
ORA-20164: A user specified error message
ORA-20165: A user specified error message
ORA-20166: A user specified error message
ORA-20167: A user specified error message
ORA-20168: A user specified error message
ORA-20169: A user specified error message
ORA-20170: A user specified error message
ORA-20171: A user specified error message
ORA-20172: A user specified error message
ORA-20173: A user specified error message
ORA-20174: A user specified error message
ORA-20175: A user specified error message
ORA-20176: A user specified error message
ORA-20177: A user specified error message
ORA-20178: A user specified error message
ORA-20179: A user specified error message
ORA-20180: A user specified error message
ORA-20181: A user specified error message
ORA-20182: A user specified error message
ORA-20183: A user specified error message
ORA-20184: A user specified error message
ORA-20185: A user specified error message
ORA-20186: A user specified error message
ORA-20187: A user specified error message
ORA-20188: A user specified error message
ORA-20189: A user specified error message
ORA-20190: A user specified error message
ORA-20191: A user specified error message
ORA-20192: A user specified error message
ORA-20193: A user specified error message
ORA-20194: A user specified error message
ORA-20195: A user specified error message
ORA-20196: A user specified error message
ORA-20197: A user specified error message
ORA-20198: A user specified error message
ORA-20199: A user specified error message
ORA-20200: A user specified error message
ORA-20201: A user specified error message
ORA-20202: A user specified error message
ORA-20203: A user specified error message
ORA-20204: A user specified error message
ORA-20205: A user specified error message
ORA-20206: A user specified error message
ORA-20207: A user specified error message
ORA-20208: A user specified error message
ORA-20209: A user specified error message
ORA-20210: A user specified error message
ORA-20211: A user specified error message
ORA-20212: A user specified error message
ORA-20213: A user specified error message
ORA-20214: A user specified error message
ORA-20215: A user specified error message
ORA-20216: A user specified error message
ORA-20217: A user specified error message
ORA-20218: A user specified error message
ORA-20219: A user specified error message
ORA-20220: A user specified error message
ORA-20221: A user specified error message
ORA-20222: A user specified error message
ORA-20223: A user specified error message
ORA-20224: A user specified error message
ORA-20225: A user specified error message
ORA-20226: A user specified error message
ORA-20227: A user specified error message
ORA-20228: A user specified error message
ORA-20229: A user specified error message
ORA-20230: A user specified error message
ORA-20231: A user specified error message
ORA-20232: A user specified error message
ORA-20233: A user specified error message
ORA-20234: A user specified error message
ORA-20235: A user specified error message
ORA-20236: A user specified error message
ORA-20237: A user specified error message
ORA-20238: A user specified error message
ORA-20239: A user specified error message
ORA-20240: A user specified error message
ORA-20241: A user specified error message
ORA-20242: A user specified error message
ORA-20243: A user specified error message
ORA-20244: A user specified error message
ORA-20245: A user specified error message
ORA-20246: A user specified error message
ORA-20247: A user specified error message
ORA-20248: A user specified error message
ORA-20249: A user specified error message
ORA-20250: A user specified error message
ORA-20251: A user specified error message
ORA-20252: A user specified error message
ORA-20253: A user specified error message
ORA-20254: A user specified error message
ORA-20255: A user specified error message
ORA-20256: A user specified error message
ORA-20257: A user specified error message
ORA-20258: A user specified error message
ORA-20259: A user specified error message
ORA-20260: A user specified error message
ORA-20261: A user specified error message
ORA-20262: A user specified error message
ORA-20263: A user specified error message
ORA-20264: A user specified error message
ORA-20265: A user specified error message
ORA-20266: A user specified error message
ORA-20267: A user specified error message
ORA-20268: A user specified error message
ORA-20269: A user specified error message
ORA-20270: A user specified error message
ORA-20271: A user specified error message
ORA-20272: A user specified error message
ORA-20273: A user specified error message
ORA-20274: A user specified error message
ORA-20275: A user specified error message
ORA-20276: A user specified error message
ORA-20277: A user specified error message
ORA-20278: A user specified error message
ORA-20279: A user specified error message
ORA-20280: A user specified error message
ORA-20281: A user specified error message
ORA-20282: A user specified error message
ORA-20283: A user specified error message
ORA-20284: A user specified error message
ORA-20285: A user specified error message
ORA-20286: A user specified error message
ORA-20287: A user specified error message
ORA-20288: A user specified error message
ORA-20289: A user specified error message
ORA-20290: A user specified error message
ORA-20291: A user specified error message
ORA-20292: A user specified error message
ORA-20293: A user specified error message
ORA-20294: A user specified error message
ORA-20295: A user specified error message
ORA-20296: A user specified error message
ORA-20297: A user specified error message
ORA-20298: A user specified error message
ORA-20299: A user specified error message
ORA-20300: A user specified error message
ORA-20301: A user specified error message
ORA-20302: A user specified error message
ORA-20303: A user specified error message
ORA-20304: A user specified error message
ORA-20305: A user specified error message
ORA-20306: A user specified error message
ORA-20307: A user specified error message
ORA-20308: A user specified error message
ORA-20309: A user specified error message
ORA-20310: A user specified error message
ORA-20311: A user specified error message
ORA-20312: A user specified error message
ORA-20313: A user specified error message
ORA-20314: A user specified error message
ORA-20315: A user specified error message
ORA-20316: A user specified error message
ORA-20317: A user specified error message
ORA-20318: A user specified error message
ORA-20319: A user specified error message
ORA-20320: A user specified error message
ORA-20321: A user specified error message
ORA-20322: A user specified error message
ORA-20323: A user specified error message
ORA-20324: A user specified error message
ORA-20325: A user specified error message
ORA-20326: A user specified error message
ORA-20327: A user specified error message
ORA-20328: A user specified error message
ORA-20329: A user specified error message
ORA-20330: A user specified error message
ORA-20331: A user specified error message
ORA-20332: A user specified error message
ORA-20333: A user specified error message
ORA-20334: A user specified error message
ORA-20335: A user specified error message
ORA-20336: A user specified error message
ORA-20337: A user specified error message
ORA-20338: A user specified error message
ORA-20339: A user specified error message
ORA-20340: A user specified error message
ORA-20341: A user specified error message
ORA-20342: A user specified error message
ORA-20343: A user specified error message
ORA-20344: A user specified error message
ORA-20345: A user specified error message
ORA-20346: A user specified error message
ORA-20347: A user specified error message
ORA-20348: A user specified error message
ORA-20349: A user specified error message
ORA-20350: A user specified error message
ORA-20351: A user specified error message
ORA-20352: A user specified error message
ORA-20353: A user specified error message
ORA-20354: A user specified error message
ORA-20355: A user specified error message
ORA-20356: A user specified error message
ORA-20357: A user specified error message
ORA-20358: A user specified error message
ORA-20359: A user specified error message
ORA-20360: A user specified error message
ORA-20361: A user specified error message
ORA-20362: A user specified error message
ORA-20363: A user specified error message
ORA-20364: A user specified error message
ORA-20365: A user specified error message
ORA-20366: A user specified error message
ORA-20367: A user specified error message
ORA-20368: A user specified error message
ORA-20369: A user specified error message
ORA-20370: A user specified error message
ORA-20371: A user specified error message
ORA-20372: A user specified error message
ORA-20373: A user specified error message
ORA-20374: A user specified error message
ORA-20375: A user specified error message
ORA-20376: A user specified error message
ORA-20377: A user specified error message
ORA-20378: A user specified error message
ORA-20379: A user specified error message
ORA-20380: A user specified error message
ORA-20381: A user specified error message
ORA-20382: A user specified error message
ORA-20383: A user specified error message
ORA-20384: A user specified error message
ORA-20385: A user specified error message
ORA-20386: A user specified error message
ORA-20387: A user specified error message
ORA-20388: A user specified error message
ORA-20389: A user specified error message
ORA-20390: A user specified error message
ORA-20391: A user specified error message
ORA-20392: A user specified error message
ORA-20393: A user specified error message
ORA-20394: A user specified error message
ORA-20395: A user specified error message
ORA-20396: A user specified error message
ORA-20397: A user specified error message
ORA-20398: A user specified error message
ORA-20399: A user specified error message
ORA-20400: A user specified error message
ORA-20401: A user specified error message
ORA-20402: A user specified error message
ORA-20403: A user specified error message
ORA-20404: A user specified error message
ORA-20405: A user specified error message
ORA-20406: A user specified error message
ORA-20407: A user specified error message
ORA-20408: A user specified error message
ORA-20409: A user specified error message
ORA-20410: A user specified error message
ORA-20411: A user specified error message
ORA-20412: A user specified error message
ORA-20413: A user specified error message
ORA-20414: A user specified error message
ORA-20415: A user specified error message
ORA-20416: A user specified error message
ORA-20417: A user specified error message
ORA-20418: A user specified error message
ORA-20419: A user specified error message
ORA-20420: A user specified error message
ORA-20421: A user specified error message
ORA-20422: A user specified error message
ORA-20423: A user specified error message
ORA-20424: A user specified error message
ORA-20425: A user specified error message
ORA-20426: A user specified error message
ORA-20427: A user specified error message
ORA-20428: A user specified error message
ORA-20429: A user specified error message
ORA-20430: A user specified error message
ORA-20431: A user specified error message
ORA-20432: A user specified error message
ORA-20433: A user specified error message
ORA-20434: A user specified error message
ORA-20435: A user specified error message
ORA-20436: A user specified error message
ORA-20437: A user specified error message
ORA-20438: A user specified error message
ORA-20439: A user specified error message
ORA-20440: A user specified error message
ORA-20441: A user specified error message
ORA-20442: A user specified error message
ORA-20443: A user specified error message
ORA-20444: A user specified error message
ORA-20445: A user specified error message
ORA-20446: A user specified error message
ORA-20447: A user specified error message
ORA-20448: A user specified error message
ORA-20449: A user specified error message
ORA-20450: A user specified error message
ORA-20451: A user specified error message
ORA-20452: A user specified error message
ORA-20453: A user specified error message
ORA-20454: A user specified error message
ORA-20455: A user specified error message
ORA-20456: A user specified error message
ORA-20457: A user specified error message
ORA-20458: A user specified error message
ORA-20459: A user specified error message
ORA-20460: A user specified error message
ORA-20461: A user specified error message
ORA-20462: A user specified error message
ORA-20463: A user specified error message
ORA-20464: A user specified error message
ORA-20465: A user specified error message
ORA-20466: A user specified error message
ORA-20467: A user specified error message
ORA-20468: A user specified error message
ORA-20469: A user specified error message
ORA-20470: A user specified error message
ORA-20471: A user specified error message
ORA-20472: A user specified error message
ORA-20473: A user specified error message
ORA-20474: A user specified error message
ORA-20475: A user specified error message
ORA-20476: A user specified error message
ORA-20477: A user specified error message
ORA-20478: A user specified error message
ORA-20479: A user specified error message
ORA-20480: A user specified error message
ORA-20481: A user specified error message
ORA-20482: A user specified error message
ORA-20483: A user specified error message
ORA-20484: A user specified error message
ORA-20485: A user specified error message
ORA-20486: A user specified error message
ORA-20487: A user specified error message
ORA-20488: A user specified error message
ORA-20489: A user specified error message
ORA-20490: A user specified error message
ORA-20491: A user specified error message
ORA-20492: A user specified error message
ORA-20493: A user specified error message
ORA-20494: A user specified error message
ORA-20495: A user specified error message
ORA-20496: A user specified error message
ORA-20497: A user specified error message
ORA-20498: A user specified error message
ORA-20499: A user specified error message
ORA-20500: A user specified error message
ORA-20501: A user specified error message
ORA-20502: A user specified error message
ORA-20503: A user specified error message
ORA-20504: A user specified error message
ORA-20505: A user specified error message
ORA-20506: A user specified error message
ORA-20507: A user specified error message
ORA-20508: A user specified error message
ORA-20509: A user specified error message
ORA-20510: A user specified error message
ORA-20511: A user specified error message
ORA-20512: A user specified error message
ORA-20513: A user specified error message
ORA-20514: A user specified error message
ORA-20515: A user specified error message
ORA-20516: A user specified error message
ORA-20517: A user specified error message
ORA-20518: A user specified error message
ORA-20519: A user specified error message
ORA-20520: A user specified error message
ORA-20521: A user specified error message
ORA-20522: A user specified error message
ORA-20523: A user specified error message
ORA-20524: A user specified error message
ORA-20525: A user specified error message
ORA-20526: A user specified error message
ORA-20527: A user specified error message
ORA-20528: A user specified error message
ORA-20529: A user specified error message
ORA-20530: A user specified error message
ORA-20531: A user specified error message
ORA-20532: A user specified error message
ORA-20533: A user specified error message
ORA-20534: A user specified error message
ORA-20535: A user specified error message
ORA-20536: A user specified error message
ORA-20537: A user specified error message
ORA-20538: A user specified error message
ORA-20539: A user specified error message
ORA-20540: A user specified error message
ORA-20541: A user specified error message
ORA-20542: A user specified error message
ORA-20543: A user specified error message
ORA-20544: A user specified error message
ORA-20545: A user specified error message
ORA-20546: A user specified error message
ORA-20547: A user specified error message
ORA-20548: A user specified error message
ORA-20549: A user specified error message
ORA-20550: A user specified error message
ORA-20551: A user specified error message
ORA-20552: A user specified error message
ORA-20553: A user specified error message
ORA-20554: A user specified error message
ORA-20555: A user specified error message
ORA-20556: A user specified error message
ORA-20557: A user specified error message
ORA-20558: A user specified error message
ORA-20559: A user specified error message
ORA-20560: A user specified error message
ORA-20561: A user specified error message
ORA-20562: A user specified error message
ORA-20563: A user specified error message
ORA-20564: A user specified error message
ORA-20565: A user specified error message
ORA-20566: A user specified error message
ORA-20567: A user specified error message
ORA-20568: A user specified error message
ORA-20569: A user specified error message
ORA-20570: A user specified error message
ORA-20571: A user specified error message
ORA-20572: A user specified error message
ORA-20573: A user specified error message
ORA-20574: A user specified error message
ORA-20575: A user specified error message
ORA-20576: A user specified error message
ORA-20577: A user specified error message
ORA-20578: A user specified error message
ORA-20579: A user specified error message
ORA-20580: A user specified error message
ORA-20581: A user specified error message
ORA-20582: A user specified error message
ORA-20583: A user specified error message
ORA-20584: A user specified error message
ORA-20585: A user specified error message
ORA-20586: A user specified error message
ORA-20587: A user specified error message
ORA-20588: A user specified error message
ORA-20589: A user specified error message
ORA-20590: A user specified error message
ORA-20591: A user specified error message
ORA-20592: A user specified error message
ORA-20593: A user specified error message
ORA-20594: A user specified error message
ORA-20595: A user specified error message
ORA-20596: A user specified error message
ORA-20597: A user specified error message
ORA-20598: A user specified error message
ORA-20599: A user specified error message
ORA-20600: A user specified error message
ORA-20601: A user specified error message
ORA-20602: A user specified error message
ORA-20603: A user specified error message
ORA-20604: A user specified error message
ORA-20605: A user specified error message
ORA-20606: A user specified error message
ORA-20607: A user specified error message
ORA-20608: A user specified error message
ORA-20609: A user specified error message
ORA-20610: A user specified error message
ORA-20611: A user specified error message
ORA-20612: A user specified error message
ORA-20613: A user specified error message
ORA-20614: A user specified error message
ORA-20615: A user specified error message
ORA-20616: A user specified error message
ORA-20617: A user specified error message
ORA-20618: A user specified error message
ORA-20619: A user specified error message
ORA-20620: A user specified error message
ORA-20621: A user specified error message
ORA-20622: A user specified error message
ORA-20623: A user specified error message
ORA-20624: A user specified error message
ORA-20625: A user specified error message
ORA-20626: A user specified error message
ORA-20627: A user specified error message
ORA-20628: A user specified error message
ORA-20629: A user specified error message
ORA-20630: A user specified error message
ORA-20631: A user specified error message
ORA-20632: A user specified error message
ORA-20633: A user specified error message
ORA-20634: A user specified error message
ORA-20635: A user specified error message
ORA-20636: A user specified error message
ORA-20637: A user specified error message
ORA-20638: A user specified error message
ORA-20639: A user specified error message
ORA-20640: A user specified error message
ORA-20641: A user specified error message
ORA-20642: A user specified error message
ORA-20643: A user specified error message
ORA-20644: A user specified error message
ORA-20645: A user specified error message
ORA-20646: A user specified error message
ORA-20647: A user specified error message
ORA-20648: A user specified error message
ORA-20649: A user specified error message
ORA-20650: A user specified error message
ORA-20651: A user specified error message
ORA-20652: A user specified error message
ORA-20653: A user specified error message
ORA-20654: A user specified error message
ORA-20655: A user specified error message
ORA-20656: A user specified error message
ORA-20657: A user specified error message
ORA-20658: A user specified error message
ORA-20659: A user specified error message
ORA-20660: A user specified error message
ORA-20661: A user specified error message
ORA-20662: A user specified error message
ORA-20663: A user specified error message
ORA-20664: A user specified error message
ORA-20665: A user specified error message
ORA-20666: A user specified error message
ORA-20667: A user specified error message
ORA-20668: A user specified error message
ORA-20669: A user specified error message
ORA-20670: A user specified error message
ORA-20671: A user specified error message
ORA-20672: A user specified error message
ORA-20673: A user specified error message
ORA-20674: A user specified error message
ORA-20675: A user specified error message
ORA-20676: A user specified error message
ORA-20677: A user specified error message
ORA-20678: A user specified error message
ORA-20679: A user specified error message
ORA-20680: A user specified error message
ORA-20681: A user specified error message
ORA-20682: A user specified error message
ORA-20683: A user specified error message
ORA-20684: A user specified error message
ORA-20685: A user specified error message
ORA-20686: A user specified error message
ORA-20687: A user specified error message
ORA-20688: A user specified error message
ORA-20689: A user specified error message
ORA-20690: A user specified error message
ORA-20691: A user specified error message
ORA-20692: A user specified error message
ORA-20693: A user specified error message
ORA-20694: A user specified error message
ORA-20695: A user specified error message
ORA-20696: A user specified error message
ORA-20697: A user specified error message
ORA-20698: A user specified error message
ORA-20699: A user specified error message
ORA-20700: A user specified error message
ORA-20701: A user specified error message
ORA-20702: A user specified error message
ORA-20703: A user specified error message
ORA-20704: A user specified error message
ORA-20705: A user specified error message
ORA-20706: A user specified error message
ORA-20707: A user specified error message
ORA-20708: A user specified error message
ORA-20709: A user specified error message
ORA-20710: A user specified error message
ORA-20711: A user specified error message
ORA-20712: A user specified error message
ORA-20713: A user specified error message
ORA-20714: A user specified error message
ORA-20715: A user specified error message
ORA-20716: A user specified error message
ORA-20717: A user specified error message
ORA-20718: A user specified error message
ORA-20719: A user specified error message
ORA-20720: A user specified error message
ORA-20721: A user specified error message
ORA-20722: A user specified error message
ORA-20723: A user specified error message
ORA-20724: A user specified error message
ORA-20725: A user specified error message
ORA-20726: A user specified error message
ORA-20727: A user specified error message
ORA-20728: A user specified error message
ORA-20729: A user specified error message
ORA-20730: A user specified error message
ORA-20731: A user specified error message
ORA-20732: A user specified error message
ORA-20733: A user specified error message
ORA-20734: A user specified error message
ORA-20735: A user specified error message
ORA-20736: A user specified error message
ORA-20737: A user specified error message
ORA-20738: A user specified error message
ORA-20739: A user specified error message
ORA-20740: A user specified error message
ORA-20741: A user specified error message
ORA-20742: A user specified error message
ORA-20743: A user specified error message
ORA-20744: A user specified error message
ORA-20745: A user specified error message
ORA-20746: A user specified error message
ORA-20747: A user specified error message
ORA-20748: A user specified error message
ORA-20749: A user specified error message
ORA-20750: A user specified error message
ORA-20751: A user specified error message
ORA-20752: A user specified error message
ORA-20753: A user specified error message
ORA-20754: A user specified error message
ORA-20755: A user specified error message
ORA-20756: A user specified error message
ORA-20757: A user specified error message
ORA-20758: A user specified error message
ORA-20759: A user specified error message
ORA-20760: A user specified error message
ORA-20761: A user specified error message
ORA-20762: A user specified error message
ORA-20763: A user specified error message
ORA-20764: A user specified error message
ORA-20765: A user specified error message
ORA-20766: A user specified error message
ORA-20767: A user specified error message
ORA-20768: A user specified error message
ORA-20769: A user specified error message
ORA-20770: A user specified error message
ORA-20771: A user specified error message
ORA-20772: A user specified error message
ORA-20773: A user specified error message
ORA-20774: A user specified error message
ORA-20775: A user specified error message
ORA-20776: A user specified error message
ORA-20777: A user specified error message
ORA-20778: A user specified error message
ORA-20779: A user specified error message
ORA-20780: A user specified error message
ORA-20781: A user specified error message
ORA-20782: A user specified error message
ORA-20783: A user specified error message
ORA-20784: A user specified error message
ORA-20785: A user specified error message
ORA-20786: A user specified error message
ORA-20787: A user specified error message
ORA-20788: A user specified error message
ORA-20789: A user specified error message
ORA-20790: A user specified error message
ORA-20791: A user specified error message
ORA-20792: A user specified error message
ORA-20793: A user specified error message
ORA-20794: A user specified error message
ORA-20795: A user specified error message
ORA-20796: A user specified error message
ORA-20797: A user specified error message
ORA-20798: A user specified error message
ORA-20799: A user specified error message
ORA-20800: A user specified error message
ORA-20801: A user specified error message
ORA-20802: A user specified error message
ORA-20803: A user specified error message
ORA-20804: A user specified error message
ORA-20805: A user specified error message
ORA-20806: A user specified error message
ORA-20807: A user specified error message
ORA-20808: A user specified error message
ORA-20809: A user specified error message
ORA-20810: A user specified error message
ORA-20811: A user specified error message
ORA-20812: A user specified error message
ORA-20813: A user specified error message
ORA-20814: A user specified error message
ORA-20815: A user specified error message
ORA-20816: A user specified error message
ORA-20817: A user specified error message
ORA-20818: A user specified error message
ORA-20819: A user specified error message
ORA-20820: A user specified error message
ORA-20821: A user specified error message
ORA-20822: A user specified error message
ORA-20823: A user specified error message
ORA-20824: A user specified error message
ORA-20825: A user specified error message
ORA-20826: A user specified error message
ORA-20827: A user specified error message
ORA-20828: A user specified error message
ORA-20829: A user specified error message
ORA-20830: A user specified error message
ORA-20831: A user specified error message
ORA-20832: A user specified error message
ORA-20833: A user specified error message
ORA-20834: A user specified error message
ORA-20835: A user specified error message
ORA-20836: A user specified error message
ORA-20837: A user specified error message
ORA-20838: A user specified error message
ORA-20839: A user specified error message
ORA-20840: A user specified error message
ORA-20841: A user specified error message
ORA-20842: A user specified error message
ORA-20843: A user specified error message
ORA-20844: A user specified error message
ORA-20845: A user specified error message
ORA-20846: A user specified error message
ORA-20847: A user specified error message
ORA-20848: A user specified error message
ORA-20849: A user specified error message
ORA-20850: A user specified error message
ORA-20851: A user specified error message
ORA-20852: A user specified error message
ORA-20853: A user specified error message
ORA-20854: A user specified error message
ORA-20855: A user specified error message
ORA-20856: A user specified error message
ORA-20857: A user specified error message
ORA-20858: A user specified error message
ORA-20859: A user specified error message
ORA-20860: A user specified error message
ORA-20861: A user specified error message
ORA-20862: A user specified error message
ORA-20863: A user specified error message
ORA-20864: A user specified error message
ORA-20865: A user specified error message
ORA-20866: A user specified error message
ORA-20867: A user specified error message
ORA-20868: A user specified error message
ORA-20869: A user specified error message
ORA-20870: A user specified error message
ORA-20871: A user specified error message
ORA-20872: A user specified error message
ORA-20873: A user specified error message
ORA-20874: A user specified error message
ORA-20875: A user specified error message
ORA-20876: A user specified error message
ORA-20877: A user specified error message
ORA-20878: A user specified error message
ORA-20879: A user specified error message
ORA-20880: A user specified error message
ORA-20881: A user specified error message
ORA-20882: A user specified error message
ORA-20883: A user specified error message
ORA-20884: A user specified error message
ORA-20885: A user specified error message
ORA-20886: A user specified error message
ORA-20887: A user specified error message
ORA-20888: A user specified error message
ORA-20889: A user specified error message
ORA-20890: A user specified error message
ORA-20891: A user specified error message
ORA-20892: A user specified error message
ORA-20893: A user specified error message
ORA-20894: A user specified error message
ORA-20895: A user specified error message
ORA-20896: A user specified error message
ORA-20897: A user specified error message
ORA-20898: A user specified error message
ORA-20899: A user specified error message
ORA-20900: A user specified error message
ORA-20901: A user specified error message
ORA-20902: A user specified error message
ORA-20903: A user specified error message
ORA-20904: A user specified error message
ORA-20905: A user specified error message
ORA-20906: A user specified error message
ORA-20907: A user specified error message
ORA-20908: A user specified error message
ORA-20909: A user specified error message
ORA-20910: A user specified error message
ORA-20911: A user specified error message
ORA-20912: A user specified error message
ORA-20913: A user specified error message
ORA-20914: A user specified error message
ORA-20915: A user specified error message
ORA-20916: A user specified error message
ORA-20917: A user specified error message
ORA-20918: A user specified error message
ORA-20919: A user specified error message
ORA-20920: A user specified error message
ORA-20921: A user specified error message
ORA-20922: A user specified error message
ORA-20923: A user specified error message
ORA-20924: A user specified error message
ORA-20925: A user specified error message
ORA-20926: A user specified error message
ORA-20927: A user specified error message
ORA-20928: A user specified error message
ORA-20929: A user specified error message
ORA-20930: A user specified error message
ORA-20931: A user specified error message
ORA-20932: A user specified error message
ORA-20933: A user specified error message
ORA-20934: A user specified error message
ORA-20935: A user specified error message
ORA-20936: A user specified error message
ORA-20937: A user specified error message
ORA-20938: A user specified error message
ORA-20939: A user specified error message
ORA-20940: A user specified error message
ORA-20941: A user specified error message
ORA-20942: A user specified error message
ORA-20943: A user specified error message
ORA-20944: A user specified error message
ORA-20945: A user specified error message
ORA-20946: A user specified error message
ORA-20947: A user specified error message
ORA-20948: A user specified error message
ORA-20949: A user specified error message
ORA-20950: A user specified error message
ORA-20951: A user specified error message
ORA-20952: A user specified error message
ORA-20953: A user specified error message
ORA-20954: A user specified error message
ORA-20955: A user specified error message
ORA-20956: A user specified error message
ORA-20957: A user specified error message
ORA-20958: A user specified error message
ORA-20959: A user specified error message
ORA-20960: A user specified error message
ORA-20961: A user specified error message
ORA-20962: A user specified error message
ORA-20963: A user specified error message
ORA-20964: A user specified error message
ORA-20965: A user specified error message
ORA-20966: A user specified error message
ORA-20967: A user specified error message
ORA-20968: A user specified error message
ORA-20969: A user specified error message
ORA-20970: A user specified error message
ORA-20971: A user specified error message
ORA-20972: A user specified error message
ORA-20973: A user specified error message
ORA-20974: A user specified error message
ORA-20975: A user specified error message
ORA-20976: A user specified error message
ORA-20977: A user specified error message
ORA-20978: A user specified error message
ORA-20979: A user specified error message
ORA-20980: A user specified error message
ORA-20981: A user specified error message
ORA-20982: A user specified error message
ORA-20983: A user specified error message
ORA-20984: A user specified error message
ORA-20985: A user specified error message
ORA-20986: A user specified error message
ORA-20987: A user specified error message
ORA-20988: A user specified error message
ORA-20989: A user specified error message
ORA-20990: A user specified error message
ORA-20991: A user specified error message
ORA-20992: A user specified error message
ORA-20993: A user specified error message
ORA-20994: A user specified error message
ORA-20995: A user specified error message
ORA-20996: A user specified error message
ORA-20997: A user specified error message
ORA-20998: A user specified error message
ORA-20999: A user specified error message
ORA-21500: internal error code, arguments: [string], [string], [string], [string], [string], [string], [string], [string]
ORA-22835: Buffer too small for CLOB to CHAR or BLOB to RAW conversion (actual: %s, maximum: %s)
ORA-24439: Connections to Oracle7 server are no longer supported
ORA-24909: Call in progress. Current operation cancelled
ORA-25150: ALTERING of extent parameters not permitted
ORA-27040: skgfrcre: create error, unable to create file
ORA-27101: Shared memory realm does not exist
ORA-28000: The account is locked
ORA-28001: The password has expired
ORA-28002: The password will expire within %s days
ORA-28003: Password verification for the specified password failed
ORA-28007: The password cannot be reused
ORA-28009: Connection as SYS should be as SYSDBA or SYSOPER
ORA-28011: The account will expire soon; change your password now
ORA-28221: REPLACE not specified
ORA-28547: connection to server failed, probable Oracle Net admin error
ORA-30021: Operation not allowed on undo tablespace
ORA-30036: Unable to extend segment by %s in undo tablespace '%s'
ORA-38301: can not perform DDL/DML over objects in Recycle Bin
FRM-40505: Unable to perform query
FRM-40508: Unable to insert record
FRM-40735: WHEN-BUTTON_PRESSED trigger raised unhandled exception

'oracle' 카테고리의 다른 글

오라클 noarchive -> archive 변환  (0) 2008.03.02
오라클 scott/tiger 접속  (0) 2008.02.18
오라클 10g  (0) 2008.02.14

아파치 error_log 에서 client denied by server configuration 에러가 날때(403 에러남)

[Thu Feb 14 17:16:34 2008] [error] [client xxx.xxx.xxx.xxx] client denied by serv
er configuration: /usr/local/tomcat/webapps/ROOT/

httpd.conf파일 열어서


아래와 같은 부분을 수정하면 됩니다.


<Directory />
   Options FollowSymLinks
   AllowOverride None
   Order deny,allow
   Deny from all (삭제)  
   Allow from all (추가)
</Directory>

'server > apache' 카테고리의 다른 글

웹서버 용량산정 참조 자료  (0) 2013.07.24
ww  (0) 2010.02.10
apache + php segmentation fault  (0) 2010.02.10
 
1주 — 플래시백 버전 질의
2주 — 롤백 모니터링
3주 — 테이블스페이스 관리
4주 — Oracle Data Pump
5주 — Flashback Table
6주 — Automatic Workload Repository
7주 — SQL*Plus의 향상된 기능
8주 — Automatic Storage Management
9주 — RMAN
10주 — 감사기능의 확장
11주 — Wait 인터페이스
12주 — Materialized Views
13주 — Enterprise Manager 10g
14주 — Virtual Private Database
15주 — 세그먼트의 관리
16주 — Transportable Tablespace
17주 — Automatic Shared Memory Management
18주 — ADDM과 SQL Tuning Advisor
19주 — Scheduler
20주 — 그 밖의 주요 기능
 
 
첫번째.
사진이 아닌 동영상으로: Flashback 버전 질의
전혀 아무런 설정 없이도 행의 모든 변경 내용을 즉시 식별 가능
Oracle9i Database에는 Flashback 질의 형태의 소위 “타임 머신” 기능이 이미 도입된 바 있습니다. DBA는 이 기능을 통해 실행 취소 세그먼트에 블록의 이전 이미지 복사본이 있으면 열의 값을 특정 시간으로 확인할 수 있습니다. 하지만 Flashback 질의는 두 시점 간의 변경된 데이타를 표시하는 대신 데이타의 고정된 스냅샷을 시간으로 나타내는 데 그칩니다. 외환 관리 등과 관련된 일부 애플리케이션에서는 값 데이타의 변경 내용을 두 시점에서가 아닌 일정 기간으로 확인해야 합니다. Oracle Database 10g에는 Flashback 버전 질의 기능이 있어 이러한 작업을 쉽고 편리하게 수행할 수 있습니다.
테이블 변경 내용 질의
이 예에서는 은행의 외환 관리 애플리케이션을 사용했습니다. 데이타베이스에는 특정 시간의 환율을 기록하는 RATES라는 테이블이 있습니다.
SQL> desc rates
 Name              Null?    Type
 ----------------- -------- ------------
 CURRENCY                   VARCHAR2(4)
 RATE                       NUMBER(15,10)
이 테이블에는 CURRENCY 열에 표시된 기타 여러 통화에 대한 US 달러의 환율이 표시됩니다. 재무 서비스 업계에서는 환율을 단순히 변경될 때마다 업데이트하는 대신 지속적인 기록으로 관리합니다. 이 접근방법은 은행 거래가 “이미 지난 시간”에 이루어져 송금으로 인한 시간상의 손실을 수용할 수 있기 때문에 채택되는 것입니다. 예를 들어, 오전 10:12에 이뤄지지만 오전 9:12에 발효된 거래는 현재 시간이 아닌 오전 9:12의 환율이 적용됩니다.
지금까지는 환율 기록 테이블을 생성해 환율 변경 내용을 저장한 후 해당 테이블에 기록이 있는지 질의할 수 밖에 없었습니다. 이 밖에도 RATES 테이블 자체에 특정 환율이 적용되는 시작 시간과 종료 시간을 기록하는 방법이 있었습니다. 변경 내용이 발생하면 기존 행의 END_TIME 열이 SYSDATE로 업데이트되며 END_TIME이 NULL인 새 환율과 함께 행이 새로 삽입되는 것입니다.
하지만 Oracle Database 10g에는 Flashback 버전 질의 기능이 있어 기록 테이블을 유지하거나 시작 및 종료 시간을 저장할 필요가 없습니다. 대신 이 기능을 사용하면 추가로 설정하지 않고도 과거의 특정 시간에 해당하는 행의 값을 가져올 수 있습니다.
예를 들어, 일상 업무를 수행하는 DBA가 환율을 여러 번 업데이트하거나, 때로는 행을 삭제하고 다시 삽입하기도 한다고 가정합니다.
insert into rates values ('EURO',1.1012);
commit;
update rates set rate = 1.1014;
commit;
update rates set rate = 1.1013;
commit;
delete rates;
commit;
insert into rates values ('EURO',1.1016);
commit;
update rates set rate = 1.1011;
commit;
이러한 일련의 작업이 끝나면 DBA는 다음과 같은 RATE 열의 현재 커밋된 값을 얻게 됩니다.
SQL> select * from rates;

CURR       RATE
---- ----------
EURO     1.1011
이 결과에는 행을 처음 생성했을 때부터 발생한 모든 변경 내용이 아닌 RATE의 현재 값이 표시됩니다. 따라서 Flashback 질의를 사용하면 해당 시점의 값을 검색할 수 있지만, 여기서 보다 핵심적인 의도는 단순히 특정 시점에 얻은 일련의 스냅샷이 아니라 캠코더를 통해 변경 내용을 기록하는 것 같이 변경 내용의 감사추적(Audit Trail)을 구축하려는 것입니다.
다음 질의에는 테이블의 변경 내용이 나와 있습니다.
select versions_starttime, versions_endtime, versions_xid, 
versions_operation, rate 
from rates versions between timestamp minvalue and maxvalue
order by VERSIONS_STARTTIME
/

VERSIONS_STARTTIME     VERSIONS_ENDTIME       VERSIONS_XID     V       RATE
---------------------- ---------------------- ---------------- - ----------
01-DEC-03 03.57.12 PM  01-DEC-03 03.57.30 PM  0002002800000C61 I     1.1012
01-DEC-03 03.57.30 PM  01-DEC-03 03.57.39 PM  000A000A00000029 U     1.1014
01-DEC-03 03.57.39 PM  01-DEC-03 03.57.55 PM  000A000B00000029 U     1.1013
01-DEC-03 03.57.55 PM                         000A000C00000029 D     1.1013
01-DEC-03 03.58.07 PM  01-DEC-03 03.58.17 PM  000A000D00000029 I     1.1016
01-DEC-03 03.58.17 PM                         000A000E00000029 U     1.1011
행을 삭제하고 다시 삽입했더라도 여기에 행의 모든 변경 내용이 표시됩니다. VERSION_OPERATION 열에는 행에서 수행한 작업(삽입/업데이트/삭제)이 나타나며, 이를 위한 모든 과정이 기록 테이블이나 추가 열 없이 이뤄집니다.
위의 질의에서 versions_starttime, versions_endtime, versions_xid, versions_operation 열은 ROWNUM, LEVEL 같이 친숙한 열과 유사한 의사(Pseduo) 열입니다. VERSIONS_STARTSCN 및 VERSIONS_ENDSCN 같은 다른 의사 열에는 해당 시간의 시스템 변경 번호(SCN)가 표시됩니다. 또한 versions_xid 열에는 행을 변경한 트랜잭션 식별자가 표시됩니다. 트랜잭션의 상세 내역은 FLASHBACK_TRANSACTION_QUERY 뷰에 나와 있으며, 여기서 XID 열에는 트랜잭션 ID가 표시됩니다. 예를 들어, 위에서 VERSIONS_XID 값인 000A000D00000029를 사용하면 UNDO_SQL 값에 실제 문이 표시됩니다.
SELECT UNDO_SQL
FROM FLASHBACK_TRANSACTION_QUERY
WHERE XID = '000A000D00000029';

UNDO_SQL
----------------------------------------------------------------------------
insert into "ANANDA"."RATES"("CURRENCY","RATE") values ('EURO','1.1013');
이 뷰에는 실제 문 외에도 커밋의 타임 스탬프와 SCN을 비롯해 질의 시작 시의 SCN과 타임 스탬프가 표시됩니다.
기간 내 변경 내용 확인
이제 이러한 정보를 효과적으로 사용하는 방법에 대해 알아보도록 하겠습니다. 오후 3:57:54의 RATE 열 값을 확인해야 한다고 가정하면 다음과 같이 실행할 수 있습니다.
select rate, versions_starttime, versions_endtime
from rates versions
between timestamp 
to_date('12/1/2003 15:57:54','mm/dd/yyyy hh24:mi:ss')
and to_date('12/1/2003 16:57:55','mm/dd/yyyy hh24:mi:ss')
/

      RATE VERSIONS_STARTTIME     VERSIONS_ENDTIME
---------- ---------------------- ----------------------
    1.1011
이 질의는 Flashback 질의와 유사합니다. 위의 예에서 시작 및 종료 시간은 NULL로 해당 시간 간격 동안 환율이 변하지 않았으며 시간 간격을 포함하고 있음을 나타냅니다. 또한 SCN을 사용하면 이전 버전 값을 확인할 수 있으며, SCN 번호는 의사 열인 VERSIONS_STARTSCN 및 VERSIONS_ENDSCN에서 가져옵니다. 다음은 이에 대한 예입니다.
select rate, versions_starttime, versions_endtime
from rates versions
between scn 1000 and 1001
/ 
키워드 MINVALUE 및 MAXVALUE를 사용하면 실행 취소 세그먼트의 모든 변경 내용이 표시됩니다. 특정 날짜 또는 SCN 값을 범위의 끝점 중 하나로 지정하고 다른 끝점을 리터럴 MAXVALUE 또는 MINVALUE로 지정할 수도 있습니다. 예를 들어, 다음은 전체 범위가 아닌 오후 3:57:52부터의 변경 내용을 알려주는 질의입니다.
select versions_starttime, versions_endtime, versions_xid, 
versions_operation, rate 
from rates versions between timestamp 
to_date('12/11/2003 15:57:52', 'mm/dd/yyyy hh24:mi:ss')
and maxvalue
order by VERSIONS_STARTTIME
/

VERSIONS_STARTTIME     VERSIONS_ENDTIME       VERSIONS_XID     V       RATE
---------------------- ---------------------- ---------------- - ----------
01-DEC-03 03.57.55 PM                         000A000C00000029 D     1.1013
01-DEC-03 03.58.07 PM  01-DEC-03 03.58.17 PM  000A000D00000029 I     1.1016
01-DEC-03 03.58.17 PM                         000A000E00000029 U     1.1011
최종 분석
Flashback 버전 질의는 틀을 벗어나 테이블 변경 내용의 짧은 휘발성 값(value) 감사(Audit)를 복제합니다. 이러한 이점을 통해 DBA는 과거의 특정 값이 아니라 일정 기간의 모든 변경 내용을 가져오므로 실행 취소 세그먼트(Undo Segment)의 데이타를 최대한 활용할 수 있습니다. 따라서 최대한으로 사용할 수 있는 버전은 UNDO_RETENTION 매개변수에 달려있다고 하겠습니다.
Flashback 버전 질의에 대한 자세한 내용은 Oracle Database Concepts 10g Release 1 (10.1) 설명서의 관련 섹션을 참조하십시오.
 
 
두번째.
얼마나 더 걸리나요?: 롤백 모니터링
롤백 작업 시간의 정확한 예측
아직 멀었나요? 얼마나 더 걸리죠?
귀에 익은 말들입니까? 이런 질문은 아이들이 좋아하는 테마 공원에 가는 도중 갈수록 횟수를 더하며 끊임없이 뒷좌석에서 들려올 수 있습니다. 이럴 때, 앞으로 정확히 얼마나 더 걸릴지 말해주거나 적어도 그 답을 혼자만이라도 알고 있다면 낫지 않겠습니까? 긴 실행 트랜잭션을 롤백했을 때도 이와 마찬가지로 여러 사용자들이 바짝 따라다니며 같은 질문을 하는 경우가 많습니다. 롤백이 진행되면서 트랜잭션에 잠금이 발생하고 일반적인 처리 성능이 저하되므로 이러한 질문을 하는 것은 당연합니다. Oracle 9i Database 이전 버전에서는 다음 질의를 실행하면,
SELECT USED_UREC
FROM V$TRANSACTION;
현재 트랜잭션에서 사용하는 실행 취소 레코드 수를 반환하며, 반복해서 실행하면 롤백 프로세스가 진행되면서 실행 취소 레코드가 해제되므로 계속해서 줄어든 값이 표시됩니다. 그런 다음 일정 간격의 스냅샷을 얻어 비율을 계산한 후 결과를 추정하여 종료 시간을 예측할 수 있습니다. V$TRANSACTION 뷰에 START_TIME이라는 열이 있지만, 이 열에는 전체 트랜잭션의 시작 시간만 표시됩니다(즉, 롤백 실행 전). 따라서 실제로 롤백을 실행한 시간을 알 수 있는 방법은 추정뿐입니다. 트랜잭션 롤백을 위한 통계 확장 Oracle Database 10g에서는 이 과제를 간단히 수행할 수 있습니다. 트랜잭션을 롤백하면 이벤트가 V$SESSION_LONGOPS 뷰에 기록되어 긴 실행 트랜잭션을 표시합니다. 롤백을 위한 프로세스가 6초 이상 걸리는 경우 레코드가 뷰에 나타납니다. 롤백의 실행이 끝나면 모니터 스크린을 가리고도 다음 질의를 실행할 수 있을 것입니다.
select time_remaining
from v$session_longops
where sid = <sid of the session doing the rollback>;
이제 이 V$SESSION_LONGOPS 뷰가 얼마나 중요한지 알았으니 이 뷰의 다른 기능들에 대해 살펴보도록 하겠습니다. Oracle Database 10g 이전 버전에도 이 뷰가 있지만 롤백 트랜잭션 정보는 캡처되지 않았습니다. 모든 열을 읽기 쉬운 방식으로 표시하기 위해 Tom Kyte가 AskTom.com에 설명한 PRINT_TABLE 함수를 사용하겠습니다. 이 프로시저는 열을 일반적인 행이 아닌 테이블로만 표시합니다.
SQL> set serveroutput on size 999999
SQL> exec print_table('select * from v$session_longops where sid = 9')
SID                           : 9
SERIAL#                       : 68
OPNAME                        : Transaction Rollback
TARGET                        :
TARGET_DESC                   : xid:0x000e.01c.00000067
SOFAR                         : 10234
TOTALWORK                     : 20554
UNITS                         : Blocks
START_TIME                    : 07-dec-2003 21:20:07
LAST_UPDATE_TIME              : 07-dec-2003 21:21:24
TIME_REMAINING                : 77
ELAPSED_SECONDS               : 77
CONTEXT                       : 0
MESSAGE                       : Transaction Rollback: xid:0x000e.01c.00000067 :
                                 10234 out of 20554 Blocks done
USERNAME                      : SYS
SQL_ADDRESS                   : 00000003B719ED08
SQL_HASH_VALUE                : 1430203031
SQL_ID                        : 306w9c5amyanr
QCSID                         : 0
이제 이 열들을 하나하나 자세히 살펴보도록 하겠습니다. 뷰에는 이전 세션의 모든 긴 실행 작업 기록이 포함되어 있으므로 세션에는 긴 실행 작업이 하나 이상일 수 있습니다. OPNAME 열에는 이 레코드가 “트랜잭션 롤백”용이라고 명시되어 있어 올바른 작업 방향으로 이끌어줍니다. TIME_REMAINING 열은 이전에 기술한 예측 잔여 시간을 초 단위로 표시하며, ELAPSED_SECONDS 열에는 현재까지 사용된 시간이 표시됩니다. 그렇다면 이 테이블에 어떻게 예측 잔여 시간을 나타낼까요? 단서는 총 수행 작업량을 나타내는 TOTALWORK 열과 현재까지 수행한 작업량을 나타내는 SOFAR 열에 있습니다. 작업 단위는 UNITS 열에 나와 있습니다. 여기서는 블록에 들어 있으므로 현재까지 20,554개의 블록 중 총 10,234개를 롤백한 것입니다. 또한 현재까지의 작업 소요 시간은 77초이므로 잔여 블록의 롤백 시간은 다음과 같이 구합니다. 77 * ( 10234 / (20554-10234) ) ≈ 77 seconds 반드시 이러한 경로를 통해서만 수치를 얻어야 하는 것은 아니지만 가장 명확한 방법입니다. 마지막으로 LAST_UPDATE_TIME 열에는 뷰 내용이 통용되는 시간이 표시되어 결과를 보다 쉽게 해석할 수 있도록 해줍니다. SQL 문 또 하나의 중요하면서도 새로운 정보는 롤백 중인 SQL 문의 식별자입니다. 이전에는 롤백 중인 SQL 문을 가져오기 위해 SQL_ADDRESS 및 SQL_HASH_VALUE를 사용했습니다. 새로운 열인 SQL_ID는 아래에서처럼 V$SQL 뷰의 SQL_ID에 해당됩니다.
SELECT SQL_TEXT
FROM V$SQL
WHERE SQL_ID = <value of SQL_ID from V$SESSION_LONGOPS>;
이 질의를 실행하면 롤백된 문을 반환하므로 SQL 문의 주소 및 해시 값과 함께 추가 검사를 제공합니다. 병렬 인스턴스 복구 DML 작업이 병렬이었으면 QCSID 열에 병렬 질의 서버 세션의 SID가 표시됩니다. 인스턴스 복구 및 실패한 트랜잭션의 후속 복구 과정 같은 병렬 롤백을 실행하는 경우 대개 이 정보를 유용하게 사용할 수 있습니다. 예를 들어, 대규모 업데이트를 수행하는 도중 인스턴스가 비정상적으로 종료되었다고 가정합시다. 인스턴스가 나타나면 실패한 트랜잭션이 롤백됩니다. 병렬 복구를 위한 초기화 매개변수 값을 사용할 수 있는 경우 롤백이 정규 트랜잭션 롤백에서 발생하므로 직렬이 아닌 병렬로 이뤄집니다. 그런 다음 롤백 프로세스의 완료 시간을 예측합니다. V$FAST_START_TRANSACTIONS 뷰에는 실패한 트랜잭션을 롤백하기 위해 발생한 트랜잭션이 표시됩니다. 이와 유사한 뷰인 V$FAST_START_SERVERS에는 롤백에 실행되는 병렬 질의 서버의 수가 나와 있습니다. 이전 버전에서는 이러한 두 개의 뷰가 있지만, 트랜잭션 식별자를 나타내는 새로운 XID 열이 추가되어 조인이 더 수월해졌습니다. Oracle9i Database 이전에서는 세 개 열(USN – 실행 취소 번호, SLT – 실행 취소 세그먼트 내의 슬롯 번호 및 SEQ – 시퀀스 번호)의 뷰에 조인해야 했습니다. 또한 상위 집합은 PARENTUSN, PARENTSLT 및 PARENTSEQ에 표시되었습니다. 하지만 Oracle Database 10g에서는 XID 열의 뷰에만 조인하면 되고 상위 XID는 알기 쉽게 PXID로 표시됩니다. 가장 유용한 정보는 V$FAST_START_TRANSACTIONS 뷰의 RCVSERVERS 열에 나와 있습니다. 병렬 롤백을 진행하면 이 열에 병렬 질의 서버의 수가 나타나며, 이를 통해 다음과 같이 시작된 병렬 질의 프로세스의 수를 확인할 수 있습니다.
select rcvservers from v$fast_start_transactions;
출력에 1로만 나타나면 트랜잭션이 가장 비효율적인 방식인 SMON 프로세스를 통해 직렬로 롤백되고 있는 것입니다. 이 경우 초기화 매개변수 RECOVERY_PARALLELISM을 0과 1을 제외한 값으로 수정한 다음 병렬 롤백 인스턴스를 다시 시작할 수 있습니다. 그런 다음, ALTER SYSTEM SET FAST_START_PARALLEL_ROLLBACK = HIGH를 실행하면 CPU 수의 네 배나 되는 병렬 서버를 생성할 수 있습니다. 위의 질의 출력이 1을 제외한 임의의 값이 표시되면 병렬 롤백이 이뤄지는 것입니다. 이 경우 동일한 뷰(V$FAST_START_TRANSACTIONS)를 질의하여 상위 및 하위 트랜잭션(상위 트랜잭션 ID – PXID 및 하위 트랜잭션 ID – XID)을 가져올 수 있습니다. 또한 XID는 이 뷰와 V$FAST_START_SERVERS를 조인하는 데 사용하여 상세 내역을 추가로 가져올 수 있습니다. 결론 요컨대 Oracle Database 10g에서 긴 실행 트랜잭션을 롤백할 때는 병렬 인스턴스 복구 세션이나 사용자 실행 롤백 문으로 표시한 다음, V$SESSION_LONGOPS 뷰를 살펴보고 향후 소요 시간의 결과를 예측하기만 하면 됩니다. 이제 테마 공원에 도착하는 시간만 예측할 수 있으면 되겠군요!
 
 
 
세번째.
손쉬운 이름 변경: 향상된 테이블스페이스 관리
Sparser인 SYSTEM, 사용자 기본 테이블스페이스 정의 지원, 새로운 SYSAUX 및 이름 바꾸기 등으로 수월해진 테이블스페이스 관리
누구나 SYSTEM 테이블스페이스에 SYS 및 SYSTEM을 제외한 세그먼트를 생성하면서 좌절감에 머리를 쥐어 뜯으며 고민한 경험이 있을 것입니다.
Oracle9i Database 이전 버전에서는 사용자를 생성할 때 DEFAULT TABLESPACE를 명시하지 않으면 기본값이 SYSTEM 테이블스페이스로 설정되었습니다. 사용자가 세그먼트를 생성하는 동안 테이블스페이스를 명시적으로 지정하지 않는 경우, 명시적으로 부여 받은 것이든 시스템 권한 UNLIMITED TABLESPACE를 통한 것이든 사용자가 테이블스페이스 할당량을 갖고 있으면 SYSTEM에 생성되었습니다. Oracle9i에서는 DBA가 명시적인 임시 테이블스페이스 절 없이 생성된 모든 사용자에 대해 기본 임시 테이블스페이스를 지정하도록 하여 이 문제를 어느 정도 해결했습니다.
Oracle Database 10g에서도 이와 유사하게 사용자에게 기본 테이블스페이스를 지정할 수 있습니다. 우선, 데이타베이스를 생성하는 과정에 CREATE DATABASE 명령은 DEFAULT TABLESPACE 절을 포함할 수 있습니다. 데이타베이스의 생성이 끝나면 다음을 실행하여 기본 테이블스페이스를 만들 수 있습니다.
ALTER DATABASE DEFAULT TABLESPACE <tsname>;
DEFAULT TABLESPACE 절 없이 생성된 모든 사용자는 기본값으로 <tsname>을 갖게 됩니다. 기본 테이블스페이스는 이 ALTER 명령을 사용해 언제든 변경하여 다른 위치의 기본값으로 다른 테이블스페이스를 지정할 수 있습니다.
여기서 중요한 점은 일부 사용자에 대해 다른 어떤 항목이 명시적으로 지정되어 있어도 이전 테이블스페이스와 함께 모든 사용자의 기본 테이블스페이스가 <tsname>으로 변경된다는 것입니다. 예를 들어, 사용자 생성 도중 사용자 USER1 및 USER2의 기본 테이블스페이스를 각각 TS1 및 TS2로 명시적으로 지정했다고 가정합니다. 데이타베이스의 현재 기본 테이블스페이스는 TS2지만, 나중에는 데이타베이스의 기본 테이블스페이스가 TS1으로 변경됩니다. USER2의 기본 테이블스페이스를 TS2로 명시적으로 지정했다 하더라도 TS1으로 바뀌게 되므로 이러한 부작용을 염두에 둬야 합니다!
데이타베이스 생성 과정에 기본 테이블스페이스를 지정하지 않으면 기본값이 SYSTEM으로 설정됩니다. 하지만 기존 데이타베이스의 기본 테이블스페이스는 어떻게 알 수 있을까요? 우선, 다음 질의를 실행합니다.
SELECT PROPERTY_VALUE
FROM DATABASE_PROPERTIES
WHERE PROPERTY_NAME = 'DEFAULT_PERMANENT_TABLESPACE';
DATABASE_PROPERTIES 뷰에는 기본 테이블스페이스 외에도 기본 임시 테이블스페이스, 전역 데이타베이스 이름, 시간대 등과 같은 몇 가지 매우 중요한 정보가 표시됩니다.
중요하지 않은 스키마의 기본 테이블스페이스
인텔리전트 에이전트 사용자 DBSNMP 및 데이타 마이닝 사용자 ODM 같은 여러 스키마는 사용자 작업과 직접적인 관련이 없지만 데이타베이스 무결성을 위해 나름대로 중요한 역할을 합니다. 이러한 스키마의 일부는 기본 테이블스페이스가 SYSTEM인데, 이는 해당 특수 테이블스페이스 내에서 객체가 확산되는 또 다른 이유이기도 합니다.
Oracle Database 10g에는 이러한 스키마의 객체를 보유하는 SYSAUX라는 새로운 테이블스페이스가 도입되었습니다. 이 테이블스페이스는 데이타베이스 생성 도중 자동으로 생성되며 지역적으로 관리됩니다. 또한 데이타 파일 이름만 유일하게 변경할 수 있습니다.
이 접근방법은 SYSTEM이 손상되어 전체 데이타베이스를 복구해야 할 때 복구를 지원합니다. 데이타베이스는 계속 실행하면서 SYSAUX의 객체를 일반 사용자 객체로 복구할 수 있습니다.
하지만 SYSAUX에 있는 이들 스키마의 일부를 다른 테이블스페이스로 옮겨야 한다면 어떻게 할까요? 크기가 늘어나 결국에는 테이블스페이스를 꽉 채우는 일이 빈번한 LogMiner에 사용되는 객체를 예로 들어봅시다. 관리 효율을 높이기 위해 이를 저마다의 테이블스페이스로 옮기는 방법을 고려할 수도 있을 것입니다. 하지만 이것이 최선의 방법일까요?
DBA라면 이러한 특수 객체를 옮기기 위한 올바른 프로시저를 알고 있어야 합니다. 다행히도 Oracle Database 10g에는 이러한 추측 작업을 수행하는 새로운 뷰가 있습니다. 이 V$SYSAUX_OCCUPANTS 뷰에는 SYSAUX 테이블스페이스에 있는 스키마의 이름, 설명, 현재 사용 공간 그리고 이동 방법이 나와 있습니다 (See 표 1 참조).
여기서 LogMiner가 어떻게 분명히 7,488KB를 차지하고 있는 것으로 표시되는지에 주목합니다. LogMiner는 SYSTEM 스키마에 속해 있으며 객체를 이동하려면 패키지 프로시저 SYS.DBMS_LOGMNR_D.SET_TABLESPACE를 실행합니다. 하지만 STATSPACK 객체의 경우 뷰에 엑스포트/임포트 접근방법을 사용하는 것이 좋으며 Streams에는 이동 프로시저가 없으므로 SYSAUX 테이블스페이스에서 이를 쉽게 옮길 수 없습니다. MOVE_PROCEDURE 열에는 SYSAUX에 기본적으로 상주하는 거의 모든 툴에 대한 올바른 이동 프로시저가 표시됩니다. 이동 프로시저는 역방향으로도 사용하여 객체를 다시 SYSAUX 테이블스페이스로 가져올 수 있습니다.
테이블스페이스 이름 바꾸기
데이타 웨어하우스 환경에서는 일반적으로 데이타 마트 아키텍처가 데이타베이스 사이에서 테이블스페이스를 이동합니다. 하지만 원본 및 대상 데이타베이스는 테이블스페이스 이름이 서로 달라야 합니다. 이름이 같은 테이블스페이스가 두 개이면 대상 테이블스페이스의 세그먼트를 다른 테이블스페이스로 옮기고 테이블스페이스를 다시 생성해야 하는데 말처럼 쉽지가 않습니다.
Oracle Database 10g에는 편리한 솔루션이 있어 영구 또는 임시 여부에 관계 없이 기존 테이블스페이스(SYSTEM 및 SYSAUX 제외)의 이름을 다음 명령을 사용해 간단히 변경할 수 있습니다.
ALTER TABLESPACE <oldname> RENAME TO <newname>;
이 기능은 아카이브 프로세스에도 유용하게 사용할 수 있습니다. 매출 기록을 관리하기 위해 범위로 분할된 테이블이 있으며, 매월의 파티션은 해당 월의 이름을 따 명명된 테이블스페이스에 있습니다. 예를 들어, 1월의 파티션에는 JAN이라는 이름이 지정되며 JAN으로 명명된 테이블스페이스에 상주합니다. 보존 정책 기간은 12개월입니다. 따라서 2004년 1월에 2003년 1월의 데이타를 아카이브할 수 있게 되는 것입니다. 대략적인 작업 과정은 다음과 유사한 형태가 됩니다.
  1. ALTER TABLE EXCHANGE PARTITION을 사용해 파티션 JAN에서 독립형 테이블 JAN03을 생성합니다.
  2. 테이블스페이스 이름을 JAN03으로 변경합니다.
  3. 테이블스페이스 JAN03에 설정된 이동 가능한 테이블스페이스를 생성합니다.
  4. 테이블스페이스 JAN03의 이름을 다시 JAN으로 변경합니다.
  5. 비어 있는 파티션을 다시 테이블로 교환합니다.
1, 2, 4 및 5단계는 순조롭게 진행되며 리두 및 실행 취소 공간 같은 리소스를 과도하게 소모하지 않습니다. 3단계는 단순히 파일을 복사하고 JAN03의 데이타 딕셔너리만 엑스포트하면 되므로 마찬가지로 매우 간단한 프로세스입니다. 이전에 아카이브한 파티션을 다시 유효화해야 하는 경우, 프로시저는 동일한 프로세스를 반대로 수행하는 것만큼 간단합니다.
Oracle Database 10g는 이러한 이름 바꾸기를 처리하는 방식에 있어 상당히 지능적입니다. UNDO로 사용되는 테이블스페이스 또는 기본 임시 테이블스페이스의 이름을 변경하는 경우 혼동이 발생할 수 있습니다. 하지만 데이타베이스가 필요한 레코드를 자동으로 조정하여 변경 내용을 반영합니다. 예를 들어, 기본 테이블스페이스 이름을 USERS에서 USER_DATA로 변경하면 DATABASE_PROPERTIES가 자동으로 변경됩니다. 변경에 앞서 다음 질의가
select property_value from database_properties
where property_name = 'DEFAULT_PERMANENT_TABLESPACE';
USERS를 반환합니다. 다음 문을 실행하고 나면
alter tablespace users rename to user_data;
USER_DATA에 대한 모든 참조가 USER_DATA로 변경되었므로 위의 질의가 USER_DATA를 반환합니다.
기본 임시 테이블스페이스를 변경하는 방법도 이와 동일합니다. UNDO 테이블스페이스 이름을 변경하더라도 다음과 같이 SPFILE에 변경을 트리거합니다.
SQL> select value from v$spparameter where name = 'undo_tablespace';

VALUE
--------
UNDOTBS1

SQL> alter tablespace undotbs1 rename to undotbs;

Tablespace altered.

SQL> select value from v$spparameter where name = 'undo_tablespace';

VALUE
--------
UNDOTBS
결론 
객체 처리 기능은 최근의 여러 Oracle 버전을 거치면서 꾸준히 향상되었습니다. Oracle8i에는 한 테이블스페이스에서 다른 테이블스페이스로의 테이블 이동이 도입되었으며, Oracle 9i Database R2는 열 이름 변경 기능을 갖추게 되었습니다. 그리고 지금은 테이블스페이스 자체의 이름을 변경할 수 있는 수준에 이르고 있습니다. 또한 이처럼 기능이 향상되면서 데이타 웨어하우스 또는 마트 환경 등에서 DBA의 작업 부담을 크게 덜어주고 있습니다.
 
 
 
네번째.
한층 강화된 엑스포트/임포트: Oracle Data Pump
Oracle Database 10g 유틸리티로 크게 향상된 데이타 이동 기능
지금까지 엑스포트/임포트 툴세트는 열악한 속도에 대한 불만에도 불구하고 최소한의 노력으로 여러 플랫폼에 데이타를 전송하기 위해 사용해 온 유틸리티였습니다. 임포트는 단순히 엑스포트 덤프 파일에서 각 레코드를 읽고 이를 일반적인 INSERT INTO 명령을 사용해 대상 테이블에 삽입하기만 하므로 임포트 프로세스가 느린 것은 그리 놀랄만한 일이 아닙니다.
이제 프로세스 속도가 월등히 향상된 Oracle Database 10g의 보다 새롭고 빠른 엑스포트/임포트 툴킷인 Oracle Data Pump, the newer and faster sibling of the export/import toolkit in Oracle Database 10g를 사용해 보십시오.
Data Pump는 엑스포트/임포트 프로세스의 전체 구성을 나타냅니다. 일반적인 SQL 문을 사용하는 대신 독점 API로 데이타를 현저하게 빠른 속도로 로드 및 언로드합니다. 제가 테스트해본 결과, 직접 모드의 엑스포트보다 성능이 10-15배 향상되었으며, 임포트 프로세스 성능도 5배 이상 증가했습니다. 또한 엑스포트 유틸리티와 달리 프로시저 같은 특정 유형의 객체만 추출할 수 있습니다.
Data Pump Export
이 새로운 유틸리티는 원래의 엑스포트인 exp와 구분하기 위해 expdp라고 합니다. 이 예에서는 Data Pump를 사용해 약 3GB 크기의 대형 테이블인 CASES를 엑스포트합니다. Data Pump는 서버 측에서 파일 조작을 사용하여 파일을 생성하고 읽으므로 디렉토리를 위치로 사용합니다. 여기서는 filesystem /u02/dpdata1을 사용해 덤프 파일을 유지할 예정입니다.
create directory dpdata1 as '/u02/dpdata1';
grant read, write on directory dpdata1 to ananda;
그리고 다음과 같이 데이타를 엑스포트합니다.
expdp ananda/abc123 tables=CASES directory=DPDATA1 
  dumpfile=expCASES.dmp job_name=CASES_EXPORT
이제 이 명령의 각 부분을 분석해 보겠습니다. 사용자 ID/암호 조합, 테이블 및 덤프 파일 매개변수는 말 그대로이므로 설명이 필요 없습니다. 원래의 엑스포트와 달리 파일이 클라이언트가 아닌 서버에 생성됩니다. 위치는 디렉토리 매개변수 값 DPDATA1로 지정되며, 이는 이전에 생성된 /u02/dpdata1을 가리킵니다. 또한 프로세스를 실행하면 서버의 디렉토리 매개변수로 지정된 위치에 로그 파일이 생성됩니다. 이 프로세스에는 기본적으로 DPUMP_DIR로 명명된 디렉토리가 사용되므로 DPDATA1 대신 생성할 수 있습니다.
위의 job_name 매개변수를 보면 원래의 엑스포트에 없는 특별한 항목이 하나 있습니다. 모든 Data Pump 작업은 작업(job)을 통해 이뤄집니다. Data Pump 작업은 DBMS 작업과 달리 주 프로세스를 대신해 데이타를 처리하는 단순한 서버 프로세스입니다. 마스터 제어 프로세스라고 하는 이 주 프로세스는 Advanced Queuing을 통해 이러한 작업 노력을 조정하는데, 이는 마스터 테이블이라고 하는 런타임 시 생성된 특수 테이블을 통해 이뤄집니다. 제시한 예에서 expdp를 실행하면서 사용자 ANANDA의 스키마를 검사하면 job_name 매개변수에 해당되는 CASES_EXPORT 테이블이 있음을 알 수 있습니다. expdp가 종료되면 이 테이블은 삭제됩니다.
엑스포트 모니터링
DPE(Data Pump Export)를 실행하면서 Control-C를 누르면 화면상에 메시지 표시를 중지하지만 프로세스 자체를 엑스포트하지는 않습니다. 대신 다음과 같이 DPE 프롬프트를 표시합니다. 이제 프로세스는 소위 “대화식” 모드에 들어갑니다.
Export>
이 접근방법에서는 DPE 작업에 여러 명령을 입력할 수 있습니다. 요약을 확인하려면 다음과 같이 프롬프트에 STATUS 명령을 사용합니다.
Export> status
Job: CASES_EXPORT
  Operation: EXPORT                         
  Mode: TABLE                          
  State: EXECUTING                      
  Degree: 1
  Job Error Count: 0
  Dump file:  /u02/dpdata1/expCASES.dmp 
      bytes written =  2048

Worker 1 Status:
  State: EXECUTING                      
  Object Schema: DWOWNER
  Object Name: CASES
  Object Type: TABLE_EXPORT/TBL_TABLE_DATA/TABLE/TABLE_DATA
  Completed Objects: 1
  Total Objects: 1
  Completed Rows: 4687818
하지만 이것은 상태 표시일 뿐이며 엑스포트는 백그라운드에서 실행되고 있습니다. 화면의 메시지를 계속 확인하려면 Export> 프롬프트에서 CONTINUE_CLIENT 명령을 사용합니다.
병렬 작업
PARALLEL 매개변수를 통해 엑스포트시 하나 이상의 스레드를 사용하면 작업 속도를 크게 개선할 수 있습니다. 스레드마다 개별 덤프 파일을 생성하므로 매개변수 dumpfile은 병렬화 만큼 많은 여러 항목을 갖게 됩니다. 또한 하나씩 명시적으로 입력하는 대신 다음과 같이 대체 문자를 파일 이름으로 지정할 수 있습니다.
expdp ananda/abc123 tables=CASES directory=DPDATA1 
  dumpfile=expCASES_%U.dmp parallel=4 job_name=Cases_Export
여기서 dumpfile 매개변수에 어떻게 대체 문자 %U가 생기는지 주목합니다. 이 대체 문자는 파일이 필요에 따라 생성되고 형식은 expCASES_nn.dmp이 됨을 나타내는데, 여기서 nn은 01에서 시작하며 필요에 따라 증가하게 됩니다.
병렬 모드에서는 상태 화면에 네 개의 작업자 프로세스가 표시됩니다. (기본 모드에서는 프로세스가 한 개만 표시됩니다.) 모든 작업자 프로세스가 데이타를 동시에 추출하며 진행률을 상태 화면에 표시합니다.
데이타베이스 파일 및 덤프 파일 디렉토리 파일 시스템에 액세스하려면 I/O 채널을 반드시 구분해야 합니다. 그렇지 않으면 Data Pump 작업의 유지와 관련된 오버헤드가 병렬 스레드의 이점을 뛰어넘어 성능을 저하시킬 수 있습니다. 병렬화는 테이블 수가 병렬 값보다 크고 테이블이 대규모인 경우에만 적용됩니다.
데이타베이스 모니터링
데이타베이스 뷰에서 실행되는 Data Pump 작업에 관해서도 자세한 정보를 확인할 수 있습니다. 작업을 모니터링하는 기본 뷰는 DBA_DATAPUMP_JOBS로 작업에서 실행되는 작업자 프로세스(DEGREE 열)의 수를 알려줍니다. 그 밖의 중요한 뷰에는 DBA_DATAPUMP_SESSIONS가 있는데, 이전 뷰 및 V$SESSION과 조인하면 주 포그라운드(Foreground) 프로세스 세션의 SID를 확인할 수 있습니다.
select sid, serial#
from v$session s, dba_datapump_sessions d
where s.saddr = d.saddr;
이 명령에는 포그라운드 프로세스의 세션이 표시됩니다. 경고 로그에서는 보다 유용한 정보를 얻을 수 있습니다. 프로세스가 시작되면 MCP 및 작업자 프로세스가 다음과 같이 경고 로그에 나타납니다.
kupprdp: master process DM00 started with pid=23, OS id=20530 to execute - 
  SYS.KUPM$MCP.MAIN('CASES_EXPORT', 'ANANDA');
kupprdp: worker process DW01 started with worker id=1, pid=24, OS id=20532 to execute - SYS.KUPW$WORKER.MAIN('CASES_EXPORT', 'ANANDA');
kupprdp: worker process DW03 started with worker id=2, pid=25, OS id=20534 to execute - SYS.KUPW$WORKER.MAIN('CASES_EXPORT', 'ANANDA');
경고 로그에는 Data Pump 작업을 위해 시작된 세션의 PID가 표시됩니다. 실제 SID는 이 질의를 사용해 확인합니다.
select sid, program from v$session where paddr in 
 (select addr from v$process where pid in (23,24,25));
PROGRAM 열에는 경고 로그 파일의 이름에 해당되는 프로세스 DM(마스터 프로세스) 또는 DW(작업자 프로세스)가 표시됩니다. SID 23 같은 작업자 프로세스에서 병렬 질의를 사용하는 경우, V$PX_SESSION 뷰에서 확인할 수 있습니다. 이 뷰에는 SID 23으로 표시된 작업자 프로세스에서 실행되는 모든 병렬 질의 세션이 나타납니다.
select sid from v$px_session where qcsid = 23;
V$SESSION_LONGOPS 뷰에서는 작업 완료에 걸리는 시간을 예측하는 또 다른 유용한 정보를 얻을 수 있습니다.
select sid, serial#, sofar, totalwork
from v$session_longops
where opname = 'CASES_EXPORT'
and sofar != totalwork;
totalwork 열에는 총 작업량이 표시되는데, 이 중 현재까지 sofar 작업량을 완료했으므로 이를 통해 얼마나 더 시간이 걸릴지 예측할 수 있습니다.
Data Pump Import
하지만 Data Pump에서 가장 눈에 잘 띄는 부분은 데이타 임포트 성능입니다. 이전에 엑스포트된 데이타를 임포트하려면 다음을 사용합니다.
impdp ananda/abc123 directory=dpdata1 dumpfile=expCASES.dmp job_name=cases_import
임포트 프로세스의 기본 작업 방식은 테이블 및 연관된 모든 객체를 생성하고 테이블이 있는 상태에서 오류를 만들어 내는 것입니다. 기존 테이블에 데이타를 추가해야 하는 경우 위의 명령행에 TABLE_EXISTS_ACTION=APPEND를 사용할 수 있습니다.
DPE와 마찬가지로 프로세스 도중 Control-C를 누르면 DPI(Date Pump Import)의 대화식 모드를 표시하며 Import>가 프롬프트됩니다.
특정 객체 작업
한 사용자에서 특정 프로시저만 엑스포트하여 다른 데이타베이스나 사용자에 다시 생성해야 했던 경험이 있습니까? 기존의 엑스포트 유틸리티와 달리 Data Pump는 특정 유형의 객체만 엑스포트할 수 있습니다. 예를 들어, 다음 명령을 실행하면 테이블, 뷰 또는 함수 등은 제외하고 오로지 프로시저만 엑스포트할 수 있습니다.
expdp ananda/iclaim directory=DPDATA1 dumpfile=expprocs.dmp include=PROCEDURE
To export only a few specific objects--say, function FUNC1 and procedure PROC1--you could use
expdp ananda/iclaim directory=DPDATA1 dumpfile=expprocs.dmp 
  include=PROCEDURE:"='PROC1'",FUNCTION:"='FUNC1'"
이 덤프 파일은 소스의 백업으로 사용됩니다. 때로는 이를 사용해 DDL 스크립트를 생성하여 나중에 사용할 수도 있습니다. DDL 스크립트 파일을 생성하려면 SQLFILE이라고 하는 특수 매개변수를 사용합니다.
impdp ananda/iclaim directory=DPDATA1 dumpfile=expprocs.dmp sqlfile=procs.sql
이 명령은 DPDATA1로 지정된 디렉토리에 procs.sql로 명명된 파일을 생성하며 엑스포트 덤프 파일 내의 객체 스크립트가 들어 있습니다. 이 접근방법을 사용하면 다른 스키마에 원본을 보다 신속하게 생성할 수 있습니다.
INCLUDE 매개변수를 사용하면 객체가 덤프 파일에서 포함 또는 제외되도록 정의할 수 있습니다. 예를 들어, INCLUDE=TABLE:"LIKE 'TAB%'" 절을 사용하면 이름이 TAB로 시작하는 테이블만 엑스포트할 수 있습니다. 마찬가지로 INCLUDE=TABLE:"NOT LIKE 'TAB%'" 구문을 사용하면 TAB으로 시작하는 모든 테이블을 제외시킬 수 있습니다. 아니면 EXCLUDE 매개변수를 사용해 특정 객체를 제외시킬 수 있습니다.
Data Pump를 사용하면 외부 테이블로 테이블스페이스를 이동할 수도 있는데, 이렇게 하면 진행 중인 병렬화를 다시 정의하고 기존 프로세스에 테이블을 추가하는 등의 작업에 매우 효과적입니다(이는 본 문서의 범위를 벗어난 내용이므로 자세한 내용은 Oracle Database Utilities 10g Release 1 10.1을 참조하십시오). 다음 명령을 실행하면 Data Pump 엑스포트 유틸리티에서 사용 가능한 매개변수 목록이 생성됩니다.
expdp help=y
마찬가지로 impdp help=y 명령을 실행하면 DPI의 모든 매개변수가 표시됩니다.
Data Pump 작업을 실행하는 동안 DPE 또는 DPI 프롬프트에 STOP_JOB을 실행하여 작업을 일시 중지한 다음 START_JOB으로 다시 시작할 수 있습니다. 이 기능은 공간이 부족하여 계속하기 전에 정정해야 하는 경우 유용하게 사용할 수 있습니다.
자세한 내용은 Oracle Database Utilities 10g Release 1 10.1 설명서 1부를 참조하십시오.
 
 
 
다섯번째.
Flashback 테이블
실수로 삭제한 테이블을 손쉽게 다시 유효화할 수 있는 Oracle Database 10g의 Flashback 테이블 기능
매우 중요한 테이블을 실수로 삭제하여 즉시 복구해야 하는 상황은 생각보다 자주 일어나는 시나리오입니다. (때로는 이처럼 불운한 사용자가 DBA일 수도 있습니다!)
Oracle9i Database에는 Flashback 질의 옵션 개념이 도입되어 데이타를 과거의 시점에서부터 검색하지만, 테이블 삭제 같은 DDL 작업을 순간적으로 되돌릴 수는 없습니다. 이 경우 유일한 수단은 다른 데이타베이스에서 테이블스페이스 적시 복구를 사용한 다음, 엑스포트/임포트 또는 기타 메서드를 사용해 현재 데이타베이스에 테이블을 다시 생성하는 것입니다. 이 프로시저를 수행하려면 복제를 위해 다른 데이타베이스를 사용하는 것은 물론, DBA의 많은 노력과 귀중한 시간이 요구됩니다.
하지만 Oracle Database 10g의 Flashback 테이블 기능으로 들어가면 몇 개의 문만 실행하여 삭제된 테이블을 간단히 검색할 수 있습니다. 그럼, 지금부터 이 기능의 작동 원리에 대해 알아보도록 하겠습니다.
자유로운 테이블 삭제
먼저, 현재 스키마의 테이블을 확인해 봅시다.
SQL> select * from tab;

TNAME                    TABTYPE  CLUSTERID
------------------------ ------- ----------
RECYCLETEST              TABLE
그런 다음, 아래와 같이 고의로 테이블을 삭제합니다.
SQL> drop table recycletest;

Table dropped.
이제 테이블의 상태를 확인합니다.
SQL> select * from tab;

TNAME                          TABTYPE  CLUSTERID
------------------------------ ------- ----------
BIN$04LhcpndanfgMAAAAAANPw==$0 TABLE
RECYCLETEST 테이블이 사라졌지만 새 테이블인 BIN$04LhcpndanfgMAAAAAANPw==$0이 있다는 점에 주목합니다. 좀 더 자세히 설명하면 삭제된 테이블 RECYCLETEST가 완전히 사라지는 대신 시스템 정의 이름으로 이름이 변경된 것입니다. 이 테이블은 여전히 동일한 테이블스페이스에 있으며 원래 테이블과 구조도 동일합니다. 테이블에 인덱스 또는 트리거가 정의되어 있는 경우, 마찬가지로 테이블과 동일한 명명 규칙을 사용하여 이름이 변경됩니다. 프로시저 같은 종속적인 소스는 무효화되지만, 대신 원래 테이블의 트리거 및 인덱스가 이름이 변경된 테이블인 BIN$04LhcpndanfgMAAAAAANPw==$0에 들어가 삭제된 테이블의 완전한 객체 구조를 보존합니다.
테이블 및 연관된 객체는 PC에 있는 것과 유사한 “휴지통(RecycleBin)”이라고 하는 논리적 컨테이너에 들어갑니다. 하지만 이들 객체가 이전에 있던 테이블스페이스에서 옮겨지는 것은 아니며 계속 해당 테이블스페이스에서 공간을 차지하고 있습니다. 휴지통은 단순히 삭제된 객체의 목록을 만드는 논리적 구조입니다. 휴지통의 컨텐트를 확인하려면 SQL*Plus 프롬프트에서 다음 명령을 사용합니다(SQL*Plus 10.1이 있어야 함).
SQL> show recyclebin

ORIGINAL NAME    RECYCLEBIN NAME                OBJECT TYPE  DROP TIME
---------------- ------------------------------ ------------ ------------------
RECYCLETEST      BIN$04LhcpndanfgMAAAAAANPw==$0 TABLE        2004-02-16:21:13:31
이렇게 하면 테이블의 원래 이름인 RECYCLETEST는 물론, 삭제된 후 생성된 새 테이블 이름과 동일한 휴지통에서의 새 이름이 표시됩니다. (참고: 정확한 이름은 플랫폼별로 다를 수 있습니다.) 테이블을 다시 유효화하기 위해서는 FLASHBACK TABLE 명령만 사용하면 됩니다.
SQL> FLASHBACK TABLE RECYCLETEST TO BEFORE DROP;

FLASHBACK COMPLETE.

SQL> SELECT * FROM TAB;

TNAME                          TABTYPE  CLUSTERID
------------------------------ ------- ----------
RECYCLETEST                    TABLE
, 테이블이 정말 간단히 유효화되지 않습니까? 지금 휴지통을 확인하면 비어 있습니다.
여기서 유의할 점은 테이블을 휴지통에 넣는다고 해도 원래 테이블스페이스의 공간이 제거되는 것은 아니라는 것입니다. 공간을 제거하려면 다음을 사용해 휴지통을 지워야 합니다.
PURGE RECYCLEBIN;
하지만 Flashback 기능을 사용하지 않고 테이블을 완전히 삭제하려면 어떻게 해야 할까요? 이 경우 다음을 사용하면 테이블을 영구적으로 삭제할 수 있습니다.
DROP TABLE RECYCLETEST PURGE;
이 명령을 실행하면 테이블 이름이 휴지통 이름으로 변경되는 것이 아니라, 10g 이전 버전에서처럼 영구적으로 삭제됩니다.
휴지통 관리
이 프로세스에서 테이블을 완전히 삭제하지 않아 테이블스페이스를 해제하지 않은 상태에서 삭제된 객체가 테이블스페이스의 모든 공간을 차지하면 어떤 일이 발생할까요?
답은 간단합니다. 그 같은 상황은 결코 발생하지 않습니다. 데이타 파일에 데이타를 추가할 공간을 확보해야 할 정도로 휴지통 데이타가 테이블스페이스로 꽉 차는 상황이 발생하면 테이블스페이스는 이른바 “공간 압축” 상태에 들어갑니다. 위의 시나리오에서 객체는 선입선출 방식으로 휴지통에서 자동으로 지워지며, 종속된 객체(예: 인덱스)는 테이블보다 먼저 제거됩니다.
마찬가지로 특정 테이블스페이스에 정의된 사용자 할당량에도 공간 압축이 발생할 수 있습니다. 테이블에는 사용 가능한 공간이 충분하지만 사용자는 할당된 공간이 부족할 수 있습니다. 이러한 상황에서 Oracle은 해당 테이블스페이스의 사용자에 속한 객체를 자동으로 지웁니다.
이 외에도 여러 가지 방법으로 휴지통을 수동으로 제어할 수 있습니다. 삭제한 후 휴지통에서 TEST라고 명명된 특정 테이블을 삭제하려면 다음을 실행하거나,
PURGE TABLE TEST;
아래와 같이 해당 휴지통 이름을 사용합니다.
PURGE TABLE "BIN$04LhcpndanfgMAAAAAANPw==$0";
이 명령을 실행하면 휴지통에서 TEST 테이블과 인덱스, 제약 조건 등과 같은 모든 종속 객체가 삭제되어 일정 공간을 확보하게 됩니다. 하지만 휴지통에서 인덱스를 영구적으로 삭제하려면 다음을 사용합니다.
purge index in_test1_01;
이렇게 하면 인덱스만 제거되며 테이블의 복사본은 휴지통에 남아 있습니다.
때로는 상위 레벨에서 지우는 것이 유용할 수도 있습니다. 예를 들어, 테이블스페이스 USERS의 휴지통에 있는 모든 객체를 지워야 한다면 다음을 실행합니다.
PURGE TABLESPACE USERS;
휴지통에서 해당 테이블스페이스의 특정 사용자만 지워야 하는 경우도 있습니다. 이 접근방법은 사용자가 많은 수의 과도 상태 테이블을 생성 및 삭제하는 데이타 웨어하우스 유형의 환경에 유용합니다. 다음과 같이 위의 명령을 수정해 지우기 작업을 특정 사용자만으로 제한할 수 있습니다.
PURGE TABLESPACE USERS USER SCOTT;
사용자 SCOTT는 다음 명령으로 휴지통을 지웁니다.
PURGE RECYCLEBIN;
DBA는 다음을 사용해 테이블스페이스의 모든 객체를 지울 수 있습니다.
PURGE DBA_RECYCLEBIN;
위에서 살펴본 것처럼 휴지통은 사용자의 특정한 요구에 맞는 다양한 방식으로 관리할 수 있습니다.
테이블 버전 및 Flashback
다음과 같이 동일한 테이블을 여러 번 생성 및 삭제해야 하는 경우도 흔히 발생합니다.
CREATE TABLE TEST (COL1 NUMBER);
INSERT INTO TEST VALUES (1);
COMMIT;
DROP TABLE TEST;
CREATE TABLE TEST (COL1 NUMBER);
INSERT INTO TEST VALUES (2);
COMMIT;
DROP TABLE TEST;
CREATE TABLE TEST (COL1 NUMBER);
INSERT INTO TEST VALUES (3);
COMMIT;
DROP TABLE TEST;
여기서 TEST 테이블을 순간적으로 되돌린다면 COL1 열의 값은 어떻게 될까요? 기존의 개념에서 보면 휴지통에서 테이블의 첫 번째 버전이 검색되고 COL1 열의 값은 1이 될 것입니다. 하지만 실제로는 첫 번째가 아닌 테이블의 세 번째 버전이 검색되므로 COL1 열의 값은 1이 아닌 3이 됩니다.
이 때 삭제된 테이블의 다른 버전을 검색할 수도 있습니다. 하지만 TEST 테이블이 존재하는 이러한 작업이 불가능한데, 이 경우 다음 두 가지를 선택할 수 있습니다.
  • 다음과 같이 이름 바꾸기 옵션을 사용합니다.
    FLASHBACK TABLE TEST TO BEFORE DROP RENAME TO TEST2;
    FLASHBACK TABLE TEST TO BEFORE DROP RENAME TO TEST1;
    
    이렇게 하면 테이블의 첫 번째 버전은 TEST1으로, 두 번째 버전은 TEST2로 다시 유효화됩니다. 또한 TEST1 및 TEST2에서 COL1의 값은 각각 1과 2가 됩니다. 또는
  • 복원할 테이블의 특정 휴지통 이름을 사용합니다. 이를 위해 먼저 테이블의 휴지통 이름을 식별한 후 다음을 실행합니다.
    FLASHBACK TABLE "BIN$04LhcpnoanfgMAAAAAANPw==$0" TO BEFORE DROP RENAME TO TEST2;
    FLASHBACK TABLE "BIN$04LhcpnqanfgMAAAAAANPw==$0" TO BEFORE DROP RENAME TO TEST1;
    
    이렇게 하면 삭제된 테이블의 두 가지 버전이 복원됩니다.
주의 사항
삭제 취소 기능을 사용하면 테이블의 이름이 원래대로 돌아가지만 인덱스 및 트리거 같은 연관된 객체는 그렇지 않으며 계속 휴지통 이름으로 남아 있습니다. 또한 뷰 및 프로시저 같이 테이블에 정의된 소스는 재컴파일되지 않으며 무효화된 상태로 남게 됩니다. 이러한 이전 이름들은 수동으로 검색한 다음 순간적으로 되돌린 테이블에 적용해야 합니다.
이 정보는 USER_RECYCLEBIN으로 명명된 뷰에서 관리됩니다. 테이블을 순간적으로 되돌리기 전에 다음 질의를 사용해 이전 이름을 검색합니다.
SELECT OBJECT_NAME, ORIGINAL_NAME, TYPE
FROM USER_RECYCLEBIN
WHERE BASE_OBJECT = (SELECT BASE_OBJECT FROM USER_RECYCLEBIN
WHERE ORIGINAL_NAME = 'RECYCLETEST')
AND ORIGINAL_NAME != 'RECYCLETEST';

OBJECT_NAME                    ORIGINAL_N TYPE
------------------------------ ---------- --------
BIN$04LhcpnianfgMAAAAAANPw==$0 IN_RT_01   INDEX
BIN$04LhcpnganfgMAAAAAANPw==$0 TR_RT      TRIGGER
테이블을 순간적으로 되돌리면 RECYCLETEST 테이블의 인덱스 및 트리거에는 OBJECT_NAME 열에 나타난 이름이 지정됩니다. 위의 질의에서는 원래 이름을 사용해 객체의 이름을 다음과 같이 변경할 수 있습니다.
ALTER INDEX "BIN$04LhcpnianfgMAAAAAANPw==$0" RENAME TO IN_RT_01;
ALTER TRIGGER "BIN$04LhcpnganfgMAAAAAANPw==$0" RENAME TO TR_RT;
한가지 유의해야 할 예외는 비트맵 인덱스입니다. 비트맵 인덱스를 삭제하면 휴지통에 들어가지 않으므로 검색할 수 없습니다. 또한 뷰에서 제약 조건 이름을 검색할 수 없습니다. 따라서 이 인덱스의 이름은 다른 소스에서 변경해야 합니다.
Flashback 테이블의 다른 용도
Flashback Drop Table에는 테이블 삭제 작업을 되돌리는 것 외에도 다른 기능이 있습니다. Flashback 질의와 마찬가지로 이를 사용해 테이블을 다른 시점으로 다시 유효화하여 전체 테이블을 “이전” 버전으로 바꿀 수 있습니다. 예를 들어, 다음 문을 사용하면 테이블을 시스템 변경 번호(SCN) 2202666520으로 다시 유효화합니다.
FLASHBACK TABLE RECYCLETEST TO SCN 2202666520;
이 기능은 Oracle Data Pump 기술로 다른 테이블을 생성하고 Flashback으로 테이블을 해당 SCN의 데이타 버전으로 채운 다음, 원래의 테이블을 새 테이블로 바꿉니다. 테이블을 어느 정도까지 순간적으로 되돌릴 수 있는지 확인하려면 Oracle Database 10g의 버전 관리 기능을 사용합니다. (자세한 내용은 이 시리즈의 1주 부분을 참조하십시오.) 또한 Flashback 절에 SCN 대신 타임 스탬프를 지정할 수도 있습니다.
 
 
 
 
제 6 주
Automatic Workload Repository
AWR을 이용하여 분석과 튜닝을 위한 데이타베이스 성능 통계정보와 메트릭을 수집하고, 데이타베이스에서 사용한 정확한 시간을 확인하거나 세션 정보를 저장할 수 있습니다.
데이타베이스에 성능에 관련한 문제가 생겼을 때, 귀하가 DBA로서 가장 먼저 취하는 조치는 무엇입니까? 아마도 문제에 일정한 패턴이 존재하는지 확인하는 것이 가장 일반적인 접근방법의 하나일 것입니다. “동일한 문제가 반복되는가?”, “특정한 시간대에만 발생하는가?”, 또는 “두 가지 문제에 연관성이 있는가?” 등의 질문을 먼저 제기해 봄으로써 보다 정확한 진단을 수행할 수 있습니다.
Oracle DBA들은 데이타베이스 운영에 관련한 통계정보를 수집하거나 성능 메트릭(metric)을 추출하기 위해 써드 파티 툴, 또는 직접 개발한 툴을 사용하고 있습니다. 이렇게 수집된 정보는, 문제 발생 이전과 이후의 상태를 비교하는 데 이용됩니다. 과거에 발생했던 이벤트들을 재현해 봄으로써 현재 문제를 다양한 관점에서 분석할 수 있습니다. 이처럼 관련 통계정보들을 지속적으로 수집하는 것은 성능 분석에서 매우 중요한 작업 중의 하나입니다.
오라클은 한동안 이를 위해 Statspack이라는 이름의 빌트-인 툴을 제공하기도 했습니다. Statspack은 상황에 따라 매우 유용하긴 했지만, 성능 관련 트러블슈팅 과정에서 요구되는 안정성이 결여되었다는 문제가 있었습니다. Oracle Database 10g는 성능 통계정보의 수집과 관련하여 그 기능이 비약적으로 향상된 Automatic Workload Repository(AWR)을 제공합니다. AWR은 데이타베이스와 함께 설치되며, 기본적인 통계정보뿐 아니라 통계정보로부터 유추된 메트릭(derived metric)도 함께 수집합니다.
간단하게 테스트해 보기
AWR을 이용한 새로운 기능들은 $ORACLE_HOME/rdbms/admin 디렉토리의 awrrpt.sql 스크립트를 실행하고, 수집된 통계정보와 메트릭 정보를 바탕으로 생성된 리포트를 확인함으로써 가장 쉽게 이해할 수 있습니다. awrrpt.sql 스크립트는 Statspack과 유사한 구조로 되어있습니다. 먼저 현재 저장된 AWR 스냅샷을 모두 표시한 후, 시간 간격 설정을 위한 입력값을 요구합니다. 출력은 두 가지 형태로 제공됩니다. 텍스트 포맷 출력은 Statspack 리포트와 유사하지만, AWR 리포지토리를 기반으로 하며 (디폴트로 제공되는) HTML 포맷을 통해 section/subsection으로 구분된 하이퍼링크를 제공하는 등 사용자 편의성을 강화하였다는 점에서 차이를 갖습니다. 먼저 awrrpt.sql 스크립트를 실행하여 리포트를 확인해 보시고, AWR의 기능에 대한.특성을 이해하시기 바랍니다.
구현 원리
이제 AWR의 설계방식과 구조에 대해 알아보기로 합시다. AWR은 수집된 성능관련 통계정보가 저장되며 이를 바탕으로 성능 메트릭을 제공함으로서 잠재적인 문제의 원인 추적을 가능하게 해주는 근간을 제공해 줍니다. Statspack의 경우와 달리, Oracle10g는 AWR을 활용하여 새로운 MMON 백그라운드 프로세스와, 여러 개의 슬레이브 프로세스를 통해 자동적으로 매시간별 스냅샷 정보를 수집합니다. 공간 절약을 위해, 수집된 데이타는 7일 후 자동으로 삭제됩니다. 스냅샷 빈도와 보관 주기는 사용자에 의해 설정 가능합니다. 현재 설정값을 보기 위해서는 아래와 같이 명령을 수행하면 됩니다:
select snap_interval, retention
from dba_hist_wr_control;

SNAP_INTERVAL       RETENTION
------------------- -------------------
+00000 01:00:00.0   +00007 00:00:00.0
위의 실행결과는 스냅샷이 매 시간대 별로 수집되고 있으며 수집된 통계가 7일 동안 보관되고 있음을 보여주고 있습니다. 스냅샷 주기를 20분으로, 보관 주기를 2일로 변경하기 위해서는 아래와 같이 수행하면 됩니다. (매개변수는 분 단위로 표시됩니다.)
begin
   dbms_workload_repository.modify_snapshot_settings (
      interval => 20,
      retention => 2*24*60
   );
end;
AWR은 수집된 통계를 저장하기 위해 여러 개의 테이블을 사용합니다. 이 테이블들은 모두 SYS 스키마의 SYSAUX 테이블스페이스 내에 저장되어 있으며, WRM$_* 또는 WRH$_*의 네임 포맷을 갖습니다. WRM$_* 테이블은 수집 대상 데이타베이스 및 스냅샷에 관련한 메타데이타 정보를, WRH$_* 테이블은 실제 수집된 통계 정보를 저장하는데 사용됩니다. (예측하시는 바와 같이, H는 “historical”, M은 “metadata”의 약자를 의미합니다.) 이 테이블을 기반으로 DBA_HIST_라는 prefix를 갖는 여러 가지 뷰가 제공되고 있으며, 이 뷰들을 응용하여 자신만의 성능 분석 툴을 만들 수도 있습니다. 뷰의 이름은 테이블 이름과 직접적인 연관성을 갖습니다. 예를 들어 DBA_HIST_SYSMETRIC_SUMMARY 뷰는 WRH$_SYSMETRIC_SUMMARY 테이블을 기반으로 합니다.
AWR 히스토리 테이블은 Statspack에서는 수집되지 않았던 다양한 정보(테이블스페이스 사용 통계, 파일시스템 사용 통계, 운영체제 통계 등)를 제공합니다. 테이블의 전체 리스트는 아래와 같이 데이타 딕셔너리 조회를 통해 확인할 수 있습니다:
select view_name from user_views where view_name like 'DBA_HIST_%' escape '';
DBA_HIST_METRIC_NAME 뷰는 AWR에 수집되는 주요 메트릭과 메트릭이 속한 그룹, 그리고 수집 단위(unit) 등을 정의하고 있습니다. DBA_HIST_METRIC_NAME 뷰의 레코드에 대한 조회 결과의 예가 아래와 같습니다:
DBID                  : 4133493568
GROUP_ID              : 2
GROUP_NAME            : System Metrics Long Duration
METRIC_ID             : 2075
METRIC_NAME           : CPU Usage Per Sec
METRIC_UNIT           : CentiSeconds Per Second 
위에서는 "초당 CPU 사용량(CPU Usage Per Sec)" 메트릭이 “100분의 1초(CentiSeconds Per Second)” 단위로 수집되고 있으며, 이 메트릭이 "System Metrics Long Duration” 그룹에 속함을 확인할 수 있습니다. 이 레코드를 DBA_HIST_SYSMETRIC_SUMMARY와 JOIN하여 실제 통계를 확인할 수 있습니다:
select begin_time, intsize, num_interval, minval, maxval, average, standard_deviation sd 
from dba_hist_sysmetric_summary where metric_id = 2075;

BEGIN    INTSIZE NUM_INTERVAL   MINVAL  MAXVAL  AVERAGE           SD
----- ---------- ------------   ------- ------- --------  ----------
11:39     179916           30         0      33        3  9.81553548
11:09     180023           30        21      35       28  5.91543912

... 후략 ...
위 조회 결과를 통해 백 분의 1초 단위로 CPU 자원이 어떻게 소비되고 있는지 확인할 수 있습니다. SD(standard deviation, 표준 편차) 값을 참조하면 계산된 평균 값이 실제 부하와 비교하여 얼마나 오차를 갖는지 분석 가능합니다. 첫 번째 레코드의 경우, 초당 백 분의 3초의 CPU 시간이 소모된 것으로 계산되었지만, 표준 편차가 9.81이나 되므로 계산된 3의 평균값이 실제 부하를 정확하게 반영하지 못하는 것으로 볼 수 있습니다. 반면 28의 평균값과 5.9의 표준 편차를 갖는 두 번째 레코드가 실제 수치에 더 가깝다고 볼 수 있습니다. 이러한 트렌드 분석을 통해 성능 메트릭과 환경 변수의 상관 관계를 보다 명확하게 이해할 수 있습니다.
통계의 활용
지금까지 AWR의 수집 대상이 어떻게 정의되는지 알아보았습니다. 이번에는 수집된 데이타를 어떻게 활용할 수 있는지 설명하기로 합니다.
성능 문제는 독립적으로 존재하는 경우가 거의 없으며, 대개 다른 근본적인 문제를 암시하는 징후로서 해석되는 것이 일반적입니다. 전형적인 튜닝 과정의 예를 짚어보기로 합시다: DBA가 시스템 성능이 저하되었음을 발견하고 wait에 대한 진단을 수행합니다. 진단 결과 “buffer busy wait”이 매우 높게 나타나고 있음을 확인합니다. 그렇다면 문제의 원인은 무엇일까요? 여러 가지 가능성이 존재합니다: 인덱스의 크기가 감당할 수 없을 만큼 커지고 있을 수도 있고, 테이블의 조밀도(density)가 너무 높아 하나의 블록을 메모리에 읽어 오는 데 요구되는 시간이 제한된 때문일 수도 있고, 그 밖의 다른 이유가 있을 수 있습니다. 원인이 무엇이든, 문제가 되는 세그먼트를 먼저 확인해 보는 것이 필요합니다. 문제가 인덱스 세그먼트에서 발생했다면, 리빌드 작업을 수행하거나 reverse key index로 변경하거나 또는 Oracle Database 10g에서 새로 제공하는 hash-partitioned index로 변경해 볼 수 있을 것입니다. 문제가 테이블에서 발생했다면, 저장 관련 매개변수를 변경해서 조밀도를 낮추거나, 자동 세그먼트 공간 관리(automatic segment space management)가 설정된 테이블스페이스로 이동할 수 있을 것입니다.
DBA가 실제로 사용하는 접근법은 일반적인 방법론, DBA의 경험 및 지식 등을 그 바탕으로 합니다. 만일 똑같은 일을 별도의 엔진이, 메트릭을 수집하고 사전 정의된 로직을 바탕으로 적용 가능한 방법을 추론하는 엔진이 대신해 준다면 어떨까요? DBA의 작업이 한층 쉬워지지 않을까요?
바로 이러한 엔진이 Oracle Database 10g에 새로 추가된 Automatic Database Diagnostic Monitor (ADDM)입니다. ADDM은 AWR이 수집한 데이타를 사용하여 결론을 추론합니다. 위의 예의 경우, ADDM은 buffer busy wait이 발생하고 있음을 감지하고, 필요한 데이타를 조회하여 wait이 실제로 발생하는 세그먼트를 확인한 후, 그 구조와 분포를 평가함으로써 최종적으로 해결책을 제시합니다. AWR의 스냅샷 수집이 완료될 때마다, ADDM이 자동으로 호출되어 메트릭을 점검하고 권고사항을 제시합니다. 결국 여러분은 데이타 분석 및 권고사항 제시를 담당하는 풀 타임 DBA 로봇을 하나 두고, 보다 전략적인 업무에 집중할 수 있게 된 셈입니다.
Enterprise Manager 10g 콘솔의 “DB Home” 페이지에서 ADDM의 권고사항과 AWR 리포지토리 데이타를 확인할 수 있습니다. AWR 리포트를 보려면, Administration->Workload Repository->Snapshot의 순서로 메뉴를 따라가야 합니다. ADDM의 자세한 기능은 향후 연재에서 소개하도록 하겠습니다.
특정 조건을 기준으로 알림 메시지를 생성하도록 설정하는 것도 가능합니다. 이 기능은 Server Generated Alert라 불리며, Advanced Queue에 푸시(push) 형태로 저장되고 리스닝 중인 모든 클라이언트에 전달되는 형태로 관리됩니다. Enterprise Manager 10g 역시 Server Generated Alert의 클라이언트의 하나로서 관리됩니다.
타임 모델 (Time Model)
성능 문제가 발생했을 때, 응답시간을 줄이기 위한 방법으로 DBA의 머릿속에 가장 먼저 떠오르는 것은 무엇일까요? 말할 필요도 없이, 문제의 근본원인을 찾아내어 제거하는 것이 최우선일 것입니다. 그렇다면 얼마나 많은 시간이 (대기가 아닌) 실제 작업에 사용되었는지 어떻게 확인할 수 있을까요? Oracle Database 10g는 여러 가지 자원에 관련한 실제 사용 시간을 확인하기 위한 타임 모델(time model)을 구현하고 있습니다. 전체 시스템 관련 소요 시간 통계는 V$SYS_TIME_MODEL 뷰에 저장됩니다. V$SYS_TIME_MODEL 뷰에 대한 쿼리 결과의 예가 아래와 같습니다.
STAT_NAME                                     VALUE
-------------------------------------         --------------
DB time                                       58211645
DB CPU                                        54500000
background cpu time                           254490000
sequence load elapsed time                    0
parse time elapsed                            1867816
hard parse elapsed time                       1758922
sql execute elapsed time                      57632352
connection management call elapsed time       288819
failed parse elapsed time                     50794
hard parse (sharing criteria) elapsed time    220345
hard parse (bind mismatch) elapsed time       5040
PL/SQL execution elapsed time                 197792
inbound PL/SQL rpc elapsed time               0
PL/SQL compilation elapsed time               593992
Java execution elapsed time                   0
bind/define call elapsed time                 0 
위에서 DB time이라는 통계정보는 인스턴스 시작 이후 데이타베이스가 사용한 시간의 누적치를 의미합니다. 샘플 작업을 실행한 다음 다시 뷰를 조회했을 때 표시되는 DB time의 값과 이전 값의 차이가 바로 해당 작업을 위해 데이타베이스가 사용한 시간이 됩니다. 튜닝을 거친 후 DB time 값의 차이를 다시 분석하면 튜닝을 통해 얻어진 성능 효과를 확인할 수 있습니다.
이와 별도로 V$SYS_TIME_MODEL 뷰를 통해 파싱(parsing) 작업 또는 PL/SQL 컴파일 작업에 소요된 시간 등을 확인할 수 있습니다. 이 뷰를 이용하면 시스템이 사용한 시간을 확인하는 것도 가능합니다. 시스템 / 데이타베이스 레벨이 아닌 세션 레벨의 통계를 원한다면 V$SESS_TIME_MODEL 뷰를 이용할 수 있습니다. V$SESS_TIME_MODEL 뷰는 현재 연결 중인 active/inactive 세션들의 통계를 제공합니다. 세션의 SID 값을 지정해서 개별 세션의 통계를 확인할 수 있습니다.
이전 릴리즈에서는 이러한 통계가 제공되지 않았으며, 사용자들은 여러 정보 소스를 참고해서 근사치를 추측할 수 밖에 없었습니다.
Active Session History
Oracle Database 10gV$SESSION에도 개선이 이루어졌습니다. 가장 중요한 변화로 wait 이벤트와 그 지속시간에 대한 통계가 뷰에 추가되어, V$SESSION_WAIT를 별도로 참조할 필요가 없게 되었다는 점을 들 수 있습니다. 하지만 이 뷰가 실시간 정보를 제공하므로, 나중에 다시 조회했을 때에는 중요한 정보가 이미 사라져 버리고 없을 수 있습니다. 예를 들어 wait 상태에 있는 세션이 있음을 확인하고 이를 조회하려 하면, 이미 wait 이벤트가 종료되어 버려 아무런 정보도 얻지 못하는 경우가 있을 수 있습니다.
또 새롭게 추가된 Active Session History(ASH)는 AWR과 마찬가지로 향후 분석 작업을 위해 세션 성능 통계를 버퍼에 저장합니다. AWR과 다른 점은, 테이블 대신 메모리가 저장 매체로 이용되며 V$ACTIVE_SESSION_HISTORY 등을 통해 조회된다는 사실입니다. 데이타는 1초 단위로 수집되며, 액티브 세션만이 수집 대상이 됩니다. 버퍼는 순환적인 형태로 관리되며, 저장 메모리 용량이 가득 차는 경우 오래된 데이타부터 순서대로 삭제됩니다. 이벤트를 위해 대기 중인 세션의 수가 얼마나 되는지 확인하려면 아래와 같이 조회하면 됩니다:
select session_id||','||session_serial# SID, n.name, wait_time, time_waited
from v$active_session_history a, v$event_name n
where n.event# = a.event#
위 쿼리는 이벤트 별로 대기하는 데 얼마나 많은 시간이 사용되었는지를 알려 줍니다. 특정 wait 이벤트에 대한 드릴다운을 수행할 때에도 ASH 뷰를 이용할 수 있습니다. 예를 들어, 세션 중 하나가 buffer busy wait 상태에 있는 경우 정확히 어떤 세그먼트에 wait 이벤트가 발생했는지 확인하는 것이 가능합니다. 이때 ASH 뷰의 CURRENT_OBJ# 컬럼과 DBA_OBJECTS 뷰를 조인하면 문제가 되는 세그먼트를 확인할 수 있습니다.
ASH 뷰는 그 밖에도 병렬 쿼리 서버 세션에 대한 기록을 저장하고 있으므로, 병렬 쿼리의 wait 이벤트를 진단하는 데 유용하게 활용됩니다. 레코드가 병렬 쿼리의 slave process로서 활용되는 경우, coordinator server session의 SID는 QC_SESSION_ID 컬럼으로 확인할 수 있습니다. SQL_ID 컬럼은 wait 이벤트를 발생시킨 SQL 구문의 ID를 의미하며, 이 컬럼과 V$SQL 뷰를 조인하여 문제를 발생시킨 SQL 구문을 찾아낼 수 있습니다. CLIENT_ID 컬럼은 웹 애플리케이션과 같은 공유 사용자 환경에서 클라이언트를 확인하는 데 유용하며, 이 값은 DBMS_SESSION.SET_IDENTFIER를 통해 설정 가능합니다.
ASH 뷰가 제공하는 정보의 유용성을 감안하면, AWR과 마찬가지로 이 정보들을 영구적인 형태의 매체에 저장할 필요가 있을 수도 있습니다. AWR 테이블을 MMON 슬레이브를 통해 디스크로 flush 할 수 있으며, 이 경우 DBA_HIST_ACTIVE_SESS_HISTORY 뷰를 통해 저장된 결과를 확인할 수 있습니다.
수작업으로 스냅샷 생성하기
스냅샷은 자동으로 수집되도록 디폴트 설정되어 있으며, 원하는 경우 온디맨드 형태의 실행이 가능합니다. 모든 AWR 기능은 DBMS_WORKLOAD_REPOSITORY 패키지에 구현되어 있습니다. 스냅샷을 실행하려면 아래와 같은 명령을 사용하면 됩니다:
execute dbms_workload_repository.create_snapshot
위 명령은 스냅샷을 즉각적으로 실행하여 그 결과를 table WRM$_SNAPSHOT 테이블에 저장합니다. 수집되는 메트릭의 수준은 TYPICAL 레벨로 설정됩니다. 더욱 자세한 통계를 원하는 경우 FLUSH_LEVEL 매개변수를 ALL로 설정하면 됩니다. 수집된 통계는 자동으로 삭제되며, 수작업으로 삭제하려는 경우 drop_snapshot_range() 프로시저를 실행하면 됩니다.
베이스라인
성능 튜닝 작업을 수행할 때에는 먼저 일련의 메트릭에 대한 베이스라인(baseline)을 수집하고 튜닝을 위한 변경 작업을 수행한 뒤, 다시 또 다른 베이스라인 셋을 수집하는 과정을 거치는 것이 일반적입니다. 이렇게 수집된 두 가지 셋을 서로 비교하여 변경 작업의 효과를 평가할 수 있습니다. AWR에서는 기존에 수집된 스냅샷을 통해 이러한 작업이 가능합니다. 예를 들어 매우 많은 자원을 사용하는 apply_interest라는 프로세스가 오후 1시부터 3시까지 실행되었고, 이 기간 동안 스냅샷 ID 56에서 59까지가 수집되었다고 합시다. 이 스냅샷들을 위해 apply_interest_1이라는 이름의 베이스라인을 아래와 같이 정의할 수 있습니다:
exec dbms_workload_repository.create_baseline (56,59,'apply_interest_1')
위 명령은 스냅샷 56에서 59까지를 ‘apply_interest_1’이라는 이름의 베이스라인으로 표시합니다. 기존에 설정된 베이스라인은 아래와 같이 확인합니다:
select * from dba_hist_baseline;

      DBID BASELINE_ID BASELINE_NAME        START_SNAP_ID END_SNAP_ID
---------- ----------- -------------------- ------------- -----------
4133493568           1 apply_interest_1                56          59
튜닝 과정을 거친 후, 또 다른 이름(예: apply_interest_2)의 베이스라인을 생성하여, 이 두 가지 베이스라인에 해당하는 스냅샷의 메트릭을 비교할 수 있습니다. 이처럼 비교 대상을 한정함으로써 성능 튜닝의 효과를 한층 향상시킬 수 있습니다. 분석이 끝나면 drop_baseline(); 프로시저로 베이스라인을 삭제할 수 있습니다 (이 때 스냅샷은 그대로 보존됩니다). 또 오래된 스냅샷이 삭제되는 과정에서, 베이스라인과 연결된 스냅샷은 삭제되지 않습니다.
결론
이 문서는 AWR의 매우 기초적인 기능만을 소개하고 있습니다. 더욱 자세한 내용을 확인하시려면 Oracle Database 10g 제품 문서를 확인하시기 바랍니다. 기술백서(“ The Self-Managing Database: Automatic Performance Diagnosis)에서도 AWR과 ADDM를 매우 깊이 있게 다루고 있습니다. 제 18주에는, ADDM을 이용하여 실제 문제를 해결하는 방법에 대해 자세하게 설명할 예정입니다.
 
 
 
 
제 7 주
SQL*Plus의 향상된 기능
Oracle Database 10g의 SQL*Plus 툴에는 프롬프트, 파일 조작 기능 등 몇 가지 눈에 띄는 기능 개선이 추가되었습니다.
DBA가 하루에 가장 많이 사용하는 툴은 무엇일까요? 아마 GUI 대신 오래된 작업 방식을 고수하고 있는 본인과 같은 DBA들이라면 SQL*Plus 커맨드 라인 툴을 그 첫 번째로 꼽을 것입니다.
Oracle Database 10g에서 Enterprise Manager 10g의 기능이 한층 강화되었음에도 불구하고, 초심자와 숙련된 DBA 모두에게 있어 SQL*Plus는 앞으로도 오랫동안 유용하게 활용될 것입니다.
이번 연재에서는 SQL*Plus 10.1.0.2에 추가된 유용한 기능을 살펴 보고자 합니다. 이 강좌를 따라 해 보려면 Oracle Database 10g에 포함된 sqlplus를 사용해야 함을 주의하시기 바랍니다 (Oracle9i Database의 sqlplus을 사용해서 10g 데이타베이스에 접속해서는 안됩니다).
프롬프트의 활용
지금 나는 어디에 있는가? 그리고 나는 누구인가? 지금 형이상학적인 질문을 던지려는 것이 아닙니다. SQL*Plus 환경의 관점에서 사용자의 위치를 묻는 질문입니다. SQL*Plus가 지금까지 천편일률적으로 제공했던 SQL> 프롬프트는 사용자가 누구이고 어디에 연결되었는지에 대한 아무런 정보도 제공하지 않습니다. 이전 릴리즈에서는 프롬프트를 바꾸려면 복잡한 코딩이 필요했습니다. SQL*Plus 10.1.0.2는 아래와 같은 간단한 명령만으로 프롬프트를 바꿀 수 있습니다:
set sqlprompt "_user _privilege> "
그러면 SQL*Plus 프롬프트는 아래와 같은 형식으로 표시됩니다:
SYS AS SYSDBA>
위의 경우는 SYS 사용자가 SYSDBA로서 접속한 상황임을 의미합니다. 위에서 _user와 _privilege라는 두 개의 변수가 활용되는 방법을 참고하십시오. _user는 현재 사용자를 _privilege는 로그인에 사용되는 privilege를 뜻합니다.
이번에는 다른 방법을 써 봅시다. 아래는 프롬프트에 오늘 날짜가 함께 표시되도록 하는 명령입니다:
SQL> set sqlprompt "_user _privilege 'on' _date >"
SYS AS SYSDBA on 06-JAN-04 >
여기에 database connection identifier를 추가할 수도 있습니다. 이 방법은 사용자의 “위치”를 수시로 확인해야 하는 환경에서 대단히 유용합니다.
SQL> set sqlprompt "_user 'on' _date 'at' _connect_identifier >"
ANANDA on 06-JAN-04 at SMILEY >
이번에는 오늘 날짜에 시간과 분까지 함께 표시하도록 해 보겠습니다.
ANANDA on 06-JAN-04 at SMILEY > alter session set nls_date_format = 'mm/dd/yyyy hh24:mi:ss';

Session altered.

ANANDA on 01/06/2004 13:03:51 at SMILEY >
이처럼 몇 번의 간단한 키 입력 만으로 매우 많은 정보를 포함하는 SQL 프롬프트가 만들어졌습니다. 이 설정을 glogin.sql 파일에 저장하면 앞으로도 계속 같은 프롬프트를 사용할 수 있습니다.
불필요한 인용부호의 생략
Oracle9i이 internal login을 더 이상 지원하지 않는다고 발표되었을 때, 많은 DBA들은 불만의 함성을 터뜨렸습니다. Internal login이 없다면 커맨드 라인 상에서 SYS 패스워드를 입력할 수 없고 따라서 보안 관리가 어려워진다는 이유였습니다. 오라클은 그 해결책으로 운영체제 프롬프트 상에서 인용부호를 사용하는 방법을 제시했습니다:
sqlplus "/ as sysdba"
DBA들은 이러한 변화에 만족하지 않았지만 참고 받아들일 수 밖에 없었습니다. Oracle Database 10g에서는 이러한 요구사항이 없어졌으며, 아래와 같은 방법으로 인용부호를 사용하지 않고 SYSDBA권한으로 로그인하는 것이 가능합니다.
sqlplus / as sysdba
이러한 변화는 단순히 입력할 문자의 수가 줄었다는 것만을 의미하지 않습니다. Unix와 같은 운영체제에서 escape 문자를 사용할 필요가 없어졌다는 것도 새로운 이점의 하나입니다.
파일 조작 기능의 향상
DBA가 문제 해결 과정에서 임시로 만든 SQL 구문이 있다고 가정해 봅시다. 이 DBA는 만든 구문을 나중에 다시 재사용하기 위해 저장하고 싶어할 수도 있습니다. 이럴 때 어떻게 해야 할까요? 아마도 아래와 같은 방법으로 파일에 개별 저장하는 방법을 쓰게 될 것입니다:
select something1 ....
save 1 
select something else ....
save 2
select yet another thing ....
save 3 
결국 위의 구문을 모두 사용하려면 그에 해당하는 세이브 파일을 모두 불러내어야 합니다. 이 얼마나 번거로운가요! SQL*Plus 10.1.0.2는 여러 구문을 파일에 append하는 형태로 저장하는 기능을 제공합니다. 위의 경우라면 아래와 같은 명령을 사용할 수 있습니다:
select something1 ....
save myscripts
select something else ....
save myscripts append
select yet another thing ....
save myscripts append
이렇게 하면 모든 구문을 myscript.sql 파일에 append 된 형태로 저장할 수 있으므로, 여러 파일에 나누어 저장한 뒤 이를 다시 하나로 연결할 필요가 없게 됩니다.
스풀링(spooling)에서도 append 형태의 저장 방식이 사용됩니다. 이전 릴리즈에서는 SPOOL RESULT.LST 명령으로 result.lst 파일을 생성할 수 있었지만, 기존에 같은 이름의 파일이 존재하는 경우 아무런 경고도 없이 덮어쓰기가 실행된다는 문제가 있었습니다. 이 때문에 실제로 작업을 수행하는 과정에서 중요한 파일이 덮어씌워져 버리는 곤란한 상황이 발생하곤 했습니다. 10g에서는 spool 명령을 수행하면서 append 방식으로 저장하도록 설정할 수 있습니다:
spool result.lst append
덮어쓰기를 원한다면 위의 append 조건 대신 REPLACE 조건을 삽입하면 됩니다. (REPLACE는 디폴트로 적용됩니다.) 아래 명령을 사용하면 세이브하기 전에 기존 파일이 존재하는지 점검합니다:
spool result.lst create
Use another name or "SPOOL filename[.ext] REPLACE"
This approach will prevent the overwriting of the file result.lst.
Login.sql 관련 문제
login.sql과 glogin.sql이라는 파일을 기억하십니까? SQL*Plus가 실행되면 현재 디렉토리에 있는 login.sql이 자동으로 실행됩니다. 하지만 심각한 기능상의 문제가 존재했습니다. Oracle9i 및 이하 버전의 login.sql 파일에 아래와 같은 내용이 포함되어 있는 경우를 생각해 봅시다:
set sqlprompt "_connect_identifier >"
데이타베이스DB1에 접속하기 위해 SQL*Plus를 실행하면 아래와 같이 프롬프트가 표시됩니다:
DB1>
이 프롬프트 상에서 데이타베이스 DB2로의 접속을 시도해 봅시다:
DB1> connect scott/tiger@db2
Connected
DB1>
DB2로 접속한 상태임에도 프롬프트가 여전히 DB1으로 표시되고 있습니다. 이유는 간단합니다. login.sql 파일은 데이타베이스 연결 시에 실행되지 않으며 SQL*Plus 시작 시에만 실행되기 때문입니다. Oracle Database 10g에서는 이러한 문제가 해결되었습니다. login.sql은 SQL*Plus가 시작되는 시점뿐 아니라 새로운 연결이 설정되는 경우에도 자동 실행됩니다. 10g 환경에서 DB1에 접속해 있다가 다른 데이타베이스로 연결을 변경하는 경우, 아래와 같이 프롬프트도 같이 변경됩니다.
In Oracle Database 10g, this limitation is removed. The file login.sql is not only executed at SQL*Plus startup time, but at connect time as well. So in 10g, if you are currently connected to database DB1 and subsequently change connection, the prompt changes.
SCOTT at DB1> connect scott/tiger@db2
SCOTT at DB2> connect john/meow@db3
JOHN at DB3>
변화를 원치 않는다면?
어떤 이유로든, SQL*Plus의 개선된 기능을 사용하고 싶지 않은 경우도 있을 수 있습니다. 그런 경우라면, 아래처럼 –C 옵션을 적용하면 됩니다:
sqlplus -c 9.2
위와 같이 입력하는 경우 9.2 버전의 SQL*Plus 환경이 실행됩니다.
Use DUAL Freely
아래와 같은 명령을 실제로 사용하는 개발자(또는 DBA)의 수가 얼마나 될 것이라 생각하십니까?
select USER into <some variable> from DUAL
아마 거의 모든 이들이 사용하고 있을 것입니다. DUAL은 호출될 때마다 새로운 논리적 I/O(Buffer I/O)를 생성합니다. 이 기능은 매우 유용하게 활용됩니다. DUAL은 <somevariable> := USER와 같은 구문만큼이나 자주 사용되고 있습니다. 하지만 오라클 코드는 DUAL을 특수한 형태의 테이블로서 취급하며, 따라서 일반적인 튜닝 방법은 적용할 수 없다는 문제가 있습니다.
Oracle Database 10g에서라면 이에 관련한 걱정은 할 필요가 없습니다. DUAL이 특수한 테이블이기 때문에, 논리적 I/O를 나타내는 consistent gets의 값도 줄어들며, event 10046 trace에서 확인할 수 있는 것처럼 optimization plan도 다른 형태로 나타납니다.
Oracle9i의 경우
Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: CHOOSE
      1   TABLE ACCESS (FULL) OF 'DUAL'
	  
10g의 경우
Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   MODE: ALL_ROWS
      0   FAST DUAL

Oracle 9i에서 사용되는 DUAL의 FULL TABLE SCAN 대신, 10g에서는 FAST DUAL optimization plan을 사용하고 있다는 점을 주목하시기 바랍니다. 이러한 기능 개선을 통해 DUAL 테이블을 자주 사용하는 애플리케이션의 연속적인 읽기 작업 성능이 대폭적으로 향상되었습니다.
참고: 엄밀하게 말하자면 DUAL 관련 기능 개선은 SQL*Plus가 아닌 SQL Optimizer의 기능이라고 보는 것이 타당할 것입니다. 여기에서는 이 기능에 접근하기 위해 주로 사용되는 툴이 SQL*Plus라는 이유로 이 기능을 소개합니다.
그 밖에 유용한 팁
그 밖에 SQL*Plus에 관련한 몇 가지 팁이 이 시리즈 전반에 걸쳐 소개되고 있습니다. 예를 들어, RECYCLEBIN 개념은 제 5주 Flashback Table 관련 연재에서 소개됩니다.
일반에 알려진 것과 달리, COPY 명령은 여전히 사용 가능합니다. 하지만 향후 릴리즈에서는 기능이 삭제될 것이라 합니다. (Oracle9i 시절부터 듣던 얘기가 아닌가요?) 이 명령이 적용된 스크립트를 사용하는 중이라면 고민할 필요가 없습니다. 사용 가능할 뿐 아니라 기술지원도 제공됩니다. 더군다나 에러 메시지 리포팅에 관련한 기능 개선도 일부 이루어졌습니다. 테이블이 LONG 칼럼을 포함하는 경우, 해당 테이블에 대한 복제본을 생성하려면 COPY 명령에 의존할 수 밖에 없습니다. CREATE TABLE AS SELECT 명령은 long 데이타 타입의 컬럼을 포함하는 테이블을 지원하지 않습니다.
 
 
 
 
제 8 주
Automatic Storage Management
마침내, DBA들은 스토리지 디스크를 추가, 이동, 삭제하는 반복적인 일상 업무로부터 해방될 수 있게 되었습니다.
오라클 데이타베이스에 사용할 새로운 서버와 스토리지 서브시스템을 지금 막 접수한 상황이라고 가정해 봅시다. 운영체제를 구성하는 문제를 제외하고, 데이타베이스를 설치하기 전에 먼저 선행되어야 할 가장 중요한 작업이 무엇일까요? 그것은 바로 스토리지 시스템 레이아웃을 생성하는 일, 좀 더 구체적으로 말해 보호 레벨(protection level)을 정의하고 적합한 RAID(Redundant Array of Inexpensive Disks) 셋을 구성하는 일일 것입니다.
데이타베이스 설치 과정에서 스토리지를 구성하는 작업은 상당히 많은 시간을 소요합니다. 적합한 디스크 구성을 선택하기 위해서는 매우 신중한 계획과 분석이 필요하며, 무엇보다도 스토리지 테크놀로지, 볼륨 관리자, 파일 시스템 등에 대한 깊은 지식을 요구합니다. 이 과정에서 수행되는 설계 작업은 대충 아래와 같은 단계로 정리할 수 있습니다. (아래는 매우 일반적인 내용만을 포함하고 있으며, 실제 작업은 구성에 따라 달라질 수 있습니다.)
  1. 스토리지가 운영체제 레벨에서 인식되는지 확인하고, 중복성(redundancy) 및 보호 레벨(protection level)을 결정합니다 (hardware RAID).
  2. 논리적 볼륨 그룹을 생성하고, 필요한 경우 스트라이핑 또는 미러링 구성을 선택합니다.
  3. 논리적 볼륨 관리자(logical volume manager)를 이용하여 논리적 볼륨 상에 파일 시스템을 생성합니다.
  4. Oracle 프로세스가 디바이스에 대한 열기, 읽기, 쓰기를 수행할 수 있도록 접근권한 설정을 합니다.
  5. 파일시스템 상에 데이타베이스를 생성하고, 가능한 경우 리두 로그, 임시 테이블스페이스, 언두 테이블스페이스 등을 별도의 non-RAID 디스크에 배치합니다.
대부분의 환경에서는, 이러한 작업의 대부분은 스토리지 시스템에 조예가 깊은 전문가에 의해 수행됩니다. 여기서 말하는 “전문가”는 DBA와 다른 별도의 인력을 의미하는 경우가 많습니다.
하지만 스트라이핑, 미러링, 논리적 파일시스템 생성과 같은 모든 작업이 단지 Oracle Database만을 위해 수행된다는 점을 감안한다면, 오라클에서 이러한 절차를 단순화할 수 있는 테크닉을 직접 제공하는 것이 순리에 맞지 않을까요?
Oracle Database 10g가 바로 이를 위한 기능을 제공합니다. Automatic Storage Management(ASM)은 위에서 언급된 작업의 상당 부분을 DBA가 오라클 프레임워크 내에서 직접 수행할 수 있도록 하는 자동화 옵션입니다. ASM을 이용하면 별도의 비용을 지불하지 않고도 Oracle Database 10g가 기본적으로 지원하는 기능만을 활용하여 높은 확장성과 성능을 갖는 파일시스템/볼륨 관리자 환경을 구현할 수 있습니다. 또 디스크, 볼륨 관리자, 파일 시스템 관리에 대한 전문적인 지식을 가지고 있을 필요도 없습니다.
이번 연재에서는, 실제 환경에서 ASM을 활용하기 위한 기본적인 방법을 설명합니다. ASM은 제한된 지면을 통해 섭렵하기에는 너무도 방대하고 강력한 기능입니다. 더 자세한 정보를 원하신다면 결론부분에서 소개하는 자료들을 참고하시기 바랍니다.
ASM이란 무엇인가?
데이타베이스에 사용할 디스크가 10개 있다고 가정해 봅시다. ASM을 이용하는 경우, OS에서는 아무 것도 수행할 필요가 없습니다. ASM은 일련의 물리적 디스크를 “디스크그룹(diskgroup)”이라는 논리적 개체로 자동으로 그룹화합니다. ASM이 사용하는 파일시스템은 사용자 파일을 저장할 수 없으며 버퍼링 기능을 제공하지 않으므로 범용 파일시스템으로 사용될 수는 없습니다. 하지만 (버퍼링 기능이 포함되지 않았기 때문에) 직접 액세스(direct access)를 통해 로우 디바이스(raw device) 수준의 성능을 내는 동시에 파일 시스템이 제공하는 편의성과 유연성을 활용할 수 있다는 장점을 제공합니다.
논리적 볼륨 관리자는 일반적으로 블록의 논리적 주소를 물리적 주소로 변환하는 함수를 제공합니다. 이 변환 작업에는 CPU 자원이 사용됩니다. 또 (RADI-5 등의) 스트라이프 구성에 새로운 디스크가 추가되는 경우, 전체 데이타 셋에 대해 비트 단위의 이동작업이 수행되어야 합니다.
반면 ASM은 파일 익스텐트(extent)를 물리적 디스크 블록으로 변환하기 위해 별도의 오라클 인스턴스를 사용합니다. 이러한 설계 덕분에 파일 익스텐트의 위치를 확인하는 작업을 보다 신속하게 수행할 수 있을 뿐 아니라, 디스크를 추가하거나 이동하는 경우에도 파일 익스텐트의 위치를 재조정할 필요가 없다는 이점을 제공합니다. ASM 인스턴스는 ASM이 실행되기 위해서 반드시 필요하며 사용자에 의해 변경될 수 없습니다. 동일 서버 내에 위치한 여러 개의 오라클 데이타베이스 인스턴스는 하나의 ASM 인스턴스를 공유합니다.
ASM 인스턴스는 “인스턴스”이긴 하지만 데이타베이스를 포함하지 않습니다. 디스크에 관련된 모든 메타데이타는 디스크그룹 자체에 저장되며, 최대한 “self-describing”한 형태로 기술됩니다.
ASM이 제공하는 이점을 간략히 정리하면 아래와 같습니다:
  • 디스크의 추가 — 디스크의 추가 작업이 매우 간단합니다. 다운타임이 전혀 발생하지 않으며, 파일 익스텐트는 자동으로 재분배됩니다.
  • I/O 분산 – I/O는 전체 디스크에 골고루 분산됩니다. 이에 관련하여 수작업이 전혀 불필요하며, 성능 병목이 발생할 가능성도 그만큼 줄어듭니다.
  • 스트라이프 “width”의 설정 – 스트라이핑의 데이타 전송 단위는 Redo Log 파일(128K 단위 전송)처럼 작게, 또는 데이타파일(1MB 단위 전송)처럼 크게 설정할 수 있습니다.
  • 버퍼링 – ASM 파일시스템에는 버퍼링 기능이 구현되어 있지 않으며, 따라서 direct I/O를 이용해 성능 향상이 가능합니다.
  • Kernerlized Async I/O – kernelized asynchronous I/O를 위해 별도의 셋업 과정을 거칠 필요가 없으며, VERITAS Quick I/O와 같은 써드 파티 파일 시스템을 이용할 필요도 없습니다.
  • 미러링 – 하드웨어 미러링을 적용할 수 없는 경우, 소프트웨어 미러링을 쉽게 구성할 수 있습니다.
ASM을 이용한 데이타베이스 생성
ASM을 이용해 데이타베이스를 생성하는 방법을 구체적으로 설명해 보겠습니다:
1. ASM Instance의 생성
Database Creation Assistant(DBCA)에서 아래와 같이 초기화 매개변수를 설정하여 ASM 인스턴스를 생성합니다:
INSTANCE_TYPE = ASM
ASM 인스턴스는 서버 부팅 과정에서 가장 먼저 시작되고, 서버 셧다운 과정에서 가장 마지막으로 중지되어야 합니다.
이 매개변수의 디폴트 값은 RDBMS이며, 이는 일반 데이타베이스 환경을 의미합니다.
2. Diskgroup의 셋업
ASM 인스턴스를 시작한 뒤, 구성된 디스크를 이용해 디스크그룹을 생성합니다.
CREATE DISKGROUP dskgrp1
EXTERNAL REDUNDANCY
DISK
'/dev/d1',
'/dev/d2',
'/dev/d3',
'/dev/d4',
... and so on for all the specific disks ...
;
위의 예에서는 dksgrp1이라는 이름의 디스크그룹을 생성하면서 /dev/d1, /dev/d2 등의 물리적 디스크를 개별적으로 지정했습니다. 이처럼 디스크를 일일이 지정할 수도 있지만, 아래와 같이 와일드카드를 사용하는 것도 가능합니다.
DISK '/dev/d*'
위 명령에서 EXTERNAL REDUNDANCY는 디스크에 장애가 발생하는 경우 디스크그룹이 다운됨을 의미합니다. 이러한 구성은 하드웨어 미러링 등의 구성이 별도로 지원되는 경우에 사용됩니다. 하드웨어 기반의 미러링이 사용되지 않는 경우라면, ASM에서 “failgroup”으로 지정된 디스크그룹을 생성하여 이를 대신할 수 있습니다.
CREATE DISKGROUP dskgrp1
NORMAL REDUNDANCY
FAILGROUP failgrp1 DISK
'/dev/d1',
'/dev/d2',
FAILGROUP failgrp2 DISK
'/dev/d3',
'/dev/d4';
위의 명령에서 d3와 d4가 d1과 d2의 미러(mirror)로서 1:1 대응되는 것이 아님에 주의하시기 바랍니다. ASM은 모든 디스크를 한꺼번에 활용하여 중복성(redundancy)을 구현합니다. 예를 들어, 디스크그룹의 d1에 생성된 특정 파일은 d4에 복제본을 가지고 있는 반면, d3에 생성된 다른 파일은 d2에 복제본을 가지고 있을 수 있습니다. 디스크에 장애가 발생한 경우 다른 디스크에 저장된 복제본을 이용하여 운영을 재개할 수 있습니다. d1과 d2가 연결된 컨트롤러가 다운된 경우에도, ASM은 failgroup으로 지정된 디스크를 이용하여 데이타 무결성을 보장합니다.
3. Tablespace의 생성
이제 ASM이 적용된 스토리지의 데이타파일을 사용하여 메인 데이타베이스의 테이블스페이스를 생성해 봅시다.
CREATE TABLESPACE USER_DATA DATAFILE '+dskgrp1/user_data_01' 
SIZE 1024M
/
이걸로 끝입니다. 이제 셋업 과정은 모두 완료되었습니다.
디스크그룹이 가상 파일시스템(virtual file system)으로서 이용되고 있음을 참고하시기 바랍니다. 이러한 구성은 데이타파일 이외의 다른 오라클 파일을 생성하는 경우에도 유용하게 활용됩니다. 예를 들어 아래와 같이 온라인 리두 로그를 생성할 수 있습니다.
LOGFILE GROUP 1 (
    '+dskgrp1/redo/group_1.258.3',
    '+dskgrp2/redo/group_1.258.3'
  ) SIZE 50M,
...
관련 자료
앞에서 언급한 것처럼, 이 문서는 지면 문제로 ASM 기능에 대한 모든 내용을 다루지 않고 있지 않습니다. 하지만 Oracle Technology Network는 ASM과 관련한 많은 정보를 제공하고 있습니다.
"Storage on Automatic," (Lannes Morris-Murphy) – ASM 소개 자료로서 매우 훌륭한 설명을 담고 있습니다.
ASMLib, , Linux 환경을 위한 ASM 관련 라이브러리로, ASM의 기능을 더욱 확장시켜 줍니다. 라이브러리 모듈에 대한 테크니컬 자료와 소스 코드의 링크를 함께 제공합니다. Oracle Database Administrator's Guide 10g Release 1 (10.1) 제 12장은 ASM에 관련하여 매우 자세한 배경 정보를 제공하고 있습니다.
아카이브 로그 역시 디스크그룹에 저장되도록 설정할 수 있습니다. 실제로 Oracle Database에 관련된 거의 모든 것이 ASM 기반 디스크그룹에서 생성 가능합니다. 백업 또한 ASM이 활용되는 분야의 하나입니다. 저가형 디스크를 이용해 데이타베이스 복구 영역을 생성한 다음, RMAN을 통해 데이타파일과 아카이브 로그파일 백업을 생성할 수 있습니다. (RMAN에 관련한 다음 연재에서 이 기능에 대해 자세히 설명할 예정입니다.)
ASM이 Oracle Database에 의해 생성되는 파일만을 지원한다는 사실을 주의하시기 바랍니다. ASM은 일반 파일 시스템을 대체하는 수단으로 이용될 수 없으며 바이너리 또는 플랫 파일을 저장할 수 없습니다.
유지 보수
디스크그룹의 유지보수를 위해 DBA가 일반적으로 수행하는 몇 가지 작업에 대해 설명하겠습니다. 디스크그룹 diskgrp1에 디스크를 추가하여 볼륨을 확장하고자 하는 경우, 아래와 같은 명령을 사용합니다:
alter diskgroup dskgrp1 add disk '/dev/d5';
어떤 디스크가 어떤 디스크그룹에 포함되었는지를 확인하려면 아래와 같이 입력합니다:
select * from v$asm_disk;
이 명령은 ASM 인스턴스가 관리하는 모든 데이타베이스의 디스크의 목록을 표시합니다. 특정 디스크를 제거하고자 하는 경우 아래 명령을 사용합니다:
alter diskgroup dskgrp1 drop disk diskb23;
결론
ASM을 이용하면 오라클 데이타베이스의 파일 관리 작업 환경을 한층 향상시키고, 고도의 확장성과 성능을 갖춘 스토리지 환경을 구성할 수 있습니다. ASM은 디스크의 자유로운 추가, 이동, 제거가 가능한 다이내믹 데이타베이스 환경을 구현하고 DBA를 반복적인 업무로부터 해방시키는데 필요한 툴셋을 제공합니다.
 
 
 
 
제 9 주
RMAN
한층 강력해진 RMAN 유틸리티는 개선된 증분백업, 증분백업의 오프라인 복구, 복구 파일 미리보기, resetlog를 이용한 복구, 파일 압축 등에 관련한 다양한 신기능을 제공합니다.
RMAN이 오라클 데이타베이스 백업 툴의 실질적인 표준으로서 인정되고 있다는 사실은 대부분의 사람들이 동의할 것입니다. 하지만 RMAN의 이전 버전에 문제가 많았던 것도 사실입니다. 필자 역시 RMAN의 기능적인 한계에 불만을 가진 사용자 중 하나였습니다.
Oracle Database 10g는 이러한 문제의 많은 부분을 해결하고 RMAN을 한층 강력하고 유용한 툴로 변화시켰습니다. 그러면 한 번 살펴보기로 합시다.
증분 백업 (Incremental Backup) 기능의 개선
RMAN은 이전부터 증분 백업을 위한 옵션을 제공해 왔습니다. 하지만 이 기능을 실제로 사용하는 경우는 극히 드물었습니다.
증분 백업은 마지막으로 증분 백업이 수행된 이후 변경된 블록만을 백업하는 방식입니다. 예를 들어 Day 1에 전체 백업(level_0)이 수행되고 Day 2와 Day 3에 두 차례의 증분 백업(level_1)이 수행된 경우를 생각해 봅시다. 두 개의 증분 백업은 각각 Day 1과 Day 2, Day 2와 Day3 사이에 변경된 블록만을 포함하고 있습니다. 이와 같은 백업 정책을 사용함으로써 백업 사이즈와 백업에 필요한 디스크 공간을 절감하고, 백업 윈도우를 단축할 수 있을 뿐 아니라 네트워크를 통해 전송되는 데이타 양을 줄일 수 있습니다.
증분 백업은 데이타 웨어하우스 환경에서 특히 유용합니다. 데이타 웨어하우스 작업의 많은 부분은 NOLOGGING 모드로 수행되므로 변경 내역이 아카이브 로그 파일에 저장되지 않으며, 따라서 미디어 복구가 불가능합니다. 데이타 웨어하우스 환경의 데이타 규모와 이 데이타의 대부분이 거의 변경되지 않는다는 사실을 고려할 때, 전체 백업은 효과적이지 못하며 현실적으로 불가능할 수도 있습니다. 그 대신 RMAN을 사용해서 증분 백업을 수행하는 것이 좋은 대안이 될 수 있습니다..
그렇다면 DBA들이 증분 백업 방식을 이용하지 않는 이유는 무엇일까요? Oracle9i와 그 이전 버전의 경우, RMAN은 증분 백업을 수행하기 위해 전체 데이타 블록에 대한 스캔을 수행했습니다. 이러한 작업이 시스템에 너무 큰 부담을 주기 때문에 증분 백업이 효율적이지 않다는 평가를 받게 된 것입니다.
Oracle Database 10g의 RMAN 기능은 이러한 점에서 크게 개선되었습니다. Oracle Database 10g는 (파일시스템의 저널과 유사한 용도를 갖는) 별도의 파일을 통해 마지막 백업 이후 변경된 블록을 추적합니다. RMAN은 (전체 데이타 블록을 스캔하는 대신) 이 파일을 참조하여 어떤 블록을 백업해야 하는지 결정합니다.
아래 명령을 통해 추적 메커니즘(tracking mechanism)을 활성화시킬 수 있습니다:
SQL> alter database enable block change tracking using file '/rman_bkups/change.log';
위 명령은 /rman_bkups/change.log 바이너리 파일을 생성하고 이 파일에 블록 변경 내역을 저장합니다. 아래 명령을 사용하면 추적 메커니즘을 비활성화할 수 있습니다:
SQL> alter database disable block change tracking;
현재 블록 변경 내역의 추적이 수행되고 있는지 확인하려면 아래 쿼리를 사용합니다:
SQL> select filename, status from v$block_change_tracking;
Flash Recovery Area
Oracle9i에서 처음 소개된 Flashback 쿼리는 언두 테이블스페이스를 사용하여 이전 버전으로의 “회귀(flash-back)”을 수행하며, 그 원리상 아주 오래 전의 과거 시점으로는 되돌릴 수 없다는 한계를 갖습니다. 이러한 문제의 대안으로서 제공되는 Flash Recovery는 리두 로그와 유사한 형태의 flashback log을 생성함으로써, 원하는 특정 시점으로 데이타베이스의 상태를 되돌릴 수 있게 합니다. Flash Recovery를 사용하려면 먼저 데이타베이스에 flash recovery area를 생성하고, 그 크기를 정의한 뒤 아래와 같은 SQL 명령을 통해 데이타베이스를 flash recovery mode로 설정하면 됩니다:
alter system set db_recovery_file_dest = '/ora_flash_area';
alter system set db_recovery_file_dest_size = 2g;
alter system set db_flashback_retention_target = 1440;
alter database flashback on; 
Flashback 기능을 사용하려면 데이타베이스가 아카이브 로그 모드로 설정되어 있어야 합니다. Flash Recovery가 활성화되면 디렉토리 /ora_flash_area에 최대 2GB 크기의 Oracle Managed File이 생성됩니다. 모든 데이타베이스 변경 사항은 이 파일에 기록되며, 이 파일을 사용하여 과거의 특정 시점으로 데이타베이스를 복구할 수 있습니다.
RMAN은 /ora_flash_area 디렉토리를 디폴트 백업 파일 저장위치로 사용하며, 따라서 백업 파일은 테이프가 아닌 디스크에 저장됩니다. 이 경우 백업 파일을 얼마나 오랫동안 보존할 것인지 설정할 수 있으며, 정의된 보존 기간이 지난 후 추가 공간이 필요해지면 파일은 자동으로 삭제됩니다.
Flash recovery area가 반드시 파일시스템 또는 디렉토리일 필요는 없으며, ASM(Automatic Storage Management) 디스크그룹으로 지정할 수도 있습니다. Flash recovery area를 ASM 디스크그룹으로 지정하려면 아래와 같은 명령을 사용합니다:
alter system set db_recovery_file_dest = '+dskgrp1';
ASM과 RMAN을 함께 사용하면, 별도의 추가비용 없이 Serial ATA 드라이브 또는 SCSI 드라이브 등의 저가형 디스크를 사용해서 뛰어난 확장성과 가용성을 갖춘 스토리지 환경을 구성할 수 있습니다. (ASM에 대한 자세한 설명은 이 시리즈의 제 8 주 연재를 참고하시기 바랍니다.) 이와 같이 구성함으로써 테이프 기반 솔루션만큼 저렴한 비용으로 디스크 백업 환경을 구축하는 동시에 백업 프로세스의 실행 속도를 향상시킬 수 있습니다.
이 접근법의 또 한 가지 장점으로 사용자 실수에 대한 보호 기능을 들 수 있습니다. ASM 파일은 일반적인 파일시스템 환경이 아니기 때문에, DBA와 시스템 관리자의 실수로 손상될 가능성이 매우 적습니다.
증분 병합 (Incremental Merge)
아래와 같은 백업 스케줄을 갖는 환경을 가정해 봅시다:
일요일 - Level 0 (전체 백업), tag level_0
월요일 - Level 1 (증분 백업), tag level_1_mon
Tuesday - Level 1 (incremental) with tag level_1_tue

... (이하 생략)
이와 같은 백업정책 하에서, 토요일에 데이타베이스 장애가 발생한 경우, 10g 이전의 환경에서는 tag level_0를 복구하고 나머지 6개의 증분 백업본을 모두 복구해야만 했습니다. 이러한 작업에는 매우 오랜 시간이 걸리며, 이는 실제로 DBA가 증분 백업을 즐겨 사용하지 않는 이유 중 하나이기도 합니다.
Oracle Database 10g RMAN은 이러한 작업환경을 극적으로 개선했습니다. Oracle Database 10g RMAN의 증분 백업 명령은 아래와 같은 형태로 수행됩니다:
RMAN> backup incremental level_1 for recover of copy with tag level_0 database;
위에서 우리는 RMAN이 incremental level_1 백업을 수행하고 그 결과를 level_0의 전체 백업본과 병합(merge)하도록 설정했습니다. 이 명령을 수행하면 그 날의 전체 백업 이미지를 갖는 level_0 백업본이 새로이 생성됩니다.
예를 들어, 화요일에 수행된 증분 백업(level_1)은 이전의 전체 백업(level_0)과 병합되어 화요일 버전의 새로운 전체 백업본이 생성됩니다. 마찬가지로 토요일에 수행된 증분백업 역시, 금요일의 전체백업본과 병합되어 새로운 전체백업으로 저장됩니다. 따라서 토요일에 데이타베이스가 장애가 발생한다면, level_0 백업본 하나와 아카이브 로그 몇 개만을 이용하여 데이타베이스를 복구할 수 있습니다. 이러한 방법으로 복구에 소요되는 시간을 극적으로 절감하고, 백업 속도를 향상시키는 한편, 전체 백업의 수행 횟수를 줄일 수 있습니다.
압축 파일(Compressed Files)
Flash recovery area에 디스크 백업을 보관하는 경우에도, 디스크 공간의 한계라는 문제는 여전히 남습니다. 특히 네트워크를 통해 백업을 수행하는 환경이라면, 백업본의 크기를 최대한 작게 유지하는 구성이 권장됩니다. 이를 위해 Oracle Database 10g RMAN은 백업 명령에 새로운 압축 옵션을 추가하였습니다:
RMAN> backup as compressed backupset incremental level 1 database;
COMPRESSED 키워드가 사용된 형태를 주의해 보시기 바랍니다. 이 키워드를 사용하는 경우 백업 데이타의 압축을 통해 백업 성능을 향상할 수 있으며, 복구 과정에서는 별도의 압축해제 작업 없이도 RMAN이 파일을 읽어 들일 수 있습니다. 압축을 설정한 경우, 백업 수행 과정에서 아래와 같은 메시지가 출력됩니다.
channel ORA_DISK_1: starting compressed incremental level 1 datafile backupset
또, RMAN list output 명령을 통해 백업 압축 설정 여부를 확인할 수도 있습니다.
RMAN> list output;

BS Key  Type LV Size       Device Type Elapsed Time Completion Time
------- ---- -- ---------- ----------- ------------ ---------------
3       Incr 1  2M         DISK        00:00:00     26-FEB-04      
        BP Key: 3   Status: AVAILABLE  Compressed: YES  Tag: TAG20040226T100154
        Piece Name: /ora_flash_area/SMILEY10/backupset/2004_02_26/o1_mf_ncsn1_TAG20040226T100154_03w2m3lr_.bkp
  Controlfile Included: Ckp SCN: 318556       Ckp time: 26-FEB-04
  SPFILE Included: Modification time: 26-FEB-04
모든 압축 알고리즘이 그러하듯, 이 방법을 쓰는 경우 CPU에 추가적인 부담을 주게 됩니다. 반면, 더 많은 RMAN 백업을 디스크에 보관할 수 있다는 장점이 있습니다. 또 Physical Standby Database에서 RMAN 백업을 실행하면 원본 서버에 부담을 주지 않고 백업을 수행할 수 있습니다.
Look Before You Leap: Recovery Preview
Oracle Database 10g의 RMAN은 한 걸음 더 나아가 복구 작업에 사용할 수 있는 백업본을 미리 확인하는 Recovery Preview 기능을 지원합니다.
RMAN> restore database preview;
위 작업의 실행 결과는 Listing 1에서 확인하실 수 있습니다. 테이블스페이스 별로 백업본을 개별적으로 확인하는 것도 가능합니다:
restore tablespace users preview;
Preview 기능을 활용하여 백업 인프라스트럭처가 정상적으로 운영되고 있는지 정기적으로 점검할 수 있습니다.
Resetlog와 복구 작업
커런트 온라인 리두 로그(current online redo log) 파일이 손실되어, 불완전한 데이타베이스 복구(incomplete database recovery)를 수행할 수 밖에 없는 상황을 가정해 봅시다 (이러한 경우는 흔치 않지만 실재로 존재합니다). 이 경우 가장 큰 문제는 resetlog입니다. 불완전 복구를 수행한 뒤에 resetlog 키워드를 사용하여 log thread의 시퀀스 넘버를 1로 재설정하고 데이타베이스를 오픈해야 하는데, 이렇게 하는 경우 이전에 백업된 데이타가 무용지물이 될 뿐 아니라 복구 작업 자체가 더욱 어려워지게 됩니다.
Oracle9i와 그 이전 버전에서는 데이타베이스를 resetlog 이전의 상태로 복구하려면 전혀 새로운 환경에 데이타베이스를 새로 구축할 수 밖에 없었습니다. Oracle Database 10g에서는 이러한 문제가 해결되었습니다. 컨트롤 파일에 추가된 새로운 기능 덕분에, RMAN이 resetlog 수행 이전 또는 이후의 모든 백업 이미지를 복구에 이용할 수 있게 된 것입니다. 또 백업을 수행하기 위해 데이타베이스를 셧다운할 필요도 없게 되었습니다. 이 기능을 사용하면, resetlog 작업 이후 데이타베이스를 바로 오픈하고 운영을 재개하는 것이 가능합니다.
Ready for RMAN
Oracle Database 10g RMAN은 한층 향상된 백업 관리 툴로 거듭 태어났습니다. 증분 백업에 관련한 기능 개선만으로도 RMAN은 더 이상 무시할 수 없는 툴이 되었습니다.
Oracle Database 10g RMAN에 대한 보다 자세한 정보는,“Oracle Database Backup and Recovery Basics 10g Release 1 (10.1)” 문서의 Chapter 4을 참고하시기 바랍니다.
 
 
 
 
제 10 주
감사 기능의 확장
Oracle Database 10g 의 개선된 감사(Audit Trail) 기능을 이용해 매우 상세한 수준의 사용자 접근 기록을 관리하고, 수작업에 의존한 트리거 기반의 감사 작업을 최소화할 수 있습니다.
사용자 Joe가 아래와 같이 테이블 로우에 대한 업데이트를 수행한 경우를 가정해 봅시다.
update SCOTT.EMP set salary = 12000 where empno = 123456;
이 작업이 데이타베이스에는 어떠한 형태의 기록으로 남게 될까요? Oracle9i Database와 그 이전 버전에서는 “누가” 접근했는지에 대한 기록은 남았지만, “어떤” 작업을 했는지에 대해서는 아무런 기록도 남지 않았습니다. 예를 들어, 사용자 Joe가 SCOTT가 소유한 EMP 테이블을 업데이트했다는 사실은 알 수 있지만, 그가 사번 123456을 가진 직원의 salary 컬럼을 업데이트했다는 사실은 알 수 없습니다. 또 salary 컬럼의 변경 전 값 역시 기록되지 않습니다. 이처럼 상세한 변경 내역을 추적하려면, 트리거를 적용하거나 Log Miner를 이용해 아카이브 로그를 뒤지는 수 밖에 없었습니다.
두 가지 방법 모두 변경된 컬럼과 값을 확인할 수는 있지만 이에 수반되는 비용이 만만치 않다는 문제가 따릅니다. 감사 목적으로 적용되는 트리거는 성능적으로 심각한 부담을 줍니다. 이러한 이유 때문에 사용자 정의 트리거의 적용을 아예 금지하는 경우도 많습니다. Log Miner는 성능 자체에는 아무런 영향을 주지 않으나 변경 내역을 추적하기 위해 아카이브 로그에 의존해야 한다는 문제가 있습니다.
Oracle9i에 추가된 Fine-Grained Auditing(FGA) 기능은 로우(row) 단위 변경 내역을 SCN 넘버와 함께 저장함으로써 이전의 데이타를 재구성할 수 있게 하지만, select 구문에 대해서만 적용 가능하고 update, insert, delete 등의 DML 구문에는 적용할 수 없습니다. 이러한 이유로, Oracle Database 10g 이전 버전에서 로우 단위의 데이타 변경 내역을 추적하려면 트리거를 적용하는 것 외에 다른 방법이 없었습니다.
Oracle Database 10g에서는 감사 작업과 관련하여 두 가지 중요한 기능 개선이 이루어졌습니다. 이 문서에서는 오라클이 제공하는 두 종류의 감사 관련 기능 – (모든 버전에서 제공되는) 표준 감사(standard audit) 기능과 (Oracle9i 이후 버전에서 제공되는) fine-grained audit 기능 – 의 관점에서 개선된 사항을 설명하고자 합니다.
새로운 기능들
첫 번째로, FGA는 select 이외의 다른 DML 구문을 지원합니다. 변경 내역은 이전과 마찬가지로 FGA_LOG$에 기록되며, DBA_FGA_AUDIT_TRAIL 뷰를 통해 확인할 수 있습니다. 또 특정 컬럼이 액세스되는 경우, 또는 정의된 일련의 컬럼이 액세스되는 경우에만 트리거가 동작하도록 설정하는 것이 가능합니다 (10g 환경에서의 FGA 기능에 대한 상세 정보를 확인하시려면 필자의 테크니컬 아티클을 확인하시기 바랍니다.)
AUDIT” SQL 명령으로 구현되는 표준 감사 기능은 특정 오브젝트의 감사를 위한 환경을 쉽고 빠르게 설정하려는 경우 유용합니다. 예를 들어, Scott가 소유자인 EMP 테이블에 대한 모든 업데이트를 추적하려면 아래와 같은 명령을 사용합니다:
audit UPDATE on SCOTT.EMP by access;
위 명령은 SCOTT.EMP 테이블에 업데이트가 발생할 때마다 그 내역을 AUD$ 테이블에 기록합니다. 감사 기록은 DBA_AUDIT_TRAIL 뷰를 통해 조회할 수 있습니다.
이 기능은 10g 이전의 버전에서도 제공되어 오던 것입니다. 하지만 이전 릴리즈에서는 제한적인 정보(구문을 실행한 사용자, 시간, 터미널 ID, 등)만을 추적할 수 있었으며, 바인딩 되는 변수 값과 같은 중요한 정보의 추적이 불가능했습니다. 10g에서는 이전 버전에서 제공되던 것에 추가하여 여러 가지 중요한 정보를 수집할 수 있도록 개선되었습니다. 감사 정보를 저장하는 AUD$ 테이블에도 몇 가지 새로운 컬럼이 추가되었으며, 이전과 마찬가지로 DBA_AUDIT_TRAIL을 통해 확인할 수 있습니다. AUD$ 테이블에 추가된 컬럼에 대해 좀 더 자세히 살펴보기로 합시다.
EXTENDED_TIMESTAMP. 이 컬럼은 감사 기록의 타임스탬프 값을 TIMESTAMP(6) 포맷으로 저장합니다 (TIMESTAMP(6) 포맷은 Time Zone 정보와 함께 그리니치 평균 시간(Universal Coordinated Time으로 불리기도 합니다)을 소수점 이하 9자리 초 단위로 표현합니다). 이 포맷으로 저장된 시간 정보의 예가 아래와 같습니다:
2004-3-13 18.10.13.123456000 -5:0
위 데이타는 미국 동부 표준 시간을 기준으로 한 2004년 3월 13일 시간 정보를 표시하고 있습니다 (-5.0”은 UTC 보다 5시간 이전을 기준으로 함을 의미합니다).
이와 같은 확장형 포맷을 사용함으로써 매우 세밀한 시간 간격으로 감사 기록을 관리할 수 있고, 여러 시간대에 걸쳐 사용되는 데이타베이스 환경에서 한층 유용한 정보를 얻을 수 있습니다.
GLOBAL_UID and PROXY_SESSIONID. 사용자 인증을 위해 Oracle Internet Directory와 같은 아이덴티티 관리 솔루션을 사용하는 경우, 데이타베이스가 사용자를 표시하는 방법도 조금 달라지게 됩니다. 예를 들어, 인증된 사용자가 (데이타베이스 사용자가 아닌) 엔터프라이즈 사용자인 경우를 생각해 봅시다. 이 경우 DBA_AUDIT_TRAIL 뷰의 USERNAME 컬럼에 enterprise userid가 표시되며, 따라서 해당 컬럼의 정보에 일관성이 없게 됩니다. Oracle Database 10g에서는 enterprise userid를 위한 GLOBAL_UID 컬럼을 별도로 제공하며, 이 컬럼을 이용하여 디렉토리 서버에 엔터프라이즈 사용자에 대한 정보를 질의할 수 있습니다.
멀티-티어 애플리케이션 환경에서 엔터프라이즈 사용자가 proxy user를 통해 데이타베이스에 접속하는 경우도 있습니다. 사용자에게 프록시 인증을 제공하는 명령이 아래와 같습니다:
alter user scott grant connect to appuser;
위 명령은 사용자 SCOTT가 APPUSER라는 proxy user를 통해 데이타베이스에 접속할 수 있게 합니다. 이 경우, Oracle 9i는 COMMENT_TEXT에 “PROXY”라는 값을 저장하지만 proxy user의 session id는 저장하지 않습니다. 10g에서는 PROXY_SESSIONID에 proxy session id를 저장합니다.
INSTANCE_NUMBER. Oracle Real Application Clusters (RAC) 환경에서 사용자가 실제로 어느 인스턴스에 연결되어 있는지 확인할 수 있다면 여러모로 도움이 될 것입니다. 10g에서는 INSTANCE_NUMBER 컬럼에 초기화 매개변수 파일에 지정된 해당 인스턴스의 인스턴스 번호를 저장합니다.
OS_PROCESS. Oracle9i와 그 이전 버전에서는 SID 값만을 저장하고 운영체제의 process id는 저장하지 않았습니다. 하지만 서버 프로세스의 OS process id가 Trace파일과 상호참조를 위해 필요한 경우가 있을 수 있습니다. 10g는 이를 위해 OS_PROCESS 컬럼을 제공합니다.
TRANSACTIONID. 이번에는 정말 중요한 정보를 소개할 차례입니다. 사용자가 아래와 같은 쿼리를 실행하는 경우를 가정해 봅시다:
update CLASS set size = 10 where class_id = 123;
commit;
위 명령은 트랜잭션으로서의 조건을 만족하며, 따라서 명령에 대한 감사 기록이 생성됩니다. 그렇다면, 실제로 생성된 감사 기록을 어떻게 확인할 수 있을까요? TRANSACTIONID 컬럼에 저장된 transaction id와 FLASHBACK_TRANSACTION_QUERY 뷰를 조인하면 됩니다. FLASHBACK_TRANSACTION_QUERY 뷰를 조회하는 간단한 예가 다음과 같습니다:
select start_scn,  start_timestamp, 
   commit_scn, commit_timestamp, undo_change#, row_id, undo_sql
from flashback_transaction_query
where xid = '<the transaction id>';
10g에서는 트랜잭션에 관련한 일반적인 정보 이외에도 undo change#와 rowid 등을 추가로 저장합니다. 트랜잭션 변경의 undo를 위한 SQL 쿼리는 UNDO_SQL 컬럼에, 변경된 로우의 rowid 값은 ROW_ID 컬럼에 저장됩니다.
System Change Number. 변경 전의 값을 저장하기 위해서 어떤 방법을 사용하고 계십니까? Oracle9i의 FGA를 이용하는 경우, flashback 쿼리를 통해 변경 전의 값을 알 수 있습니다. 이 작업을 위해서는 변경 작업에 사용된 SCN을 알고 있어야 하며, 이 정보는 감사 기록의 System Change Number 컬럼에 저장됩니다. 아래와 같은 쿼리를 사용하면 변경 전의 값을 확인할 수 있습니다.
select size from class as of SCN 123456
where where class_id = 123;
This will show what the user saw or what the value was prior to the change.
DB 감사 기능의 확장
처음에 우리가 제기했던 문제가 무엇인지 다시 상기해 보겠습니다. 표준 감사(standard auditing)을 통해 캡처되지 않던 정보들인 사용자가 실행한 SQL 구문과 바인드 변수들이 문제였습니다. Oracle Database 10g에서는 초기화 매개변수의 간단한 변경 작업만으로 이러한 목적을 달성할 수 있습니다. 매개변수 파일에 아래 항목을 삽입하면 됩니다.
audit_trail = db_extended
이 매개변수는 SQL텍스트와 컬럼에 사용된 변수 값의 기록을 활성화합니다. 이 기능은 10g 이전 버전에서는 지원되지 않았습니다.
트리거가 필요한 경우
롤백된 결과의 취소. 감사 기록은 원본 트랜잭션으로부터 파생된 autonomous transaction을 통해 생성됩니다. 그렇기 때문에 원본 트랜잭션이 롤백 되는 경우에도 감사 기록은 커밋됩니다.
간단한 예를 들어 설명해 보겠습니다. CLASS 테이블의 UPDATE 작업에 대한 감사를 수행하는 경우를 가정해 봅시다. 사용자가 SIZE 컬럼의 값을 20을 10으로 변경하는 구문을 실행한 뒤 아래와 같이 롤백했습니다:
update class set size = 10 where class_id = 123;
rollback
결국 SIZE 컬럼은 (10이 아닌) 20의 값을 갖게 됩니다. 하지만 감사 기록에는 (롤백이 수행되었음에도) 변경 작업이 수행된 것처럼 기록이 됩니다. 사용자가 롤백을 수행하는 빈도가 높은 경우, 이러한 현상으로 인해 곤란한 문제가 생길 수 있습니다. 이러한 경우라면 커밋된 변경내역만을 캡처하기 위해 트리거를 사용할 수 밖에 없습니다. 즉 사용자 정의된 audit trail에 기록하도록 CLASS테이블에 대한 트리거가 있다면, CLASS 테이블에 대한 변경 작업이 롤백 되는 경우, 설정된 트리거 역시 롤백 됩니다.
변경 전 값의 캡처. 오라클이 제공하는 감사 기록은 변경 이전/이후의 값을 제공하지 않습니다. 예를 들어, 위에서 예로 든 변경 작업에 대한 감사기록은 실행된 구문과 SCN 넘버를 저장하고 있지만 변경 전 값(20)은 저장하지 않습니다. SCN 넘버를 이용한 flashback 쿼리를 통해 이전 값을 확인할 수 있지만, 이것도 해당 정보가 undo 세그먼트에 저장되어 있는 경우에만 가능합니다. 해당 정보가 undo_retention period 설정된 보관 기간을 초과한 경우, 이전 값을 조회할 수 있는 방법은 없습니다. 트리거를 이용하면 undo_retention period의 설정값에 관계없이 변경전 값을 영구적으로 저장할 수 있습니다.
통합 감사 기록
FGA와 표준 감사(standard auditing) 기능은 서로 유사한 형태의 정보를 제공하므로, 이 두 가지를 함께 사용함으로써 매우 다양한 정보를 얻을 수 있습니다. Oracle Database 10g는 두 종류의 정보를 통합한 DBA_COMMON_AUDIT_TRAIL 뷰를 제공합니다. 이 뷰는 DBA_AUDIT_TRAIL 뷰와 DBA_FGA_AUDIT_TRAIL 뷰의 UNION ALL로 구성됩니다. (하지만 두 감사 작업이 제공하는 정보의 성격에 분명한 차이가 있는 것도 사실입니다.)
결론
10g로 오면서, 감사 기능은 단순한 “액션 레코더(action recorder)” 또는 “패스트 리코딩 메커니즘(fast-recording mechanism)”에서 매우 상세한 수준의 사용자 액티비티를 저장하는 수단으로 변화되었으며, 이 기능을 활용하여 수작업으로 수행되던 트리거 기반 감사 작업을 최소화할 수 있습니다. 또 표준 감사(standard auditing)과 FGA의 감사 결과를 통합함으로써, 데이타베이스 액세스에 대한 추적이 한층 용이해졌습니다.
더욱 자세한 정보는, Oracle Database Security Guide 10g Release 1 (10.1) 문서의 제 11 장을 참고하시기 바랍니다.
 
 
 
 
제 11 주
Wait 인터페이스
10g의 wait 인터페이스는 ADDM에 의해 아직 캡처 되지 않은 현 시점의 성능 문제를 진단하고 해결하기 위한 유용한 정보를 제공합니다.
"데이타베이스가 너무 느리네요!"
성능에 불만을 가진 사용자들은 이렇게 말하곤 합니다. 여러분들도 필자와 같다면, DBA로 근무하면서 이런 이야기를 수없이 들어왔을 것입니다.
그렇다면 성능 문제를 해결하기 위해 어떻게 대처하십니까? 사용자의 불만을 무시하는 방법도 있겠지만(대부분의 DBA에게 이런 사치는 허용되지 않으므로), 데이타베이스 내부 또는 외부에 세션 wait 이벤트가 발생했는지 확인하는 것이 첫 번째로 하는 일이 될 것입니다.
오라클은 이를 위해 단순하면서도 강력한 메커니즘을 제공합니다. V$SESSION_WAIT 뷰가 바로 그것입니다. V$SESSION_WAIT 뷰를 통해 세션이 대기 중인 이벤트가 무엇이고 얼마나 자주, 얼마나 오랫동안 대기했는지 등의 정보를 확인할 수 있습니다. 예를 들어 특정 세션이 “db file sequential read” 이벤트를 대기하고 있는 경우, 컬럼 P1과 P2는 세션이 대기 중인 블록의 file_id와 block_id를 각각 표시하게 됩니다. 대부분의 경우, V$SESSION_WAIT 뷰만으로 wait 이벤트에 관련한 충분한 정보를 확인할 수 있습니다. 하지만 다음과 같은 두 가지 약점이 있습니다:
For most wait events this view is sufficient, but it is hardly a robust tuning tool for at least two important reasons:
  • V$SESSION_WAIT 뷰는 현 시점의 상태만을 표시합니다. wait 이벤트가 종료되면, 그 히스토리 정보 역시 소실되어 버립니다. 또 V$SESSION_EVENT 뷰는 세션이 시작된 이후의 누적된 정보를 제공하지만 그 정보가 상세하지 않습니다.
  • V$SESSION_WAIT 뷰는 wait 이벤트에 대한 정보만을 포함하고 있습니다. 그 밖의 다른 정보(userid, terminal 등)를 확인하려면 V$SESSION 뷰와 조인해야 합니다.
Oracle Database 10g는 최소한의 작업으로 보다 많은 정보를 얻을 수 있도록 wait 인터페이스를 혁신적으로 개선하였습니다. 이번 연재에서는, wait 인터페이스와 관련하여 Oracle Database 10g가 제공하는 새로운 기능을 소개하고 성능 문제 해결을 위한 활용 방안을 설명합니다. 대부분의 경우 ADDM(Automatic Database Diagnostic Manager)이 성능 분석 툴로 활용되겠지만, ADDM에 의해 아직 수집되지 않은 현 시점의 성능 문제를 분석하는 데에는 wait 인터페이스가 유용합니다.
Session Wait 뷰의 기능 향상
먼저 V$SESSION_WAIT의 개선된 기능에 대해 예를 통해 설명하도록 하겠습니다.
사용자의 세션이 갑자기 느려졌다는 불만이 제기된 경우를 가정해 봅시다. 먼저 사용자 세션의 SID를 확인하고, 해당 SID에 대해 V$SESSION_WAIT 뷰를 조회합니다. 그 결과가 아래와 같습니다:
SID                      : 269
SEQ#                     : 56
EVENT                    : enq: TX - row lock contention
P1TEXT                   : name|mode
P1                       : 1415053318
P1RAW                    : 54580006
P2TEXT                   : usn<<16 | slot
P2                       : 327681
P2RAW                    : 00050001
P3TEXT                   : sequence
P3                       : 43
P3RAW                    : 0000002B
WAIT_CLASS_ID            : 4217450380
WAIT_CLASS#              : 1
WAIT_CLASS               : Application
WAIT_TIME                : -2
SECONDS_IN_WAIT          : 0
STATE                    : WAITED UNKNOWN TIME
굵은 글씨체로 표시된 컬럼을 주목하시기 바랍니다. WAIT_CLASS_ID, WAIT_CLASS#, WAIT_CLASS 등은 모두 10g에서 새로 추가된 컬럼들입니다. WAIT_CLASS는 wait의 유형을 구분해 주는 컬럼으로, idle한 wait이므로 무시해도 되는지 또는 유효한 wait인지를 판단하는 중요한 기준으로 활용됩니다. 위의 예에서는 wait class가 “Application”으로 표시되고 있으며, 따라서 주목할 만한 이유가 충분한 것으로 판단할 수 있습니다.
이러한 컬럼들은 튜닝 과정에서 매우 요긴하게 활용됩니다. 예를 들어 다음과 같은 쿼리를 작성하여 세션 wait에 관련한 정보를 얻을 수 있습니다.
select wait_class, event, sid, state, wait_time, seconds_in_wait
from v$session_wait
order by wait_class, event, sid
/
쿼리 실행 결과의 예가 다음과 같습니다:
WAIT_CLASS  EVENT                       SID STATE                WAIT_TIME SECONDS_IN_WAIT
----------  -------------------- ---------- ------------------- ---------- ---------------
Application enq: TX -                   269 WAITING                      0              73
            row lock contention        
Idle        Queue Monitor Wait          270 WAITING                      0              40
Idle        SQL*Net message from client 265 WAITING                      0              73
Idle        jobq slave wait             259 WAITING                      0            8485
Idle        pmon timer                  280 WAITING                      0              73
Idle        rdbms ipc message           267 WAITING                      0          184770
Idle        wakeup time manager         268 WAITING                      0              40
Network     SQL*Net message to client   272 WAITED SHORT TIME           -1               0
여기 몇 가지 이벤트(Queue Monitor Wait, JobQueue Slave등)가 “Idle” 이벤트로 표시되고 있음을 확인할 수 있습니다. 이러한 항목들은 nonblocking wait로 간주하고 고려 대상에서 제외할 수도 있습니다. 하지만 “idle” 이벤트가 다른 문제를 암시하는 경우도 있습니다. 예를 들어, “idle”로 표시되는 SQL*Net 관련 이벤트는 네트워크 지연율이 높음을 나타낼 수도 있습니다.
WAIT_TIME이 -2의 값을 갖는 경우도 참고할 필요가 있습니다. Windows와 같은 일부 플랫폼은 fast timing mechanism을 지원하지 않습니다. 해당 플랫폼에 초기화 매개변수 TIMED_STATISTICS 가 설정되어 있지 않은 경우, 정확한 시간 통계를 얻을 수 없습니다. 이 경우 Oracle9i에서는 관련된 컬럼에 비정상적으로 높은 숫자가 표시되며, 이로 인해 성능 문제의 진단이 더욱 어려워질 수 있습니다. 10g에서는 이러한 경우 일률적으로 -2의 값을 표시합니다. 즉, -2는 플랫폼이 fast timing mechanism을 지원하지 않으며 TIMED_STATISTICS 매개변수가 설정되어 있지 않음을 의미합니다. (이 문서의 뒷부분에서는 fast timing mechanism이 사용 가능한 것으로 가정하고 설명을 진행합니다.)
Session 뷰와 Session Wait 뷰의 통합
세션에 대한 자세한 정보를 얻기 위해서 V$SESSION_WAIT를 V$SESSION과 조인해야만 했던 사실을 기억하고 계실 겁니다. 10g에서는 V$SESSION 뷰 안에 V$SESSION_WAIT의 정보가 기본적으로 포함됩니다. V$SESSION이 wait 이벤트에 관련하여 추가로 제공하는 컬럼이 아래와 같습니다:
EVENT#                     NUMBER
EVENT                      VARCHAR2(64)
P1TEXT                     VARCHAR2(64)
P1                         NUMBER
P1RAW                      RAW(4)
P2TEXT                     VARCHAR2(64)
P2                         NUMBER
P2RAW                      RAW(4)
P3TEXT                     VARCHAR2(64)
P3                         NUMBER
P3RAW                      RAW(4)
WAIT_CLASS_ID              NUMBER
WAIT_CLASS#                NUMBER
WAIT_CLASS                 VARCHAR2(64)
WAIT_TIME                  NUMBER
SECONDS_IN_WAIT            NUMBER
STATE                      VARCHAR2(19)
이 컬럼들은 V$SESSION_WAIT에서 제공되는 것들과 동일한 이름을 가지며 동일한 정보를 제공합니다. 결국 이벤트에 대한 세션 wait 정보를 확인하기 위해 V$SESSION_WAIT를 따로 조회할 필요가 없게 된 것입니다.
그럼 다시 처음에 설명한 예로 돌아가 봅시다. SID 269의 세션이 “enq: TX - row lock contention”이벤트를 대기 중이며, 이는 이 세션이 다른 세션이 점유하고 있는 락(lock)을 대기 중임을 의미합니다. 문제를 진단하기 위해서는 “다른” 세션이 무엇인지 확인해야 합니다. 그렇다면 어떤 방법을 써야 할까요?
Oracle9i와 이전 버전에서는 매우 복잡한 쿼리를 실행해야만 락을 소유한 세션의 SID를 알아낼 수 있었습니다. 10g에서는 다음과 같은 간단한 쿼리로 해결 가능합니다:
select BLOCKING_SESSION_STATUS, BLOCKING_SESSION
from v$session 
where sid = 269

BLOCKING_SE BLOCKING_SESSION
----------- ----------------
VALID                    265
이렇게 간단한 작업만으로 SID 265 세션이 세션 269를 블로킹하고 있음이 확인되었습니다.
얼마나 많은 Wait가 발생했는가?
아직도 사용자를 만족시키기에는 이릅니다. 사용자의 세션이 느려진 이유는 도대체 무엇일까요? 다음과 같은 쿼리를 먼저 실행해 봅시다:
select * from v$session_wait_class where sid = 269;
결과는 다음과 같이 표시됩니다:
SID SERIAL# WAIT_CLASS_ID WAIT_CLASS# WAIT_CLASS    TOTAL_WAITS TIME_WAITED
---- ------- ------------- ----------- ------------- ----------- -----------
 269    1106    4217450380           1 Application           873      261537
 269    1106    3290255840           2 Configuration           4           4
 269    1106    3386400367           5 Commit                  1           0
 269    1106    2723168908           6 Idle                   15      148408
 269    1106    2000153315           7 Network                15           0
 269    1106    1740759767           8 User I/O               26           1
위 결과는 세션 wait에 대한 매우 상세한 정보를 제공하고 있습니다. 위 결과를 통해 애플리케이션에 관련한 세션 wait 이벤트가 873회, 총 261,537 centi-second에 걸쳐 발생되었으며, 네트워크 관련한 wait 이벤트가 15회 발생되었음을 확인할 수 있습니다.
이 방법을 응용하여, wait class에 관련한 시스템의 전반적인 통계를 확인할 수도 있습니다. (여기에서도 시간은 centi-second 단위로 표시됩니다.)
select * from v$system_wait_class;

WAIT_CLASS_ID WAIT_CLASS# WAIT_CLASS    TOTAL_WAITS TIME_WAITED
------------- ----------- ------------- ----------- -----------
   1893977003           0 Other                2483       18108
   4217450380           1 Application          1352      386101
   3290255840           2 Configuration          82         230
   3875070507           4 Concurrency            80         395
   3386400367           5 Commit               2625        1925
   2723168908           6 Idle               645527   219397953
   2000153315           7 Network              2125           2
   1740759767           8 User I/O             5085        3006
   4108307767           9 System I/O         127979       18623
대부분의 성능 문제는 독립적으로 존재하지 않으며, 문제의 패턴을 암시하는 단서로서 해석되는 것이 일반적입니다. 이러한 패턴을 확인하려면 wait class의 히스토리 뷰를 조회해야 합니다:
select * from v$waitclassmetric;
V$WAITCLASSMETRIC 뷰는 지난 1분 동안의 wait class 통계를 제공합니다.
select wait_class#, wait_class_id, 
average_waiter_count "awc", dbtime_in_wait,
time_waited,  wait_count
from v$waitclassmetric
/

WAIT_CLASS# WAIT_CLASS_ID  AWC DBTIME_IN_WAIT TIME_WAITED WAIT_COUNT
----------- ------------- ---- -------------- ----------- ----------
          0    1893977003    0              0           0          1
          1    4217450380    2             90        1499          5
          2    3290255840    0              0           4          3
          3    4166625743    0              0           0          0
          4    3875070507    0              0           0          1
          5    3386400367    0              0           0          0
          6    2723168908   59              0      351541        264
          7    2000153315    0              0           0         25
          8    1740759767    0              0           0          0
          9    4108307767    0              0           8        100
         10    2396326234    0              0           0          0
         11    3871361733    0              0           0          0
WAIT_CLASS_ID 컬럼을 주목하시기 바랍니다. 지난 1분 동안 4217450380의 WAIT_CLASS_ID 값을 갖는 wait class를 2개의 세션이 총 5회, 1,499 centi-second에 걸쳐 대기하였음을 알 수 있습니다. 그렇다면 이 wait class는 무엇일까요? V$SYSTEM_WAIT_CLASS를 조회하면 이 wait class가 바로 "Application” class임을 확인할 수 있습니다.
DBTIME_IN_WAIT 컬럼도 매우 유용하게 활용됩니다. Automatic Workload Repository(AWR)를 주제로 한 제 6 주 연재에서, 10g가 한층 세분화된 시간 통계를 제공하며, 데이타베이스 내부에서 실제로 사용된 시간을 정확하게 확인할 수 있음을 설명한 바 있습니다. DBTIME_IN_WAIT가 바로 데이타베이스 내부에서 사용된 시간을 표시하는 컬럼입니다.
단서는 항상 남는다
해당 사용자의 세션이 종료되자 DBA는 안도의 한숨을 내쉽니다. 하지만 도대체 어떤 wait 때문에 세션에 성능 문제가 발생했는지에 대한 궁금증이 가시지 않습니다. V$SESSION_WAIT를 조회하면 쉽게 해답을 얻을 수 있겠지만, 이제 wait 이벤트가 종료된 상황이므로 이 뷰에는 더 이상 기록이 남아있지 않을 것입니다. 이런 경우 어떻게 하시겠습니까?
10g는 액티브 세션의 마지막 10개 이벤트에 관련한 session wait 히스토리 정보를 자동 저장하고 관리하며, 이 결과는 V$SESSION_WAIT_HISTORY 뷰를 통해 조회할 수 있습니다:
select event, wait_time, wait_count
from v$session_wait_history
where sid = 265
/

EVENT                           WAIT_TIME WAIT_COUNT
------------------------------ ---------- ----------
log file switch completion              2          1
log file switch completion              1          1
log file switch completion              0          1
SQL*Net message from client         49852          1
SQL*Net message to client               0          1
enq: TX - row lock contention          28          1
SQL*Net message from client           131          1
SQL*Net message to client               0          1
log file sync                           2          1
log buffer space                        1          1
세션이 inactive 상태가 되거나 연결이 끊어진 경우, 관련된 기록도 뷰에서 삭제됩니다. 하지만 히스토리 정보는 AWR 테이블에 별도로 저장됩니다. AWR의 V$ACTIVE_SESSION_HISTORY 뷰는 session wait에 관련한 정보를 제공합니다. (AWR에 관련한 자세한 정보는 본 시리즈의 제 6 주 연재를 참고하시기 바랍니다.)
결론
Oracle Database 10g의 향상된 wait 모델을 사용하여 한층 쉽게 성능 분석 작업을 진행할 수 있습니다. 세션 히스토리 정보를 이용하면 문제의 징후가 사라진 이후에도 문제 원인을 진단할 수 있습니다. 또 wait를 wait class로 구분함으로써, 각 wait 유형별 영향을 이해하고 효율적으로 대처할 수 있게 됩니다.
wait 이벤트 관련 다이내믹 성능 뷰에 대한 보다 자세한 정보는 Oracle Database Performance Tuning Guide 10g Release 1 (10.1)제 10 장을 확인하시기 바랍니다.
 
 
 
 
제 12 주
Materialized Views
10g에서는 강제적인 query rewrite, tuning advisor 등의 새로운 기능을 통해 materialized view의 관리 기능을 향상시켰습니다.
스냅샷(snapshot)이라 불리기도 하는 Materialized view(MV)는 오라클에서 오래 전부터 구현해 온 기능의 하나입니다. MV는 쿼리 결과를 별도 세그먼트에 저장하고, 쿼리가 재실행되는 경우 사용자에게 미리 저장된 결과를 전달함으로써 쿼리를 여러 차례 재실행하는 데 따르는 성능적인 부담을 줄여줍니다. MV는 데이타 웨어하우스 환경에서 특히 유용합니다. 또 “fast refresh” 메커니즘을 이용해 MV를 전체적으로 또는 부분적으로 refresh할 수 있습니다.
다음과 같은 materialized view를 구현한 경우를 가정해 봅시다:
create materialized view mv_hotel_resv
refresh fast
enable query rewrite
as
select distinct city, resv_id, cust_name
from hotels h, reservations r 
where r.hotel_id = h.hotel_id';
어떻게 하면 이 MV가 완벽하게 동작하는데 필요한 오브젝트들이 모두 생성되었는지 확인할 수 있을까요? Oracle Database 10g 이전 버전에서는, DBMS_MVIEW 패키지의 EXPLAIN_MVIEW 프로시저와 EXPLAIN_REWRITE 프로시저를 이용해 이를 확인할 수 있었습니다. 이 프로시저들은 10g에서도 여전히 사용 가능합니다. 이 프로시저들을 이용하면 특정 MV에 “fast refreshability”, “query rewritability” 등의 기능 구현 여부를 정확하게 확인할 수 있었지만, 어떻게 하면 이러한 기능을 구현할 수 있는지에 대한 조언을 구할 수는 없었습니다. 이를 위해서는, 각각의 MV 구조를 비주얼한 방법으로 확인해야만 했으며, 실질적으로 이 방법은 효용성이 없었습니다.
10g에서는 새로 추가된 DBMS_ADVISOR 패키지에 포함된 TUNE_MVIEW 프로시저를 통해 이러한 작업을 간단하게 수행할 수 있습니다. 먼저 IN 매개변수에 MV 생성 스크립트의 텍스트를 저장한 후 패키지를 호출합니다. 프로시저는 자동 생성된 이름을 가진 Advisor Task를 생성하며, OUT 매개변수를 통해 사용자에게 생성된 Advisor의 이름을 반환합니다.
예를 들어 설명해 보겠습니다. 먼저 SQL*Plus에서 OUT 매개변수에 저장할 변수를 정의해야 합니다.
SQL> -- first define a variable to hold the OUT parameter
SQL> var adv_name varchar2(20)
SQL>  begin
  2  dbms_advisor.tune_mview
  3     (
  4        :adv_name,
  5        'create materialized view mv_hotel_resv refresh fast enable query rewrite as
            select distinct city, resv_id, cust_name from hotels h, 
	    reservations r where r.hotel_id = h.hotel_id');
  6* end;
다음에는 adv_name 변수를 조회하여 Advisor의 이름을 확인합니다.
SQL> print adv_name

ADV_NAME
-----------------------
TASK_117
다음으로, DBA_TUNE_MVIEW 뷰를 질의하여 Advisor가 제공하는 권고내역을 확인합니다. 이 명령을 실행하기 전에 SET LONG 999999 명령을 실행하는 것을 잊지 마시기 바랍니다. (DBA_TUNE_MVIEW 뷰의 STATEMENT 컬럼은 CLOB 데이타타입을 사용하므로, 문자 출력을 80개로 제한하는 디폴트 환경을 수정해야 합니다.)
select script_type, statement 
from   dba_tune_mview 
where  task_name = 'TASK_117' 
order  by script_type, action_id;
실행 결과는 아래와 같습니다:
SCRIPT_TYPE    STATEMENT
-------------- ------------------------------------------------------------
IMPLEMENTATION CREATE MATERIALIZED VIEW LOG ON "ARUP"."HOTELS" WITH ROWID,
               SEQUENCE ("HOTEL_ID","CITY")  INCLUDING NEW VALUES

IMPLEMENTATION ALTER MATERIALIZED VIEW LOG FORCE ON "ARUP"."HOTELS" ADD
               ROWID, SEQUENCE ("HOTEL_ID","CITY")  INCLUDING NEW VALUES

IMPLEMENTATION CREATE MATERIALIZED VIEW LOG ON "ARUP"."RESERVATIONS" WITH
               ROWID, SEQUENCE ("RESV_ID","HOTEL_ID","CUST_NAME")
               INCLUDING NEW VALUES

IMPLEMENTATION ALTER MATERIALIZED VIEW LOG FORCE ON "ARUP"."RESERVATIONS"
               ADD ROWID, SEQUENCE ("RESV_ID","HOTEL_ID","CUST_NAME")
               INCLUDING NEW VALUES

IMPLEMENTATION CREATE MATERIALIZED VIEW ARUP.MV_HOTEL_RESV   REFRESH FAST
               WITH ROWID ENABLE QUERY REWRITE AS SELECT
               ARUP.RESERVATIONS.CUST_NAME C1, ARUP.RESERVATIONS.RESV_ID
               C2, ARUP.HOTELS.CITY C3, COUNT(*) M1 FROM ARUP.RESERVATIONS,
               ARUP.HOTELS WHERE ARUP.HOTELS.HOTEL_ID =
               ARUP.RESERVATIONS.HOTEL_ID GROUP BY
               ARUP.RESERVATIONS.CUST_NAME, ARUP.RESERVATIONS.RESV_ID,
               ARUP.HOTELS.CITY

UNDO           DROP MATERIALIZED VIEW ARUP.MV_HOTEL_RESV
SCRIPT_TYPE 컬럼은 제공되는 조언의 유형을 의미합니다. 대부분의 내용이 실제 구현에 관련된 권고사항으로 구성되어 있으므로 SCRIPT_TYPE으로 “IMPLEMENTATION”이 설정된 것을 확인할 수 있습니다. 사용자가 승인하는 경우, 제시된 권고사항은 ACTION_ID 컬럼에 정의된 순서대로 실행됩니다.
Advisor가 제시한 권고사항을 자세히 살펴보면, 우리가 비주얼 분석 작업을 통해 생성하는 것과 유사한 내용으로 구성되어 있다는 사실을 알 수 있을 것입니다. 위에서 제시된 권고사항은 매우 논리적입니다. Fast refresh를 구현하려면 베이스 테이블MATERIALIZED VIEW LOG가 생성되어야 하며, 이 때 “including new values”와 같은 조건을 포함하여야 합니다. STATEMENT 컬럼은 이러한 권고사항을 구현하기 위해 실행되는 SQL 구문을 담고 있습니다.
구현 작업의 마지막 단계로서, Advisor는 MV 생성 과정에서 수정이 필요한 부분을 지적해 줍니다. 위의 예에서 무엇이 달라졌는지 확인해 보시기 바랍니다. count(*)가 MV에 추가되었습니다. MV가 “fast refresh”를 지원하는 것으로 설정했기 때문에 count(*)가 반드시 포함되어야 하며, Advisor는 이 부분이 생략된 것을 발견하고 권고사항에 포함시킨 것입니다.
프로시저 TUNE_MVIEWEXPLAIN_MVIEWEXPLAIN_REWRITE가 제공하는 것보다 수준 높은 조언을 제공하며, MV를 생성하는 보다 쉽고 효율적인 방법도 함께 제시합니다. 때로 Advisor는 질의를 보다 효율적으로 하기 위한 방법으로 하나 이상의 MV를 제안하기도 합니다.
어떤 분은 그게 과연 얼마나 유용할까라고 의문을 던질 수도 있을 것입니다. 숙련된 DBA라면 누구나 MV 생성 스크립트에서 빠진 부분을 찾아내어 직접 수정할 수 있기 때문입니다. 사실 Advisor가 하는 역할이 바로 이것입니다. Advisor는 마치 숙련된 DBA처럼 전문적인 권고를 제시합니다. 한 가지 분명하게 다른 점은, Advisor가 돈을 안받고 일할 뿐 아니라 휴가나 월급인상을 요구하지도 않는다는 사실입니다. 이러한 기능이 있음으로 해서, 선임 DBA가 하급 DBA에게 반복적인 업무를 인계하고 보다 전략적인 목표에 집중할 수 있게 되는 것입니다.
TUNE_MVIEW 프로시저를 실행할 때 Advisor name을 미리 지정해서 매개변수를 통해 전달할 수도 있습니다. 이렇게 하는 경우 Advisor는 자동 생성된 이름 대신 사용자가 지정한 이름을 사용합니다.
쉬워진 구현 작업
이제 권고사항을 확인하고 바로 구현 작업에 들어갈 준비가 되었습니다. 위 실행결과의 STATEMENT 컬럼을 조회한 결과를 별도 스크립트 파일에 스풀링한 다음, 그 파일을 실행하는 것도 한 방법입니다. 아니면 그보다 쉬운 방법으로 별도 제공되는 프로시저를 실행할 수도 있습니다:
begin
   dbms_advisor.create_file (
      dbms_advisor.get_task_script ('TASK_117'),  
   'MVTUNE_OUTDIR',
   'mvtune_script.sql'
);
end;
/
이 프로시저는 아래와 같은 방법으로 디렉토리 오브젝트가 미리 생성되어 있음을 가정합니다:
create directory mvtune_outdir as '/home/oracle/mvtune_outdir';
dbms_advisor를 호출하면 /home/oracle/mvtune_outdir 디렉토리에 mvtune_script.sql이라는 이름의 파일이 생성됩니다. 이 파일을 열어 보면 다음과 같은 내용이 포함되어 있음을 확인할 수 있을 것입니다:
Rem  SQL Access Advisor: Version 10.1.0.1 - Production
Rem
Rem  Username:        ARUP
Rem  Task:            TASK_117
Rem  Execution date:
Rem

set feedback 1
set linesize 80
set trimspool on
set tab off
set pagesize 60

whenever sqlerror CONTINUE

CREATE MATERIALIZED VIEW LOG ON
    "ARUP"."HOTELS"
    WITH ROWID, SEQUENCE("HOTEL_ID","CITY")
    INCLUDING NEW VALUES;

ALTER MATERIALIZED VIEW LOG FORCE ON
    "ARUP"."HOTELS"
    ADD ROWID, SEQUENCE("HOTEL_ID","CITY")
    INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW LOG ON
    "ARUP"."RESERVATIONS"
    WITH ROWID, SEQUENCE("RESV_ID","HOTEL_ID","CUST_NAME")
    INCLUDING NEW VALUES;

ALTER MATERIALIZED VIEW LOG FORCE ON
    "ARUP"."RESERVATIONS"
    ADD ROWID, SEQUENCE("RESV_ID","HOTEL_ID","CUST_NAME")
    INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW ARUP.MV_HOTEL_RESV
    REFRESH FAST WITH ROWID
    ENABLE QUERY REWRITE
    AS SELECT ARUP.RESERVATIONS.CUST_NAME C1, ARUP.RESERVATIONS.RESV_ID C2, ARUP.HOTELS.CITY
       C3, COUNT(*) M1 FROM ARUP.RESERVATIONS, ARUP.HOTELS WHERE ARUP.HOTELS.HOTEL_ID
       = ARUP.RESERVATIONS.HOTEL_ID GROUP BY ARUP.RESERVATIONS.CUST_NAME, ARUP.RESERVATIONS.RESV_ID,
       ARUP.HOTELS.CITY;

whenever sqlerror EXIT SQL.SQLCODE

begin
  dbms_advisor.mark_recommendation('TASK_117',1,'IMPLEMENTED');
end;
/
이 파일은 권고사항을 구현하기 위해 필요한 모든 정보를 포함하고 있으며, 수작업으로 파일을 생성하는 수고를 덜어줍니다. DBA 로봇이 다시 한 번 그 위력을 발휘하는 순간입니다.
Rewrite이 불가능한 경우의 실행 차단
이제 여러분들도 Query Rewrite 기능이 얼마나 중요하고 유용한지 깨닫게 되셨으리라 믿습니다. Query Rewrite는 I/O 작업과 프로세싱 작업을 줄여주고 결과를 한층 빠르게 얻을 수 있게 합니다.
위의 예를 기준으로 계속 설명해 보겠습니다. 사용자가 다음과 같은 쿼리를 실행하는 경우를 생각해 봅시다:
Select city, sum(actual_rate)
from hotels h, reservations r, trans t
where t.resv_id = r.resv_id
and h.hotel_id = r.hotel_id
group by city;
실행 통계는 아래와 같이 확인되었습니다:
0   recursive calls
0   db block gets
6   consistent gets
0   physical reads
0   redo size
478 bytes sent via SQL*Net to client
496 bytes received via SQL*Net from client
2   SQL*Net roundtrips to/from client
1   sorts (memory)
0   sorts (disk)
consistent gets가 6의 값을 갖는 것에 주목하시기 바랍니다. 이것은 매우 낮은 수치입니다. 이는 3 개의 테이블을 기반으로 생성된 2개의 MV를 이용하도록 쿼리가 재작성 되었기 때문에 얻어진 결과입니다. Select 작업은 테이블이 아닌 MV를 통해 수행되었으며, 그 덕분에 훨씬 적은 I/O와 CPU를 사용했습니다.
하지만 Query Rewrite가 실패하면 어떻게 될까요? 실패의 이유에는 여러 가지가 있을 수 있습니다. 초기화 매개변수 query_rewrite_integrity이 “TRUSTED”로 설정되고 MV status가 “STALE”로 설정되었다면 쿼리는 재작성 되지 않을 것입니다. 쿼리를 실행하기 전에 세션 매개변수를 설정함으로써 이 현상을 테스트해 볼 수 있습니다:
alter session set query_rewrite_enabled = false;
위 명령을 실행하고 나면, explain plan은 MV가 아닌 3개 테이블로부터 select를 수행하는 것으로 변경됩니다. 실행 통계도 아래와 같이 달라지게 됩니다:
0   recursive calls
0   db block gets
16  consistent gets
0   physical reads
0   redo size
478 bytes sent via SQL*Net to client
496 bytes received via SQL*Net from client
2   SQL*Net roundtrips to/from client
2   sorts (memory)
0   sorts (disk)
consistent gets의 값이 6에서 16으로 크게 증가했음을 확인할 수 있습니다. 추가 자원을 활용하기 어려운 실제 환경에서는, 이러한 변화를 용납하기는 어려우며, 따라서 쿼리를 재작성 하는 방법을 선택할 수 밖에 없습니다. 이러한 경우 쿼리가 재작성 되는 경우에만 실행 가능하도록 설정해야 할 수도 있습니다.
Oracle9i Database와 그 이전 버전에서는 단 한 가지 옵션만 가능했습니다. Query Rewrite를 disable할 수는 있었지만 베이스 테이블에 대한 액세스는 막을 수 없었습니다. 반면 Oracle Database 10gREWRITE_OR_ERROR라는 힌트를 이용하여 베이스 테이블에 대한 접근을 차단하는 기능을 제공합니다. 위의 쿼리는 아래와 같은 형태로 재작성될 수 있습니다:
select /*+ REWRITE_OR_ERROR */ city, sum(actual_rate)
from hotels h, reservations r, trans t
where t.resv_id = r.resv_id
and h.hotel_id = r.hotel_id
group by city;
에러 메시지는 아래와 같이 표시됩니다:
from hotels h, reservations r, trans t
     *
ERROR at line 2:
ORA-30393: a query block in the statement did not rewrite
ORA-30393은 구문이 MV를 사용할 수 있는 형태로 재작성될 수 없으며, 이로 인해 구문 실행에 실패하였음을 의미하는 에러입니다. 이 방법을 사용하여 시스템 리소스를 많이 잡아먹는 쿼리를 사전에 차단할 수 있습니다. 다만 한 가지 주의할 점은, MV 중 (전체가 아닌) 하나만이라도 이용이 가능한 경우 쿼리가 실행된다는 사실입니다. 예를 들어 MV_ACTUAL_SALES은 사용할 수 있지만 MV_HOTEL_RESV는 사용할 수 없는 경우라 해도, 쿼리는 재작성되고 실행되며, 에러는 발생하지 않습니다. 이 경우 execution plan은 아래와 같습니다:
Execution Plan
----------------------------------------------------------
0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=11 Card=6 Bytes=156)
1    0   SORT (GROUP BY) (Cost=11 Card=6 Bytes=156)
2    1     HASH JOIN (Cost=10 Card=80 Bytes=2080)
3    2       MERGE JOIN (Cost=6 Card=80 Bytes=1520)
4    3         TABLE ACCESS (BY INDEX ROWID) OF 'HOTELS' (TABLE) (Cost=2 Card=8 Bytes=104)
5    4           INDEX (FULL SCAN) OF 'PK_HOTELS' (INDEX (UNIQUE)) (Cost=1 Card=8)
6    3         SORT (JOIN) (Cost=4 Card=80 Bytes=480)
7    6           TABLE ACCESS (FULL) OF 'RESERVATIONS' (TABLE) (Cost=3 Card=80 Bytes=480)
8    2       MAT_VIEW REWRITE ACCESS (FULL) OF 'MV_ACTUAL_SALES' (MAT_VIEW REWRITE) (Cost=3 Card=80 Bytes=560)
이 쿼리는 MV_ACTUAL_SALES은 사용하지만 MV_HOTEL_RESV는 사용하지 않고, 대신 HOTELS 테이블과 RESERVATIONS 테이블에 직접 액세스합니다. 만일 HOTELS 또는 RESERVATIONS 테이블에 풀 테이블 스캔이 발생한다면, MV를 사용하는 경우보다 훨씬 많은 리소스를 사용하게 될 것입니다. 쿼리와 MV를 설계하는 과정에서 반드시 참고해야 할 부분입니다.
물론 Resource Manager를 이용하면 리소스 사용을 통제할 수도 있습니다. 하지만 REWRITE_OR_ERROR 힌트를 이용하면 Resource Manager가 호출되기도 전에 쿼리의 실행을 사전 차단하는 것이 가능합니다. Resource Manager는 옵티마이저 통계에 근거하여 필요한 리소스를 예측하며, 통계가 존재하지 않거나 그 정확도가 떨어지는 경우 잘못된 예측을 할 수도 있습니다. 반면 REWRITE_OR_ERROR 힌트를 이용하면 통계와 무관하게 테이블 액세스를 차단할 수 있다는 장점이 있습니다.
Explain Plan의 개선 기능
앞의 explain plan 조회결과 예에서 아래 행을 참고하시기 바랍니다:
MAT_VIEW REWRITE ACCESS (FULL) OF 'MV_ACTUAL_SALES' (MAT_VIEW REWRITE) 
MAT_VIEW REWRITE 액세스 방식은 10g에서 처음 소개되는 것입니다. 이것은 테이블 또는 세그먼트가 아닌 MV가 액세스되고 있음을 의미합니다. 이 프로시저를 사용하면 테이블과 MV 중 어느 쪽이 사용되고 있는지를 바로 확인할 수 있습니다.
결론
10g에 새로 추가된tuning advisor의 강력한 권고 기능을 이용하여 MV를 더 쉽게 관리할 수 있습니다. 필자는 개인적으로, 튜닝 권고내역이 바로 실행 가능한 형태의 완성된 스크립트로 제공된다는 점이 마음에 듭니다. 리소스를 최대한 아껴야 하는 의사결정 시스템에서는, REWRITE_OR_ERROR 힌트를 사용하여 쿼리 재작성이 불가능한 경우 실행 자체를 차단하는 방법이 매우 유용하게 사용될 수 있습니다.
10g에서 MV를 관리하는 방법에 대한 보다 자세한 정보는 Oracle Database Data Warehousing Guide 10g Release 1 (10.1)제 8 장을 참고하시기 바랍니다.
 
 
 
 
제 13 주
Enterprise Manager 10g
마침내, 초심자와 전문가 모두를 위해 Oracle 관리와 운영에 관한 문제를 한꺼번에 해결해 줄 수 있는 툴이 완성되었습니다.
여러분이 일상적인 DBA 업무를 위해 주로 사용하는 툴은 무엇입니까? 사용자 그룹 모임에서 똑같은 질문을 던진 적이 있었습니다.
답변은 DBA의 경력에 따라 달랐습니다. 오랜 경력의 관리자일 수록 SQL*Plus 커맨드 라인 툴을 가장 선호하였고 (필자 역시 그러합니다), 다른 이들의 경우 몇 가지 써드 파티 제품을 중심으로 의견이 갈라졌습니다. 하지만 초심자 수준의 DBA의 경우에는 Enterprise Manager(EM)을 단연 선호하는 툴로 꼽았습니다.
이러한 결과는 충분히 예상할 수 있는 것입니다. Oracle Enterprise Manager는 지난 수 년 동안 꾸준히 발전되어 왔습니다. 처음에 캐릭터 모드 디스플레이의 SQL*DBA로 시작된 EM은 클라이언트 운영체제 기반의 툴로, 그리고 최종적으로는 자바 기반 환경으로 진화되었습니다. EM은 대부분의 DBA 작업을 수행하기에 충분한 수준의 정보를 제공하며, 새로운 문법을 배우기 귀찮아하는 사용자, 또는 GUI 툴을 사용하여 사용자 추가, 데이타파일 수정, 롤백 세그먼트 점검 등의 일상적인 작업을 수행하고자 하는 관리자들을 위한 솔루션으로 자리잡았습니다.
하지만 EM이 기대만큼 빠르게 확산되지 않은 이유 중 하나는, EM이 데이타베이스 서버의 발전속도를 따라잡지 못했다는 사실에 있습니다. 예를 들어 Oracle9i Database의 EM은 (이미 Oracle8i부터 지원되던 기능인) subpartitioning을 지원하지 않았습니다..
Oracle Database 10g에 포함된 EM의 새로운 버전은 과거의 문제로부터의 결별을 선언합니다. 새로운 아키텍처와 새로운 인터페이스가 적용되었으며, 초심자와 숙련된 사용자를 모두 만족할 수 있는 강력하고도 포괄적인 DBA 툴박스를 제공합니다. 더 중요한 사실은, 추가적인 비용 없이 데이타베이스와 함께 기본으로 제공된다는 것입니다. 만일 지금 써드 파티 툴을 검토하고 있는 중이라면, 그 경쟁 제품으로서 EM을 심각하게 고려해 볼 필요가 있습니다. 여러분이 (필자처럼) 커맨드 라인 툴의 맹신자라 하더라도, EM이 여러 상황에서 매우 유용하게 사용될 수 있음을 깨닫게 될 것입니다.
이번 연재에서는 EM의 새로운 기능을 소개합니다. Enterprise Manager가 제공하는 기능의 영역이 워낙 광범위하기 때문에 전체 기능을 종합적으로 다루기는 불가능합니다. 대신, 몇 가지 기본적인 내용을 설명하고 관련 자료를 소개하는 한편, (이 시리즈의 기본 취지에 맞추어) 현실적인 문제를 해결하기 위해 툴을 활용하는 사례들을 제공하고자 합니다.
아키텍처
EM 10g는 Database 10g 소프트웨어와 함께 디폴트로 설치됩니다. 클라이언트 운영체제를 기반으로 하던 이전 버전과 달리, 새로운 버전은 (DB Console이라 불리는) HTTP 서버의 형태로 데이타베이스 서버 상에 설치됩니다 (그림 1 참고). EM 인터페이스는 모든 종류의 웹 브라우저를 지원합니다.
figure 1
그림 1: EM 아키텍처
DB Console의 포트 번호는 $ORACLE_HOME/install/portlist.ini 파일에 정의되어 있습니다. 이 파일의 예가 아래와 같습니다 (포트 번호는 설치 예에 따라 달라질 수 있습니다).
Ultra Search HTTP port number = 5620
iSQL*Plus HTTP port number = 5560
Enterprise Manager Agent Port =
Enterprise Manager Console HTTP Port (starz10) = 5500
Enterprise Manager Agent Port (starz10) = 1830
위 파일에서 데이타베이스 starz10의 Agent가 포트 1830에서, EM 콘솔이 포트 5500에서 리스닝 하고 있음을 확인할 수 있습니다. 아래 URL을 입력하면 EM 로그온 스크린을 호출할 수 있습니다:
http://starz/em/console/logon/logon
로그온 스크린에서는 DBA 사용자로서 로그인이 가능합니다. 이번 예에서는 SYS 사용자로 로그인해 보겠습니다.
메인 데이타베이스 홈 페이지
로그인 후, 메인 데이타베이스의 홈 페이지가 표시됩니다. 홈 페이지의 상단 부분은 데이타베이스의 중요 정보를 요약 형식으로 제공하고 있습니다 (그림 2 참고).
figure 2
그림 2: 메인 데이타베이스 홈 페이지 (상단)
위 그림에서 참고할 부분에 동그라미 표시를 하고 번호를 매겼습니다. 제일 먼저 “General” (1)이라 표시된 영역을 참고하시기 바랍니다. 이 영역은 데이타베이스에 관련한 가장 기초적인 정보(인스턴스 네임, 데이타베이스 시작 시간 등)를 제공합니다. Oracle Home 항목의 하이퍼링크를 클릭하면 같은 홈을 공유하는 모든 제품과 오라클 데이타베이스가 표시됩니다. Listeners 항목의 하이퍼링크는 하이퍼링크 상에 표시된 리스너를 통해 등록된 모든 데이타베이스와 인스턴스의 정보로 연결됩니다. 그리고 마지막으로 호스트 명(starz)이 표시됩니다.
"Host CPU" (2) 항목에서는 CPU 관련 정보가 간략하게 제공됩니다. "Active Sessions" (3) 항목은 액티브 세션 통계와 세션의 현재 실행 상태에 대한 정보 (4)를 제공합니다. 위 그림에서는 세션이 사용한 시간 중 99%가 wait에 사용되었음을 확인할 수 있습니다 (그 원인은 뒤에서 확인하기로 합니다). "High Availability" (5) 항목은 가용성에 관련한 정보를 제공합니다. 예를 들어 “Instant Recovery Time”은 인스턴스의 MTTR 목표, 다시 말해 인스턴스 장애 발생 시 복구(instance crash recovery)에 얼마나 많은 시간이 사용될 것인지를 의미합니다.
"Space Usage" (6) 항목을 주목하시기 바랍니다. 이 항목은 23 개의 세그먼트에 대한 경고 메시지가 존재함을 알려주고 있습니다 (경고 메시지의 내용에 대해서는 뒷부분에서 확인하기로 합니다). "Diagnostic Summary" (7)는 데이타베이스의 진단 결과를 요약하고 있습니다. “Performance Finding”의 숫자는, 10g에 새로 추가된 성능 진단 엔진인 ADDM(Automatic Database Diagnostic Monitor)이 발견한 성능 이슈의 수를 의미합니다. 또, EM은 환경을 자동으로 분석하여 베스트 프랙티스를 위반한 사례가 있는지 점검하는 기능을 제공합니다. 분석 작업의 결과는 “Policy Violation”에 표시됩니다. 마지막으로 “Alert Log”는 최근 발생한 ORA error에 대한 정보를 제공합니다. 이 정보는 매우 중요한 가치를 갖습니다. Alert log에 대한 자동 검색을 통해 Oracle 에러를 확인해 줌으로써, 수작업으로 에러 메시지를 찾는 시간을 상당 수준 절감할 수 있습니다.
그림 3은 데이타베이스 홈 페이지의 하단 영역을 보여주고 있습니다. 여기에서는 메시지의 상세한 내용을 확인할 수 있습니다. "Alerts" (1) 항목은 주목할 필요가 있는 알림(alert) 메시지들의 목록을 표시합니다. 제일 첫 번째 메시지(2)는 Archiver 프로세스에 hang이 발생했음을 알려 주고 있습니다. 그 이유를 확인하기 위해 메시지에 연결된 링크를 클릭하면 에러 정보를 담은 alert.log 파일의 상세한 내용이 표시됩니다. 이 경우, flashback recovery area의 공간이 부족한 것이 원인임을 확인되었고, 공간 확보 작업을 거침으로써 Archiver가 다시 정상적으로 동작하게 될 것입니다.
figure 3
그림 3: 메인 데이타베이스 홈 페이지 (하단)
또 다른 알림 메시지(3)은 wait에 관한 정보를 표시하고 있습니다. 데이타베이스가 69%의 시간을 “Application” wait class에 관련된 wait에 사용하고 있습니다. 홈 페이지의 상단에 있던 wait에 관련한 세션 정보를 기억하십니까? 이 메시지를 통해 그 원인이 확인된 셈입니다. 하이퍼링크를 클릭하면 실제 발생 중인 wait에 대한 상세 정보가 표시됩니다.
다음의 알림 메시지(4)는 감사에 관련된 내용입니다. 사용자 SYS가 특정 클라이언트 머신으로부터 데이타베이스에 접속했습니다. 하이퍼링크를 클릭하면 연결에 관련한 상세 정보를 얻을 수 있습니다. 마지막 메시지(5)는 몇 가지 invalid 오브젝트에 대해 알려주고 있습니다. 하이퍼링크를 클릭하면 invalid 오브젝트에 대한 상세 정보 화면이 표시됩니다.
지금까지 확인한 것처럼, 데이타베이스 홈 페이지는 주목할 필요가 있는 모든 정보를 표시하는 대시보드의 역할을 담당합니다. 세부적인 정보로 화면을 가득 채우는 대신, 인터페이스를 매우 간결한 형태로 정리하고 한 번의 마우스 클릭만으로 상세한 정보를 확인할 수 있도록 배려하였습니다. 이 모든 정보를 직접 수집할 수도 있겠지만 그렇게 하려면 많은 시간과 노력이 필요할 것입니다. EM 10g는 설치 후 별도의 구성 과정을 거치지 않고도 필요한 모든 정보를 제공합니다.
일반적 용례
이번에는 새로운 EM을 활용하여 보다 일상적인 업무를 해결하는 사례에 대해 알아보기로 합시다.
DBA가 수행하는 일상 업무의 하나로 테이블 및 인덱스의 변경을 들 수 있습니다. 데이타베이스 홈 페이지에서 “Administration” 탭을 선택하여 (그림 3의 6번) 클릭합니다. 이 페이지를 통해서 undo segment의 설정, 테이블스페이스 및 스키마 오브젝트의 생성, Resource manager설정, Scheduler의 설정 등의 데이타베이스 관리 작업을 수행할 수 있습니다. “Tables” 탭을 클릭하면 그림 4와 같은 화면이 표시됩니다.
figure 4
그림 4: 테이블 관리
붉은 원 안에 표시된 플래시 심볼을 주목하시기 바랍니다. 이 심볼은 값의 목록을 불러오기 위해 사용하는 버튼입니다. 화면에서 LOV 심볼을 클릭하면 데이타베이스에 존재하는 사용자의 목록이 표시되어 그 중 하나를 선택할 수 있게 합니다. “Go” 버튼을 클릭하면 해당 사용자를 위한 테이블의 목록이 표시됩니다. 이 과정에서 와일드카드 문자(%)를 사용하는 것도 가능합니다. 예를 들어 “%TRANS%”는 테이블명에 TRANS라는 문자열을 포함하는 모든 테이블을 표시하는 조건으로 사용됩니다.
예를 들어 설명해 보겠습니다. 컬럼의 변경을 위해 TRANS 테이블을 선택합니다 하이퍼링크를 클릭하면 “Edit Table” 스크린이 표시됩니다 (그림 5 참고).
figure 5
그림 5: 테이블 관리
ACTUAL_RATE 컬럼의 데이타 타입을 NUMBER(10)에서 NUMBER(11)으로 변경하고자 하는 경우, 숫자(그림 5의1번 참고)를 변경한 후 “Apply”를 클릭합니다. 이 작업에 사용되는 SQL 구문을 확인하려면 “Show SQL” 버튼을 클릭합니다.
같은 화면에서 용량의 증가 현황에 관련한 정보를 확인할 수도 있습니다. 나중에 세그먼트 관리에 관한 연재에서 자세히 설명하겠지만, 일정 기간 동안 오브젝트 크기가 증가한 추이를 관찰하는 것이 가능합니다. EM은 같은 정보를 그래픽 형태로 표시합니다. 이 정보를 얻으려면 “Segment” 탭(그림5의 2번 참고))을 클릭합니다. 그러면 그림 6에서 보여지는 것과 같은 Segment 스크린이 표시됩니다.
figure 6
그림 6: Segment 스크린
붉은 색 원 안에 표시된 항목들을 주목하십시오: 이 화면은 얼마나 많은 공간이 세그먼트에 할당되었고(2), 얼마나 많은 공간이 실제로 사용되고 있으며(1), 얼마나 많은 공간이 낭비되고 있는지(3)를 알려줍니다. 스크린의 하단(4)에는 오브젝트에 할당된 공간과 사용된 공간을 그래픽을 통해 표시하고 있습니다. 이 경우, 테이블의 용량은 일정하게 유지되고 있음을 알 수 있습니다.
그 밖에도 다양한 테이블 관련 작업을 수행할 수 있습니다. 예를 들어 “Constraints” 탭은 제약조건 관리에 활용됩니다.
EM을 이용한 성능 튜닝
지금까지 확인한 것처럼, EM은 룩앤필 면에서 많은 변화를 겪었지만 과거 Java 버전에서 제공되던 기능을 모두 그대로 포함하고 있습니다. 하지만 이전 버전과 분명하게 구분되는 점은, EM이 Oracle Database의 새로운 기능을 모두 지원한다는 사실입니다. (예를 들어 EM을 이용하여 subpartition 기능을 구현할 수 있게 되었습니다.)
하지만 숙련된 DBA라면 툴이 더 많은 기능을 제공할 수 있기를 원할 것입니다. 성능 문제에 관련한 트러블슈팅 및 사전 예방적인 성능튜닝과 같은 경우입니다. 한 가지 예를 들어보겠습니다. 앞부분의 예에서 데이타베이스가 “Application” wait class에 대한 대기에 많은 시간을 소모하고 있음을 확인한 바 있으며 그 원인을 진단하길 원하고 있습니다(그림 3의 3)). 어떠한 튜닝 작업이든 CPU, 디스크, 호스트 서브시스템의 상호 관계를 확인하는 것이 중요하며, 따라서 이러한 모든 변수들을 하나의 맥락으로 파악할 수 있다면 도움이 될 것입니다. 이를 위해 데이타베이스 홈 페이지에서 “Performance” 탭을 클릭합니다. 그림 7에서 보여지는 것과 같은 화면이 뜰 것입니다.
figure 7
그림 7: Performance 탭
같은 시간대를 기준으로 다양한 성능 지표가 표시되고 있으며, 따라서 다양한 성능 변수의 상호관계를 쉽게 파악할 수 있습니다. 그림 7 (3)번의 급격한 부하 증가 현상은 Scheduler 태스크의 실행 주기와 일치합니다. 또 같은 기간 동안 7개의 세션이 Scheduler 관련 wait를 위해 대기하였음을 알 수 있습니다. 그렇다면 어떤 영향이 있었을까요? 같은 시기의 CPU 성능지표를 확인하시기 바랍니다 (초록색 영역). (4)번의 기준선으로 미루어 CPU의 사용률이 가장 높게 올라간 것을 확인할 수 있습니다. 그 이전과 이후에는 CPU 사용량이 급격하게 증가한 경우가 한 차례도 없었습니다. (1) 번의 Run Queue Length가 증가한 것은 Scheduler가 실행되면서 과도한 메모리를 요구했기 때문으로 보이며, 이로 인해 페이징 작업(2)이 늘어난 것을 알 수 있습니다. 이처럼 모든 현상을 하나의 맥락으로 이해함으로써 데이타베이스 성능 문제를 보다 정확하게 진단할 수 있습니다.
뒷부분의 Run Queue Length(5)와 Paging Rate(6) 곡선은 Physical Reads(7)의 곡선과 일치합니다. 그 원인은 무엇일까요?
위의 결과를 “Sessions: Waiting and Working” 그래프와 비교해 보면, 대부분의 세션이 “Application” wait class를 대기하고 있었음을 알 수 있습니다. 하지만 해당 시간대에 대기 중이었던 이벤트가 무엇이었는지 분명하게 파악할 필요가 있습니다. 해당 시간대의 영역을 클릭하면 그림 8에서 보여지는 것과 같은 Active Sessions 스크린이 표시됩니다.
figure 8
그림 8: Active Sessions Waits
위 화면을 통해 세션이 enq: TX - row lock contention 이벤트를 대기 중이었음을 알 수 있습니다. 그렇다면 그 원인이 된 SQL 구문은 무엇일까요? 이미 화면에 문제의 SQL 구문에 대한 SQL ID(8rkquk6u9fmd0)가 표시되고 있습니다 (붉은색 원 참고). 해당 SQL ID를 클릭하면 그림 9와 같은 SQL Details 스크린이 표시됩니다.
figure 9
그림 9: SQL Details 스크린
위 스크린을 통해 SQL 구문과 execution plan을 포함하는 상세 정보를 확인할 수 있습니다. 이 경우 SQL이 row lock contention을 발생시킨 것을 알 수 있으며, 따라서 애플리케이션 설계가 성능 문제의 원인이 된 것으로 결론을 내릴 수 있습니다.
Latch Contention
"Performance" 탭을 클릭한 결과가 아래 그림과 같은 경우를 가정해 봅시다.
figure 10
그림 10: Performance 탭 (두 번째 예)
그림에서 붉은색 사각형으로 표시된 영역의 성능 지표를 주목하시기 바랍니다. 오전 12시 20분을 전후하여 CPU에 관련한 wait가 많이 발생했고, 이로 인해 CPU의 run queue 사이즈가 증가했음을 알 수 있습니다. 이제 wait 현상의 원인을 분석할 차례입니다.
먼저 그래프의 CPU 경합에 관련된 정보 영역을 클릭하여 (“Click Here”표 표시된 부분 참고) wait에 관련한 상세한 정보를 확인합니다.
figure 11
그림 11: Active Session Waits
"Active Sessions Working: CPU Used" 그래프의 회색으로 표시된 영역(1)을 참고하시기 바랍니다. 마우스를 드래그하여 해당 영역의 위치를 바꿀 수도 있으며, 이 경우, 아래의 파이 차트(2와 3)는 선택된 시간대를 기준으로 다시 계산됩니다. 위 그림에서 ID 8ggw94h7mvxd7를 갖는 SQL 구문에 많은 부하가 걸리고 있음을 알 수 있습니다(2). 또 사용자 ARUP의 SID 265 세션이 가장 많은 자원을 사용하고 있다는 사실도 확인할 수 있습니다(3). 해당 세션을 클릭하면 “Session Details” 스크린이 표시됩니다. “Wait Events” 탭을 클릭하여 세션에 관련된 wait 이벤트의 상세한 정보를 확인할 수 있습니다 (그림 12 참고).
figure 12
그림 12: Wait Event 상세 정보
위 그림에서, library cache에 대한 wait가 118 centisecond로 가장 오랜 시간이 걸리고 있음을 확인합니다. “Latch: Library Cache”의 하이퍼링크를 클릭하면 그림 13과 같은 화면이 나타납니다.
figure 13
그림 13: Wait Histogram
위 화면은 10g 이전 버전에서는 제공되지 않던 정보를 표시하고 있습니다. Latch 경합에 관련된 문제를 진단하면서, 118 centisecond의 대기시간이 여러 세션들의 짧은 wait들이 합산된 결과인지, 아니면 하나의 세션이 오랜 시간을 대기한 결과인지 어떻게 알 수 있을까요?
위의 히스토그램이 그 정보를 제공합니다. 약 250여 개의 세션이 wait에 1 millisecond (첫 번째 붉은색 원 참고)를 사용했으며, 180여 개의 세션이 4~8 millisecond(두 번째 붉은색 원 참고)를 사용했음을 알 수 있습니다. 따라서 매우 짧은 시간의 wait가 원인이 되고 있으며, 따라서 latch 경합이 심각한 수준이 아니라는 결론을 내릴 수 있습니다.
데이타베이스 홈 페이지에서 “Advisor Central” 탭을 클릭하면, ADDM, SQL Access Advisor를 비롯한 각종 Advisor 툴에 접근할 수 있습니다. ADDM은 자동으로 성능지표를 수집하여 Advisor Central 페이지에 그 결과를 표시하며, 각각의 결과를 클릭하면 ADDM이 제시하는 권고안을 확인할 수 있습니다. SQL Tuning Advisor가 제시하는 권고안도 이 페이지에서 확인할 수 있습니다. (ADDM과 SQL Tuning Advisor에 대한 자세한 내용은 향후 연재에서 설명합니다.)
더욱 쉬워진 유지보수 작업
데이타베이스 홈 페이지의 “Maintenance” 탭은 백업 및 복구, 데이타 익스포트/임포트 (Data Pump), 데이타베이스 클로닝 등의 유지보수 작업에 관련한 툴의 정보를 제공합니다. 이 화면에서 유지보수 정책에 위반된 내역을 확인하고 베스트 프랙티스를 구현할 수 있습니다.
결론
이 문서는 EM의 기능 중 극히 일부만을 설명하고 있습니다. 이 문서는 EM의 기능을 전반적으로 설명하기보다, 특정한 업무를 위해 EM을 사용하는 예에 초점을 맞추어 논의를 진행했습니다.
Oracle 10g EM은 초보 DBA가 Oracle Database 관리의 개념을 이해하는데 매우 유용한 툴입니다. EM의 활용에 관련한 테크닉은 "2-Day DBA" reference manual"에 잘 설명되어 있습니다. DBA 업무를 처음 시작하는 관리자라면 반드시 읽어 보실 것을 권합니다.
 
 
 
 
제 14 주
Virtual Private Database
VPD에 새롭게 추가된 4가지 policy type, 컬럼 단위 policy 적용, column masking 등의 신기능을 활용하여 한층 강력하고 유연한 보안 환경을 구현할 수 있습니다.
Fine Grained Access Control이라는 용어로 불리기도 하는 Virtual Private Database(VPD)는 로우(row) 레벨의 강력한 보안 기능을 제공합니다. VPD는 Oracle8i에서 처음 소개된 이후, 교육용 소프트웨어에서 금융 애플리케이션에 이르기까지 다양한 영역에서 활용되고 있습니다.
VPD는 접수된 데이타 쿼리를 사용자의 권한에 맞도록 테이블의 일부분만을 포함하는 부분적인 뷰에 대한 쿼리로 자동 변경합니다. VPD는 모든 쿼리에 대해 사용자에게 접근 허용된 로우(row)만을 필터링하도록 쿼리 조건을 추가합니다. 예를 들어 사용자가 SCOTT가 account manager로 할당된 account만을 보아야 하는 경우, VPD는 쿼리를 아래와 같이 재작성합니다:
select * from accounts;
to:
select * from accounts
where am_name = 'SCOTT';
DBA가 ACCOUNTS 테이블에 보안 정책을 설정하면, 설정된 정책에는 policy function이라 불리는 함수가 적용됩니다. 이 함수는 “where am_name = 'SCOTT'“와 같은 문자열을 반환하고, 생성된 문자열은 쿼리 조건에 추가되는 predicate으로 활용됩니다. VPD 기능에 대해 친숙하지 않은 경우라면, 오라클 매거진의 기사 "Keeping Information Private with VPD를 참고하실 것을 권장 드립니다."
Policy Types
이처럼 반복적인 파싱을 통해predicate을 생성하는 것은 성능적으로 부담이 될 수 있습니다. 경우에 따라서는 성능 개선을 위한 대안을 고려할 수 있습니다. 대부분의 경우 predicate은 사용자가 누구이고, 그 사용자의 권한이 어디까지이고, 사용자의 상급 관리자가 누구인지 등의 여부에 따라 다이내믹하게 결정됩니다. Policy function에 의해 생성 및 반환되는 문자열은 매우 다이내믹한 성격을 가지며, 따라서 오라클은 정확성을 보장하기 위해서 매번 policy function을 반복적으로 수행합니다. 이는 성능 및 자원사용률 면에서 낭비를 초래할 수 있습니다. 이처럼 predicate이 실행할 때마다 달라지는 경우의 policy를 “dynamic” policy라 부릅니다. Dynamic policy는 Oracle9i 데이타베이스와 그 이전 릴리즈에서 이미 지원되어 왔습니다.
이와 별도로, Oracle Database 10g는 성능 향상을 목적으로 context_sensitive, shared_context_sensitive, shared_static, static 등의 다양한 policy 유형을 추가로 제공합니다: 이제 각 policy 유형의 의미와 활용 방안을 알아 보기로 합시다.
Dynamic Policy. 하위 버전과의 호환성을 유지하기 위한 목적에서, 10g의 디폴트 policy 유형은 “dynamic”으로 설정되어 있습니다. Dynamic policy는 테이블이 액세스 될 때마다 각각의 로우(row) 및 모든 사용자에 대해서 policy function을 실행합니다. Dynamic policy가 predicate를 활용하는 방법에 대해 좀 더 자세히 알아보겠습니다:
where am_name = 'SCOTT'
where 키워드를 제외한다면, predicate은 크게 두 부분으로 나뉘어집니다. 등호 기호의 앞부분(am_name)과 뒷부분(‘SCOTT’)로 나뉘어집니다. 대부분의 경우, 뒷부분에는 사용자 데이타로부터 제공되는 변수와 같습니다. (예를 들어 사용자가 SCOTT라면 값은 ‘SCOTT’가 됩니다.) 등호 기호의 앞부분은 static한 문자열로 이루어지며, 따라서 각 로우(row) 별로 policy function을 반복적으로 수행할 필요가 없습니다. 이처럼 등호 기호의 앞부분과 뒷부분의 static / dynamic 여부를 미리 알고 있다면 부분적으로 성능을 개선하는 것이 가능해집니다. 10g에서는 이를 위해 “context_sensitive” 타입을 지원하며, dbms_rls.add_policy 호출 과정에서 이 타입을 매개변수로 사용할 수 있습니다:
policy_type => dbms_rls.context_sensitive
이번에는 여러 개의 컬럼을 갖는 ACCOUNTS 테이블에 대한 예를 설명하겠습니다. ACCOUNTS 테이블에 포함된 BALANCE 컬럼은 해당 계좌의 잔액을 표시합니다. 특정 사용자가 application context에 의해 정의된 액수 이하의 잔액을 갖는 계좌들을 조회할 수 있도록 허용되었다고 가정해 봅시다. 하드 코딩을 통해 계좌 잔액의 액수를 입력하는 대신 Policy function을 이용해 다음과 같이 application context를 활용할 수 있습니다:
create or replace vpd_pol_func
(
   p_schema in varchar2,
   p_table in varchar2
) 
return varchar2
is
begin
   return 'balance < sys_context(''vpdctx'', ''maxbal'')';
end;
Application context VPDCTXMAXBAL 속성을 설정해 두고 함수가 런타임에 값을 가져오도록 할 수 있습니다.
위의 코드 예제를 주의 깊게 살펴 보시기 바랍니다. Predicate은 크게 두 부분(‘<’기호 이전과 이후)으로 나뉘어집니다. 앞부분의 “balance”는 static한 문자열입니다. 뒷부분은 application context가 변경되기 전까지는 원래 값을 그대로 유지하므로 어느 정도 static하다고 볼 수 있습니다. 따라서 application context속성이 변경되지 않는 한, 전체 predicate이 static하다고 판단할 수 있으며, 따라서 함수를 재실행할 필요가 없습니다. Oracle Database 10g는 policy type이 context sensitive로 지정된 경우 이러한 최적화 알고리즘을 사용합니다. 세션에서 context 변경이 발생하지 않는 경우 함수는 재실행되지 않으며, 상당한 수준의 성능 향상 효과를 볼 수 있습니다.
Static Policy. 경우에 따라서는 보다 static한 형태의 predicate이 사용될 수도 있습니다. 위의 예제에서는 maximum balance를 변수로서 정의했습니다. 이러한 접근방식은 Oracle userid가 많은 웹 사용자에 의해 공유되고 사용자의 권한에 따라 이 변수(application context)가 변경되어야 하는 웹 애플리케이션 환경에서 유용합니다. 예를 들어 TAO와 KARTHIK이라는 두 사용자가 APPUSER라는 동일한 데이타베이스 사용자로 접근하는 경우에도, 각 세션 별로 설정된 application context에 의해 서로 다른 권한을 할당 받게 됩니다. 다시 말해 MAXBAL의 값은 Oracle userid가 아닌 TAO와 KARTHIK의 개별 세션 별로 바인드 됩니다.
Static policy의 경우 predicate은 아래와 같이 보다 예측 가능한 형태로 제시됩니다.
LORA와 MICHELLE는 각각 Acme Bearings와 Goldtone Bearings의 어카운트 관리자입니다. 두 사람은 데이타베이스에 연결할 때 개인 ID를 사용하며 각자에게 허용된 로우(row)만을 조회할 수 있습니다. Lora의 경우 predicate은 “where CUST_NAME = 'ACME'”, Michelle의 경우 predicate은 “where CUST_NAME = 'GOLDTONE'”입니다. 이 경우 predicate은 사용자ID와 연결되며, 따라서 그들에 의해 생성된 어떤 세션이든 application context가 제공하는 같은 값을 predicate으로 사용하게 됩니다.
10g는 이러한 경우 SGA 캐시에 predicate을 저장하고 해당 세션에 대해서는 policy function을 재실행하지 않고 계속적으로 재활용합니다. Policy function은 아래와 같습니다:
create or replace vpd_pol_func
(
   p_schema in varchar2,
   p_table in varchar2
) 
return varchar2
is
begin
   return 'cust_name = sys_context(''vpdctx'', ''cust_name'')';
end;
Policy는 아래와 같이 정의됩니다:
policy_type => dbms_rls.static
이와 같은 접근법을 사용하면 policy function이 단 한 차례만 수행됨을 보장할 수 있습니다. 세션에서 application context가 변경되는 경우에도 함수는 재실행되지 않으므로 성능이 대폭적으로 향상됩니다.
Static policy는 여러 구독자(subscriber)가 사용하는 애플리케이션 환경에서 유용합니다. 이 경우 여러 사용자 또는 구독자가 단일 데이타베이스의 데이타를 공유하게 됩니다. 구독자가 로그인하면, 로그인 과정에서 “after-login trigger”가 동작하여 application context를 설정하고 policy function을 실행함으로써 predicate을 얻게 됩니다.
하지만 static policy는 양날의 칼과도 같습니다. 위의 예에서는, application context의 VPDCTX.CUST_NAME속성의 값이 세션 내에서 변경되지 않는다고 가정했습니다. 만일 그 가정이 잘못되었다면 어떻게 될까요? 속성 값이 변해도 policy function이 재실행되지 않으므로 predicate에 새로운 값이 반영되지 않을 것이며, 따라서 완전히 잘못된 결과가 나올 것입니다. 그러므로 static policy를 사용할 때는 주의할 필요가 있으며, 속성 값이 변하지 않음을 분명히 확신할 수 있을 때에만 static policy를 사용해야 합니다. 이러한 가정이 불가능하다면, context sensitive policy를 사용하는 것이 무난합니다.
Shared Policy Types. 코드를 재활용하고 파싱된 코드의 활용도를 최대로 높이려면, 여러 테이블에 공통적으로 적용되는 policy function을 구현할 필요가 있습니다. 예를 들어, 위의 예에서 계좌 별로 두 개의 테이블(SAVINGS와 CHECKING)이 존재하지만 규칙은 똑같이 적용되는 경우를 가정해 봅시다. 사용자는 자신에게 허용된 것 이외의 계좌 잔액은 조회할 수 없습니다. 이 경우 CHECKING 테이블과 SAVINGS 테이블에 사용되는 policy function은 동일합니다. 생성되는 policy의 유형은 context_sensitive로 가정합니다.
다음과 같은 이벤트가 순서대로 발생한다고 가정해 봅시다:
1. 세션 연결
2. application context 설정
3. select * from savings;
4. select * from checking;
Application context가 3번, 4번 과정에서 변경되지 않았음에도 조회되는 테이블이 다르기 때문에 policy function은 재실행될 것입니다. 하지만 policy function이 같기 때문에 재실행할 필요는 없었습니다. 10g는 동일한 policy를 여러 오브젝트가 공유하는 기능을 제공합니다. 위의 예의 경우 policy type은 다음과 같이 정의됩니다:
New in 10g is the ability to share a policy across objects. In the above example, you would define the policy type of these policies as:
policy_type => dbms_rls.shared_context_sensitive
이처럼 policy를 “shared”로 지정함으로써 함수의 불필요한 실행을 방지하고 성능을 향상시킬 수 있습니다.
Selective Columns
이번에는 특정 컬럼이 select 된 경우에만 VPD policy가 적용되는 경우를 생각해 봅시다. ACCOUNTS 테이블이 아래와 같은 레코드를 갖는다고 가정합니다:
ACCTNO ACCT_NAME    BALANCE
------ ------------ -------
     1 BILL CAMP    1000
     2 TOM CONNOPHY 2000
     3 ISRAEL D     1500    
Michelle은 balance가 1,600이 넘는 account를 조회할 수 없습니다. Michelle이 다음과 같은 쿼리를 실행한 경우:
select * from accounts;
아래와 같은 결과를 확인하게 될 것입니다:
ACCTNO ACCT_NAME    BALANCE
------ ------------ -------
     1 BILL CAMP    1000
     3 ISRAEL D     1500    
1,600이 넘는 balance를 갖는 acctno 2는 실행 결과에 포함되지 않았습니다. Michelle의 관점에서 볼 때 이 테이블은 3개가 아닌 2개의 로우를 갖습니다. 따라서 Michelle이 아래와 같은 쿼리를 실행하는 경우에도 그 결과는 3이 아닌 2가 반환됩니다:
select count(*) from accounts;
하지만 security policy에 예외 조건을 적용할 필요도 있습니다.
지금 Michelle은 모든 account balance를 조회하려 하는 것이 아니고 그저 데이타의 count만을 확인하려 하는 것입니다. Michelle이 조회할 수 없는 레코드의 count를 포함시키는 것을 허용하는 것을 고려할 수도 있을 것입니다. 10g는 이러한 경우를 위해 dbms_rls.add_policy 호출 과정에서 사용할 수 있는 새로운 매개변수를 추가하였습니다:
sec_relevant_cols => 'BALANCE'
이 매개변수가 사용된 경우, 사용자가 BALANCE 컬럼을 명시적으로 또는 암시적으로(예: select *) 조회하는 경우에 한해 VPD policy는 해당 로우를 조회 대상에서 제외시킵니다. 하지만 그 밖의 경우(예를 들어 사용자가 전체 로우의 count를 조회하는 경우)에는 테이블의 모든 로우에 대한 select가 허용됩니다. 이 경우 아래와 같이 쿼리를 수행하면 그 결과로 2가 아닌 3이 반환됩니다:
select count(*) from accounts;
하지만 아래의 쿼리는 두 개의 레코드만을 반환합니다.
select * from accounts;
Column Masking
이번에는 다른 조건이 붙는 경우를 생각해 봅시다. 일정한 임계값 이상의 balance를 갖는 레코드에 대한 조회를 완전히 제한하는 대신, 임계값 이상의 balance 컬럼에 대해 마스킹(masking)을 수행하는 조건으로 모든 레코드를 반환하는 방법을 선택할 수도 있습니다.
Michelle은 1,600 이상의 balance를 갖는 account를 조회할 수 없습니다. Michelle이 아래와 같은 쿼리를 실행하면:
select * from accounts;
acctno 1과 acctno3의 두 가지 레코드만을 확인하게 됩니다. 그 대신 아래와 같은 결과를 보기 원할 수 있습니다:
ACCTNO ACCT_NAME    BALANCE
------ ------------ -------
     1 BILL CAMP    1000
     2 TOM CONNOPHY <null>
     3 ISRAEL D     1500    
위의 경우 모든 레코드가 표시되지만, 2000의 balance를 가진 acctno2 레코드의 BALANCE 컬럼은 null로 표시됩니다. 이러한 방법을 “column masking”이라 부르며, dbms_rls.add_policy 호출 과정에서 아래 매개변수를 사용하여 활성화할 수 있습니다:
sec_relevant_cols_opt =>  dbms_rls.all_rows
이 방법은 특정 컬럼에 대한 보안 유지가 필요한 경우 매우 유용하게 활용되며, 구현과정에서 별도의 코딩이 불필요합니다. 또 이 방법을 데이타 암호화의 대안으로 활용할 수도 있습니다.
결론
Oracle Database 10g의 VPD 기능은 매우 강력한 형태로 발전되었으며, policy를 기준으로 선택적인 컬럼을 마스킹하거나, 특정 컬럼이 액세스되는 경우에만 policy를 적용하는 등의 다양한 요구사항을 지원합니다. 또 애플리케이션의 성격에 따라 다른 policy를 적용함으로써 성능을 향상시킬 수도 있습니다.
VPD와 dbms_rls 패키지에 대한 자세한 설명은 PL/SQL Packages and Types ReferenceChapter 79, 또는 Oracle Database Security Guide를 참고하시기 바랍니다. 필자가 Don Burleson과 공동집필한 Oracle Privacy Security Auditing (Rampant TechPress)도 참고가 될 것입니다.
 
 
 
 
제 15 주
세그먼트의 관리
Oracle Database 10g가 새로 제공하는 공간 재확보 기능, 온라인 테이블 재구성, 스토리지 증가량 예측 기능 등을 이용하여 세그먼트의 공간을 효율적으로 관리할 수 있습니다.
오래 전, Oracle Database의 경쟁 RDBMS 제품을 평가해 달라는 요청을 받은 일이 있습니다. 경쟁사의 프리젠테이션이 진행되는 동안, 청중들이 가장 감탄했던 기능이 바로 온라인 재구성(online reorganization) 기능이었습니다. 이 제품은 (오라클로 따지면 세그먼트에 해당하는) 영역의 데이타 블록을 온라인 상태에서 재배치하는 기능을 제공했습니다.
당시 오라클이 제공하던 Oracle9i Database는 이러한 기능을 제공하지 못했습니다. 이제 Oracle Database 10g는 온라인 상태에서 낭비되는 공간을 재확보하고 오브젝트를 보다 컴팩트(compact)한 형태로 관리할 수 있게 하는 기능을 추가적으로 제공합니다.
이 기능을 자세히 살펴 보기에 앞서, 이전에는 세그먼트 관리를 어떤 방법으로 수행했는지 설명하도록 하겠습니다.
기존의 관리 방법
그림 1과 같은 형태로 채워진 세그먼트를 가정해 봅시다. 작업이 수행되면서 그림 2와 같이 일부 로우(row)가 삭제되고 나면 낭비되는 공간이 생기게 됩니다. 낭비되는 공간은 (i) 남아있는 블록의 마지막 영역과 기존 테이블의 마지막 영역의 사이에서, 그리고 (ii) 로우가 부분적으로만 삭제된 블록 내부에서 발생합니다.
figure 1
그림 1: 테이블에 할당된 블록. (로우는 회색 사각형으로 표시됨)
오라클이 이 영역에 대한 할당을 바로 해제하지 않고, 새로운 insert 작업 및 기존 로우의 확장에 대비한 예비 공간으로 활용합니다. 지금까지의 점유되었던 공간의 최고점을 High Water Mark(HWM)이라 부릅니다 (그림 2 참고).
figure 2
그림 2: 일부 로우가 삭제된 후 (HWM은 변경되지 않았음)
하지만 이와 같은 접근 방식에는 두 가지 문제점이 존재합니다:
  • 사용자의 쿼리가 풀 테이블 스캔을 발생시키는 경우, 오라클은 (설사 관련된 데이타가 전혀 존재하지 않는 경우라 하더라도) HWM 아래쪽의 모든 영역을 스캔합니다. 이로 인해 풀 테이블 스캔에 소요되는 시간이 길어질 수 있습니다.
  • 로우가 direct path 정보와 함께 insert 되는 경우 (예를 들어 APPEND 힌트를 사용한 Insert, 또는 SQL*Loader direct path를 통해 insert 되는 경우) 새로 추가되는 데이타 블록은 HWM의 위쪽 영역에 추가됩니다. 따라서 HWM의 아래쪽 영역은 낭비된 채로 남게 됩니다.
Oracle9i와 그 이전 버전에서 공간을 재확보하려면, 테이블을 drop하고 다시 생성한 다음 데이타를 다시 로드하는 방식, 또는 ALTER TABLE MOVE 명령을 사용하여 테이블을 다른 테이블스페이스로 이동하는 방식을 사용해야 했습니다. 이 두 가지 방식은 모두 오프라인 상태에서 수행되어야 한다는 문제가 있습니다. 그 대안으로 online table reorganization 기능을 사용할 수도 있지만, 이를 위해서는 기존 테이블 크기의 두 배나 되는 공간이 필요했습니다.
10g의 경우 이러한 작업은 훨씬 간소화되었습니다. 10g의 Automatic Segment Space Management(ASSM)이 해당 테이블스페이스에 활성화되어 있는 경우, 세그먼트, 테이블, 인덱스를 shrink하고 free block을 재확보한 뒤 다른 용도로 할당하도록 데이터베이스로 반환됩니다. 그 자세한 방법을 알아보기로 합시다.
10g의 세그먼트 관리 기능
웹사이트를 통해 온라인으로 접수된 예약 정보를 보관하는 BOOKINGS라는 이름의 테이블이 존재한다고 가정해 봅시다. 확인 절차를 거친 예약은 BOOKINGS_HIST 테이블에 저장되고 해당 레코드는 BOOKINGS 테이블에서 삭제됩니다. 예약에서 확인까지 걸리는 시간은 고객에 따라 다릅니다. 이 경우 레코드 삭제로 인해 남은 공간이 충분하지 않은 경우에는 레코드가 테이블 HWM의 위쪽 영역에 insert 됩니다.
이제 낭비되는 공간을 재확보할 차례입니다. 먼저 해당 세그먼트에서 얼마나 많은 공간을 확보할 수 있는지 확인해야 합니다. 이 테이블은 ASSM이 적용된 테이블스페이스에 위치하고 있으므로, 아래와 같이 DBMS_SPACE 패키지의 SPACE_USAGE 프로시저를 사용해야 합니다:
declare

   l_fs1_bytes number;
   l_fs2_bytes number;
   l_fs3_bytes number;
   l_fs4_bytes number;
   l_fs1_blocks number;
   l_fs2_blocks number;
   l_fs3_blocks number;
   l_fs4_blocks number;
   l_full_bytes number;
   l_full_blocks number;
   l_unformatted_bytes number;
   l_unformatted_blocks number;
begin
   dbms_space.space_usage(
      segment_owner      => user,
      segment_name       => 'BOOKINGS',
      segment_type       => 'TABLE',
      fs1_bytes          => l_fs1_bytes,
      fs1_blocks         => l_fs1_blocks,
      fs2_bytes          => l_fs2_bytes,
      fs2_blocks         => l_fs2_blocks,
      fs3_bytes          => l_fs3_bytes,
      fs3_blocks         => l_fs3_blocks,
      fs4_bytes          => l_fs4_bytes,
      fs4_blocks         => l_fs4_blocks,
      full_bytes         => l_full_bytes,
      full_blocks        => l_full_blocks,
      unformatted_blocks => l_unformatted_blocks,
      unformatted_bytes  => l_unformatted_bytes
   );
   dbms_output.put_line(' FS1 Blocks = '||l_fs1_blocks||' Bytes = '||l_fs1_bytes);
   dbms_output.put_line(' FS2 Blocks = '||l_fs2_blocks||' Bytes = '||l_fs2_bytes);
   dbms_output.put_line(' FS3 Blocks = '||l_fs3_blocks||' Bytes = '||l_fs3_bytes);
   dbms_output.put_line(' FS4 Blocks = '||l_fs4_blocks||' Bytes = '||l_fs4_bytes);
   dbms_output.put_line('Full Blocks = '||l_full_blocks||' Bytes = '||l_full_bytes);
end;
/
The output is:
FS1 Blocks = 0 Bytes = 0
FS2 Blocks = 0 Bytes = 0
FS3 Blocks = 0 Bytes = 0
FS4 Blocks = 4148 Bytes = 0
Full Blocks = 2 Bytes = 16384
실행 결과를 통해 4,148개의 블록이 75-100%의 free space(FS4)를 포함하고 있으며, 이를 제외하고는 free space가 전혀 존재하지 않음을 확인할 수 있습니다. Full block은 단 2개에 불과합니다. 따라서 4,148 개의 블록에서 공간을 확보할 수 있습니다.
이제 테이블에 row-movement가 활성화되어 있는지 점검해야 합니다. row-movement를 활성화 하기 위해서는 아래와 같이 입력합니다:
alter table bookings enable row movement;
또는 Enterprise Manager 10g의 Administration 페이지에서 작업할 수도 있습니다. 또, 테이블의 모든 rowid 기반 트리거가 비활성화되어 있는지 점검해야 합니다. (로우가 이동되면서 rowid가 변경될 수 있기 때문입니다.)
마지막으로, 아래 명령을 사용하여 테이블의 기존 로우를 재구성합니다.
alter table bookings shrink space compact;
이 명령은 그림 3과 같은 형태가 되도록 블록 내부의 로우를 재배치하고, HWM 아래쪽 영역에 free block을 확보합니다. (하지만 HWM 자체는 변경되지 않습니다.)
figure 3
그림 3: 재구성작업을 거친 뒤의 테이블 블록
작업이 완료된 후 공간 사용률에 변화가 있는지 확인해 봅시다. 앞에서 소개한 PL/SQL 코드를 사용하여 얼마나 많은 블록이 재구성되었는지 확인할 수 있습니다:
FS1 Blocks = 0 Bytes = 0
FS2 Blocks = 0 Bytes = 0
FS3 Blocks = 1 Bytes = 0
FS4 Blocks = 0 Bytes = 0
Full Blocks = 2 Bytes = 16384
이제 매우 중요한 변화가 있었음을 확인할 수 있습니다. FS4 블록 (75-100%의 여유 공간을 갖는 블록)의 수가 4,148에서 0으로 바뀌었습니다. 또 FS3 블록(50-75%의 여유 공간을 갖는 블록)의 수가 0에서 1로 증가했습니다. 반면 HWM은 변경되지 않았으며, 전체 공간사용률에도 아무런 변화가 없었습니다. 사용중인 전체 공간은 아래와 같이 확인할 수 있습니다:
SQL> select blocks from user_segments where segment_name = 'BOOKINGS';

   BLOCKS
---------
     4224
테이블이 점유중인 블록의 수(4,224)는 변경되지 않았으며, HWM도 기존 위치를 그대로 유지하고 있습니다. 다음과 같은 명령을 사용하면 HWM의 위치를 아래쪽을 이동하고 상위 영역을 재확보할 수 있습니다:
alter table bookings shrink space;
여기서 COMPACT 키워드가 사용되지 않은 점을 주목하시기 바랍니다. 위 구문을 실행하면 테이블이 사용되지 않은 블록을 반환하고 HWM을 재설정합니다. 아래와 같이 테이블에 할당된 공간을 확인하고 그 결과를 점검할 수 있습니다:
SQL> select blocks from user_segments where segment_name = 'BOOKINGS';

    BLOCKS
----------
         8
블록의 수가 4,224 개에서 8개로 줄었습니다. 그림 4에서 보여지는 것처럼 테이블 내에서 사용되지 않던 모든 공간이 반납되어 다른 세그먼트에서 활용할 수 있게 되었습니다.
figure 4
그림 4: Shrink 작업 수행 후 free block이 데이타베이스로 반납된 결과
Shrink 작업은 온라인 상태에서 수행되며 사용자에게 아무런 영향을 미치지 않습니다.
테이블 인덱스에 대한 shrink 작업도 아래와 같이 수행할 수 있습니다:
alter table bookings shrink space cascade;
온라인 shrink 명령은 낭비되는 공간을 재확보하고 HWM을 재설정하는 매우 강력한 기능입니다. 필자는 개인적으로 HWM 재설정 기능의 유용성을 높이 평가합니다. HWM을 재설정함으로써 풀 테이블 스캔의 성능을 향상시킬 수 있기 때문입니다.
Shrinking 작업 대상 세그먼트 찾기
온라인 shrink 작업을 수행하기 전에, 압축율을 비약적으로 향상시킬 수 있는 대상 세그먼트를 찾아내는 작업을 수행해야 할 수도 있습니다. dbms_space 패키지에 내장된 verify_shrink_candidate 함수를 사용하여 이 작업을 간단하게 마무리할 수 있습니다. 아래 PL/SQL 코드는 대상 세그먼트가 1,300,000 바이트로 shrink 될 수 있는지 테스트합니다:
begin
   if (dbms_space.verify_shrink_candidate
         ('ARUP','BOOKINGS','TABLE',1300000)
   ) then
       :x := 'T';
   else
       :x := 'F';
   end if;
end;
/

PL/SQL procedure successfully completed.

SQL> print x

X
--------------------------------
T
If you use a low number for the target shrinkage, say 3,000:
begin
   if (dbms_space.verify_shrink_candidate
         ('ARUP','BOOKINGS','TABLE',30000)
   ) then
       :x := 'T';
   else
       :x := 'F';
   end if;
end;
이 경우 변수 x의 값은 ‘F’로 반환되었습니다. 이는 테이블이 3,000 바이트로 shrink 될 수 없음을 의미합니다.
인덱스 크기의 예측
이번에는 특정 테이블, 또는 여러 개의 테이블에 대해 인덱스를 생성해야 하는 경우를 가정해 봅시다. 컬럼, uniqueness 등의 구조에 관련한 일반적인 고려사항을 제외하고 가장 중요한 작업을 들라면, 인덱스의 크기를 예상하는 일을 꼽을 수 있을 것입니다. 테이블스페이스의 공간이 새로운 인덱스를 수용할 수 있을 만큼 충분한지 확인해야 합니다.
Oracle9i Database와 그 이전 버전의 경우, DBA들은 스프레드시트 또는 별개의 프로그램의 사용하여 인덱스의 크기를 예측하곤 했습니다. 10g에서는 새로 추가된 DBMS_SPACE 패키지를 이용해서 이 작업을 간단하게 마무리할 수 있습니다. 그렇다면 그 실제 활용 사례를 알아봅시다.
BOOKINGS 테이블의 booking_id 컬럼과 cust_name 컬럼을 대상으로 하는 새로운 인덱스를 추가해야 합니다. 새로운 인덱스가 얼마나 많은 공간을 사용하게 될까요? 아래와 같은 PL/SQL 스크립트를 실행하면 간단하게 확인할 수 있습니다:
declare
   l_used_bytes number;
   l_alloc_bytes number;
begin
   dbms_space.create_index_cost (
      ddl => 'create index in_bookings_hist_01 on bookings_hist '||
        '(booking_id, cust_name) tablespace users',
      used_bytes => l_used_bytes,
      alloc_bytes => l_alloc_bytes
   );
   dbms_output.put_line ('Used Bytes      = '||l_used_bytes);
   dbms_output.put_line ('Allocated Bytes = '||l_alloc_bytes);
end;
/   
실행 결과가 아래와 같습니다:
Used Bytes      = 7501128
Allocated Bytes = 12582912
인덱스의 크기를 증가시킬 수 있는 매개변수(INITRANS 등)를 사용한 경우를 가정해 봅시다.
declare
   l_used_bytes number;
   l_alloc_bytes number;
begin
   dbms_space.create_index_cost (
      ddl => 'create index in_bookings_hist_01 on bookings_hist '||
        '(booking_id, cust_name) tablespace users initrans 10',
      used_bytes => l_used_bytes,
      alloc_bytes => l_alloc_bytes
   );
   dbms_output.put_line ('Used Bytes      = '||l_used_bytes);
   dbms_output.put_line ('Allocated Bytes = '||l_alloc_bytes);
end;
/
실행 결과는 아래와 같습니다:
Used Bytes      = 7501128
Allocated Bytes = 13631488
INITRANS 매개변수의 값을 높인 결과 Allocated Bytes가 훨씬 증가했음을 확인할 수 있습니다. 이와 같은 방법으로 인덱스가 사용하게 될 공간의 크기를 쉽게 예측할 수 있습니다.
하지만 두 가지 주의해야 할 점이 있습니다. 먼저, 이 프로세스는 “SEGMENT SPACE MANAGEMENT AUTO”가 활성화된 테이블스페이스에만 적용 가능합니다. 두 번째로, 패키지는 테이블 통계를 근거로 인덱스의 크기를 예측합니다. 따라서 테이블의 통계가 최신 상태를 유지하고 있는지 점검하는 것이 중요합니다. 가장 주의할 점은, 테이블에 통계가 존재하지 않는 경우 패키지가 에러를 발생시키는 대신 엉뚱한 계산 결과를 제시한다는 사실입니다.
테이블 크기의 예측
이번에는 BOOKING_HIST 테이블이 평균 30,000의 row length를 가진 로우로 구성되어 있고 테이블의 PCTFREE 매개변수가 20으로 설정된 경우를 가정해 보겠습니다. PCT_FREE를 30으로 올리는 경우 테이블의 크기가 얼마나 증가하게 될까요? PCT_FREE가 10% 증가한 만큼, 테이블의 크기도 10% 증가하게 될까요? DBMS_SPACE 패키지의 CREATE_TABLE_COST 프로시저를 사용하면 간단하게 확인할 수 있습니다. 테이블의 크기를 예측하기 위한 코드가 아래와 같습니다:
declare
   l_used_bytes number;
   l_alloc_bytes number;
begin
   dbms_space.create_table_cost (
       tablespace_name => 'USERS',
       avg_row_size => 30,
       row_count => 30000,
       pct_free => 20,
       used_bytes => l_used_bytes,
       alloc_bytes => l_alloc_bytes
   );
   dbms_output.put_line('Used: '||l_used_bytes);
   dbms_output.put_line('Allocated: '||l_alloc_bytes);
end;
/
실행 결과는 다음과 같습니다:
Used: 1261568
Allocated: 2097152
테이블의 PCT_FREE 매개변수를 30에서 20으로 아래와 같이 조정한 후 다시 실행합니다:
pct_free => 30
we get the output:
Used: 1441792
Allocated: 2097152
사용된 공간의 크기가 1,261,568에서 1,441,792로 증가했습니다. 이는 PCT_FREE 매개변수가 데이타 블록에 더 많은 여유 공간을 할당하기 대문입니다. 증가된 비율은 예상대로 10%가 아닌 14%로 확인되었습니다. 이처럼 DBMS_SPACE 패키지를 사용하여 PCT_FREE와 같은 매개변수를 변경하는 경우 또는 테이블을 다른 테이블스페이스로 이동하는 경우의 테이블 크기를 예측할 수 있습니다.
세그먼트의 크기 예측
Acme Hotel은 주말을 맞아 수요가 급증할 것을 예상하고 있습니다. DBA는 증가하는 수요를 감당하기에 충분한 공간이 남아 있는지 확인하려 합니다. 테이블의 크기가 얼마나 증가할지 어떻게 예측할 수 있을까요?
10g가 제공하는 예측 기능의 정확성은 우리를 놀라게 하기에 충분합니다. 결과를 얻기 위해서는 아래와 같은 쿼리를 실행하기만 하면 됩니다.
select * from 
table(dbms_space.OBJECT_GROWTH_TREND 
('ARUP','BOOKINGS','TABLE'));
dbms_space.object_growth_trend() 함수는 PIPELINEd 포맷으로 레코드를 반환하며, TABLE() casting을 통해 그 결과를 확인할 수 있습니다. 출력된 결과가 아래와 같습니다:
TIMEPOINT                      SPACE_USAGE SPACE_ALLOC QUALITY
------------------------------ ----------- ----------- ------------
05-MAR-04 08.51.24.421081 PM       8586959    39124992 INTERPOLATED
06-MAR-04 08.51.24.421081 PM       8586959    39124992 INTERPOLATED
07-MAR-04 08.51.24.421081 PM       8586959    39124992 INTERPOLATED
08-MAR-04 08.51.24.421081 PM     126190859  1033483971 INTERPOLATED
09-MAR-04 08.51.24.421081 PM       4517094     4587520 GOOD
10-MAR-04 08.51.24.421081 PM     127469413  1044292813 PROJECTED
11-MAR-04 08.51.24.421081 PM     128108689  1049697234 PROJECTED
12-MAR-04 08.51.24.421081 PM     128747966  1055101654 PROJECTED
13-MAR-04 08.51.24.421081 PM     129387243  1060506075 PROJECTED
14-MAR-04 08.51.24.421081 PM     130026520  1065910496 PROJECTED
출력된 결과는 시간(TIMEPOINT 컬럼)별로 BOOKINGS 테이블 크기의 증가 추이를 보여주고 있습니다. SPACE_ALLOC 컬럼은 테이블에 할당된 바이트 수를 의미하며 SPACE_USAGE 컬럼은 그 중 몇 바이트가 실제로 사용되고 있는지를 나타내고 있습니다. 이 정보는 Automatic Workload Repository(AWR, 본 연재 제 6 주 참고)에 의해 수집된 데이타를 기반으로 합니다. 위 데이타 중 실제로 데이타가 수집된 것은 2004년 3월 9일입니다 (QUALITY 컬럼의 값이 “GOOD”인 것으로 확인합니다). 따라서 해당 시점의 할당 공간 및 사용 공간의 수치는 정확하다고 판단할 수 있습니다. 반면, 이후 모든 데이타의 QUALITY 컬럼은 “PROJECTED”의 값을 가지며, 이는 데이타가 AWR에 의해 수집된 데이타를 근거로 추정된 것임을 의미합니다.
3월 9일 이전 데이타의 경우 QUALITY 컬럼의 값이 “INTERPOLATED”로 표시되어 있습니다. 이 데이타는 수집되거나 추정된 것이 아니며, 단순히 수집된 데이타의 패턴에 대한 interpolation을 통해 얻어진 것입니다. 이처럼 데이타가 수집되지 않은 과거 시점이 존재하는 경우, 그 값은 interpolation을 통해 계산됩니다.
결론
세그먼트 단위의 관리 기능을 이용하여 세그먼트 내부의 공간에 대한 설정을 변경하고, 테이블 내부의 여유 공간을 재확보하거나 온라인 테이블 재구성 작업을 통해 성능을 향상시킬 수 있습니다. 10g의 새로운 기능은 테이블 재구성에 관련된 반복적인 업무를 절감하는 효과를 제공합니다. 특히 온라인 세그먼트에 대한 shrink 기능은, 내부 fragmentation을 제거하고 high water mark를 조정함으로써 풀 테이블 스캔의 성능을 극적으로 향상시키는 효과가 있습니다.
Shrink 작업에 관한 자세한 정보는 Oracle Database SQL Reference관련 항목을 참고하시기 바랍니다. DBMS_PACKAGEPL/SQL Packages and Types ReferenceChapter 88에서 설명되고 있습니다. 기술백서 The Self-Managing Database: Proactive Space & Schema Object Management는Oracle Database 10g의 공간 관리에 관련한 새로운 기능을 종합적으로 설명하고 있습니다. Oracle Database 10g온라인 데모 또한 OTN을 통해 제공되고 있습니다.
 
 
 
 
제 16 주
Transportable Tablespaces
10g의 transportable tablespace는 서로 다른 플랫폼 간의 데이타 이동을 지원하므로, 데이타 배포 작업을 한층 쉽고 빠르게 수행할 수 있습니다. 또, external table을 이용한 다운로드 기능을 활용하여 데이타 이동 및 변환 작업을 보다 효율적으로 완료할 수 있습니다.
데이타베이스 간의 데이타 이동 작업을 어떻게 처리하십니까? 여러 가지 방법이 있겠지만 그 중에서도 가장 돋보이는 것이 바로 transportable tablespace입니다. Transportable tablespace는 대상 테이블스페이스 집합이 자체적으로 다른 테이블스페이스에 있는 오브젝트를 참조하는 것이 없는 “self-contained”이어야 하며, 테이블스페이스를 읽기전용 상태로 설정한 뒤 메타데이타만을 먼저 익스포트(export)하고, OS 레벨의 카피 작업을 통해 데이타파일을 타겟 플랫폼으로 복사한 다음, 데이타 딕셔너리에 메타데이타를 임포트(이 프로세스를 “plugging”이라 부르기도 합니다.)하는 방식으로 데이타를 전송합니다. .
OS 파일 카피 작업은 SQL*Loader를 이용한 익스포트/임포트 작업과 같은 데이타 이동 방식에 비해 일반적으로 훨씬 빠른 처리 성능을 보입니다. 하지만 Oracle9i Database와 그 이전 버전의 경우, 소스 데이타베이스와 타겟 데이타베이스가 동일 OS플랫폼으로 구성되어야 한다는 제약사항 때문에 그 유용성에 제한을 받았습니다 (예를 들어 Solaris와 HP-UX 간의 테이블스페이스 전송은 불가능했습니다).
Oracle Database 10g에서는 이러한 기능 제약이 사라졌습니다. OS byte order가 동일하기만 하면 서로 다른 플랫폼 간이라도 테이블스페이스 전송이 가능해졌습니다. byte order에 대한 상세한 설명은 이 세션의 범위를 넘어서지만, 간략히 살펴보면 Windows를 포함하는 일부 운영체제의 경우, 멀티-바이트 바이너리 데이타를 저장할 때 least significant byte를 최하위 메모리 주소에 저장하는 방식을 사용합니다. 이러한 시스템을 “little endian”이라 부릅니다. 반면, Solaris를 비롯한 다른 운영체제는 most significant byte를 최하위 메모리 주소에 저장하며, 이러한 시스템을 “big endian”이라 부릅니다. Big-endian 시스템이 little-endian 시스템으로부터 데이타를 읽어 들이려면 변환 프로세스를 거쳐야 합니다. 그렇지 않은 경우, byte order 문제로 데이타가 올바르게 표시되지 않습니다. (Byte order에 대한 상세한 설명은 Embedded Systems Programming 2002년 1월호 기사, "Introduction to Endianness"를 참고하시기 바랍니다.) 하지만 동일한 endian을 갖는 플랫폼 간에 테이블스페이스를 전송하는 경우에는 변환 작업이 필요하지 않습니다.
그렇다면 어떤 운영체제가 어떤 byte order를 사용하는지 어떻게 알 수 있을 까요? 아래와 같은 쿼리를 사용하면 바로 확인할 수 있습니다:
SQL> select * from v$transportable_platform order by platform_id;


PLATFORM_ID PLATFORM_NAME                       ENDIAN_FORMAT
----------- ----------------------------------- --------------
          1 Solaris[tm] OE (32-bit)             Big
          2 Solaris[tm] OE (64-bit)             Big
          3 HP-UX (64-bit)                      Big
          4 HP-UX IA (64-bit)                   Big
          5 HP Tru64 UNIX                       Little
          6 AIX-Based Systems (64-bit)          Big
          7 Microsoft Windows IA (32-bit)       Little
          8 Microsoft Windows IA (64-bit)       Little
          9 IBM zSeries Based Linux             Big
         10 Linux IA (32-bit)                   Little
         11 Linux IA (64-bit)                   Little
         12 Microsoft Windows 64-bit for AMD    Little
         13 Linux 64-bit for AMD                Little
         15 HP Open VMS                         Little
         16 Apple Mac OS                        Big
인텔 기반 Linux 운영체제를 사용하는 SRC1서버의 USERS 테이블스페이스를, Microsoft Windows 기반 TGT1 서버로 전송하는 경우를 생각해 봅시다. 이 경우 소스 플랫폼과 타겟 플랫폼 모두 little endian type 시스템입니다. USERS 테이블스페이스의 데이타파일은 users_01.dbf입니다. 전송 작업은 아래와 같은 절차를 거쳐 수행됩니다:
  1. 테이블을 READ ONLY 상태로 설정합니다:
    alter tablespace users read only;
  2. 테이블을 익스포트 합니다.. 운영체제 프롬프트에서 다음과 같이 입력합니다:
    exp tablespaces=users transport_tablespace=y file=exp_ts_users.dmp
    exp_ts_users.dmp 파일은 메타데이타만을 포함하고 있으므로 그 크기가 매우 작습니다.
  3. exp_ts_users.dmp 파일과 users_01.dbf 파일을 TGT1 서버로 복사합니다. FTP를 사용하는 경우에는 binary 옵션을 설정합니다.
  4. 데이타베이스에 테이블스페이스를 “플러깅(plugging)” 합니다. 운영체제 프롬프트에서 다음과 같이 입력합니다.
    imp tablespaces=users transport_tablespace=y file=exp_ts_users.dmp datafiles='users_01.dbf'
4번째 단계를 마치고 나면 타겟 데이타베이스에 USERS 테이블스페이스가 생성되며, 테이블스페이스의 컨텐트도 사용 가능한 상태가 됩니다.
시스템 SRC1과 TGT1은 각각 Linux, Windows 운영체제를 사용한다는 사실을 명심하시기 바랍니다. 만일 Oracle9i 환경이었다면 TGT1의 데이타베이스가 users_01.dbf 데이타파일을 인식하지 못했을 것이고, 결국 전체 프로세스가 실패로 돌아갔을 것입니다. 이러한 경우라면 일반적인 익스포트/임포트 기능을 이용하거나, 플랫 파일을 생성한 뒤 SQL*Loader로 로드하거나, 데이타베이스 링크를 통해 direct load insert를 실행해야 할 것입니다.
10g에서는 타겟 데이타베이스가 다른 플랫폼으로부터 전송된 데이타파일을 정상적으로 인식하므로, 이러한 대안을 고려할 필요가 없습니다. 위의 예에서는 OS의 byte order 역시 동일하므로 (little endian), 변환 작업을 수행할 필요도 없습니다.
이 기능은 데이타 웨어하우스의 데이타가, 특수한 목적으로 운영되는 소규모 데이타 마트(data mart)에 정기적으로 전송되는 환경에서 특히 유용합니다. 10g 환경으로 구성된 경우, 데이타 웨어하우스는 대형 엔터프라이즈급 서버에, 데이타 마트는 Linux 기반 인텔 머신과 같은 저가형 서버에 구성하는 것이 가능해집니다. 이처럼 transportable tablespace를 사용하여 다양한 하드웨어와 운영체제를 조합한 환경을 구현할 수 있습니다.
서로 다른 endian을 갖는 시스템 간의 데이타 전송
소스 플랫폼과 타겟 플랫폼이 서로 다른 endian을 갖는 경우 어떻게 데이타 전송을 처리할 수 있을까요? 앞에서 설명한 것처럼 타겟 서버와 소스 서버의 byte order가 다르면 전송된 데이타를 올바르게 인식할 수 없으므로, 단순 카피 작업으로 데이타 파일을 이동하는 것이 불가능합니다. 하지만 방법은 있습니다. 바로 Oracle 10g RMAN 유틸리티가 데이타파일을 다른 byte order로 변환하는 기능을 지원하고 있습니다.
위의 예에서, 만일 SRC1 서버가 Linux(little endian)를 기반으로 하고, TGT1 서버가 HP-UX(big endian)을 기반으로 한다면, 3단계와 4단계의 사이에 변환을 위한 별도의 단계를 적용해야 합니다. RMAN을 사용하면 Linux 환경의 데이타파일을 HP-UX 포맷으로 변환할 수 있습니다 (단 테이블스페이스가 읽기전용 상태로 설정되어 있어야 합니다).
RMAN> convert tablespace users
2> to platform 'HP-UX (64-bit)'
3>  format='/home/oracle/rman_bkups/%N_%f';

Starting backup at 14-MAR-04
using channel ORA_DISK_1
channel ORA_DISK_1: starting datafile conversion
input datafile fno=00004 name=/usr/oradata/dw/starz10/users01.dbf
converted datafile=/home/oracle/rman_bkups/USERS_4
channel ORA_DISK_1: datafile conversion complete, elapsed time: 00:00:07
Finished backup at 14-MAR-04
위 과정을 거치면 /home/oracle/rman_bkups 디렉토리에 표준 RMAN 파일 포맷의 파일이 <tablespace_name>_<absolute_datafile_no> 의 파일명으로 생성됩니다. 결국 USERS 테이블스페이스 자체는 전혀 변경되지 않았고, HP-UX 환경을 위한 새로운 파일이 생성되었습니다. 이제 이 파일을 타겟 시스템으로 복사한 뒤 위에서 설명한 것과 같은 처리 과정을 거치면 됩니다.
RMAN 변환 명령은 매우 강력합니다. 위와 같은 명령을 사용하는 경우, RMAN은 순차적으로 데이타파일을 생성합니다. 여러 개의 데이타파일을 포함하는 테이블스페이스를 처리할 때에는 여러 개의 변환 프로세스를 병렬적으로 수행하도록 명령할 수도 있습니다. 그렇게 하려면 위 명령에 아래 구문을 삽입하면 됩니다:
parallelism = 4
위와 같이 하면 네 개의 RMAN 채널이 생성되어 각각 별도의 데이타파일에 대해 변환 작업을 수행합니다. 하지만 parallelism이 정말로 효과를 발휘하는 것은, 많은 수의 테이블스페이스를 한꺼번에 변환할 때입니다. 아래는 두 개의 테이블스페이스(USERS와 MAINTS)를 HP-UX 포맷으로 변경하는 예입니다:
RMAN> convert tablespace users, maints
2> to platform 'HP-UX (64-bit)'
3> format='/home/oracle/rman_bkups/%N_%f'
4> parallelism = 5;

Starting backup at 14-MAR-04
using target database controlfile instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: sid=244 devtype=DISK
allocated channel: ORA_DISK_2
channel ORA_DISK_2: sid=243 devtype=DISK
allocated channel: ORA_DISK_3
channel ORA_DISK_3: sid=245 devtype=DISK
allocated channel: ORA_DISK_4
channel ORA_DISK_4: sid=272 devtype=DISK
allocated channel: ORA_DISK_5
channel ORA_DISK_5: sid=253 devtype=DISK
channel ORA_DISK_1: starting datafile conversion
input datafile fno=00004 name=/usr/oradata/dw10/dw10/users01.dbf
channel ORA_DISK_2: starting datafile conversion
input datafile fno=00005 name=/usr/oradata/dw10/dw10/users02.dbf
channel ORA_DISK_3: starting datafile conversion
input datafile fno=00006 name=/usr/oradata/dw10/dw10/maints01.dbf
channel ORA_DISK_4: starting datafile conversion
input datafile fno=00007 name=/usr/oradata/dw10/dw10/maints02.dbf
converted datafile=/home/oracle/rman_bkups/USERS_4
channel ORA_DISK_1: datafile conversion complete, elapsed time: 00:00:03
converted datafile=/home/oracle/rman_bkups/USERS_5
channel ORA_DISK_2: datafile conversion complete, elapsed time: 00:00:00
converted datafile=/home/oracle/rman_bkups/MAINTS_6
channel ORA_DISK_3: datafile conversion complete, elapsed time: 00:00:01
converted datafile=/home/oracle/rman_bkups/MAINTS_7
channel ORA_DISK_4: datafile conversion complete, elapsed time: 00:00:01
Finished backup at 14-MAR-04
위의 실행결과에서, 변환된 파일이 기존 파일명과 무관하고 이해하기도 어려운 파일명을 갖게 되는 것을 볼 수 있습니다 (예를 들어, users01.dbf는 USERS_4로 변환됩니다). 원하는 경우 데이타파일의 naming format을 변경할 수 있습니다. 이 프로세스는 Data Guard에서 사용하는 데이타파일 naming 방식과 유사합니다:
RMAN> convert tablespace users
2> to platform 'HP-UX (64-bit)'
3> db_file_name_convert '/usr/oradata/dw10/dw10','/home/oracle/rman_bkups'
4> ;

Starting backup at 14-MAR-04
using channel ORA_DISK_1
channel ORA_DISK_1: starting datafile conversion
input datafile fno=00004 name=/usr/oradata/dw10/dw10/users01.dbf
converted datafile=/home/oracle/rman_bkups/users01.dbf
channel ORA_DISK_1: datafile conversion complete, elapsed time: 00:00:03
channel ORA_DISK_1: starting datafile conversion
input datafile fno=00005 name=/usr/oradata/dw10/dw10/users02.dbf
converted datafile=/home/oracle/rman_bkups/users02.dbf
channel ORA_DISK_1: datafile conversion complete, elapsed time: 00:00:01
Finished backup at 14-MAR-04 
위와 같이 하면 기존의 파일명을 그대로 유지할 수 있습니다. /home/oracle/rman_bkups 디렉토리에 가 보면, users01.dbf와 users02.dbf가 생성된 것을 확인할 수 있습니다. 위 예제의 경우, 파일의 변환 작업은 소스 플랫폼에서 수행되었습니다. 필요한 경우 타겟 플랫폼에서 변환을 수행할 수도 있습니다. 예를 들어, users01.dbf를 HP-UX 기반의 TGT1 서버로 카피한 후 아래와 같이 HP-UX 포맷으로 변환할 수 있습니다:
In the above cases, we converted the files on the source platform. However, you can do that on the target platform as well. For example, you can copy file users01.dbf to host TGT1 running HP-UX and then convert the file to HP-UX format with:
RMAN> convert
2> datafile '/usr/oradata/dw10/dw10/users01.dbf'
3> format '/home/oracle/rman_bkups/%N_%f'
4> ;
이렇게 함으로써 해당 디렉토리에 지정된 포맷의 파일을 생성할 수 있습니다.
그렇다면 데이타파일을 굳이 타겟 플랫폼에서 변환하는 이유가 무엇일까요? 첫 번째로, 소스 플랫폼의 테이블스페이스를 READ ONLY 상태로 두는 기간이 짧아지므로 다운타임을 줄일 수 있다는 점을 들 수 있습니다. 데이타파일을 3중 미러 형태로 구성하고 테이블스페이스를 읽기 전용으로 설정한 다음, 3번째 미러를 분리한 후 곧바로 테이블스페이스를 읽기/쓰기 모드로 변경할 수도 있습니다. 분리된 3번째 미러는 타겟 시스템에 마운트된 후 변환됩니다. 이렇게 하면 테이블스페이스가 읽기 전용 상태에 있는 기간을 최소화할 수 있습니다.
또 다른 이유로 성능을 들 수 있습니다. 지속적으로 부하가 발생하는 OLTP 데이타베이스에서 RMAN 변환 작업을 수행함으로써 시스템에 불필요한 부담을 주게 될 수 있습니다. 그 대신, 병렬 작업에 최적화된 데이타 웨어하우스 서버에서 오프라인 형태로 변환 작업을 처리하는 것이 바람직할 수 있습니다.
External Table을 데이타 전송 매개체로 활용하기
Oracle9i Database에서 처음 소개된 external table은 일정한 형식을 갖춘 일반 텍스트 파일을 테이블처럼 보이게 하고 SQL 구문을 통해 접근할 수 있게 하는 기능입니다. OLTP 데이타베이스에서 운영 중인 TRANS 테이블의 컨텐트를, external table을 사용하여 데이타 웨어하우스 데이타베이스로 이동해야 하는 경우를 생각해 봅시다. 그 과정이 아래와 같습니다:
  1. OLTP 데이타베이스에서, TRANS 테이블의 컨텐트를 포함하는 텍스트 파일을 생성합니다. 생성된 텍스트 파일을 /home/oracle/dump_dir 디렉토리에 trans_flat.txt라는 이름으로 저장합니다. (SQL 구문을 이용하여 텍스트 파일의 생성이 가능합니다.)
    spool trans_flat.txt
    select <column_1> ||','|| <column_2> ||','|| ...
    from trans;
    spool off
  2. ftp, rcp 등의 전송 메커니즘을 사용하여 파일을 데이타 웨어하우스 서버에 복사합니다. (파일은 /home/oracle/dump_dir 디렉토리에 위치하고 있습니다.) 데이타 웨어하우스 데이타베이스에서 dump_dir 디렉토리를 생성합니다:
  3. On the data warehouse database, create a directory object named dump_dir as:
    create directory dump_dir as '/home/oracle/dump_dir';
  4. external table을 생성합니다:
    create table trans_ext
    (
       ... <columns of the table> ...
    )
    organization external
    (
       type oracle_loader
       default directory admin
       access parameters
       (
          records delimited by newline
          badfile 'trans_ext.bad'
          discardfile 'trans_ext.dis'
          logfile 'trans_ext.log'
          fields terminated by ","  optionally enclosed by '"'
          (
              ... <columns> ...
          )
       )
       location ('trans_flat.txt')
    )
    reject limit unlimited;
  5. Direct load insert, merge 등의 일반적인 방법을 사용하여 external table을 일반 테이블로 로드합니다.
위에서 텍스트 파일을 생성하는 첫 번째 단계는 가장 많은 시간을 소요합니다. SQL 구문을 사용하여 텍스트를 생성하고 파일에 스풀링하는 과정은 절차 상으로는 간단하지만 실행 시간이 오래 걸립니다. SQL*Plus 대신 Pro*C 또는 OCI 프로그램을 사용하여 처리 시간을 어느 정도 단축할 수 있지만 그래도 꽤 오랜 시간이 필요합니다. 컬럼을 수작업으로 지정하는 것도 작업을 지체시키는 요인이 됩니다.
이 두 가지 문제는 10g에서 완전히 해결되었습니다. 이제 external table 생성 프로세스를 사용하여 테이블을 포터블 포맷으로 신속하게 언로드할 수 있습니다. 위 예의 첫 번째 단계는 아래와 같은 간단한 SQL 구문으로 대치됩니다:
create directory dump_dir as '/home/oracle/dump_dir';

create table trans_dump
organization external
(
   type oracle_datapump
   default directory dump_dir
   location ('trans_dump.dmp')
)
as
select * from trans
/
위 명령은 /home/oracle/dump_dir 디렉토리에 trans_dump.dmp라는 이름의 파일을 생성합니다. 이 파일은 ASCII 텍스트 파일이 아닙니다. 메타데이타는 일반 텍스트이지만, 실제 데이타는 raw 포맷을 사용하고 있습니다. 하지만, 이 파일은 export dump 파일과 마찬가지로 모든 운영체제에서 호환 가능하며, 데이타의 다운로드가 매우 빠르게 수행된다는 점에서 export와 차별화됩니다. 이 파일을 데이타 웨어하우스 서버에 카피하고 위에서 설명한 것과 동일한 방법으로 external table을 생성할 수 있습니다.
그렇다면 지금까지 설명한 방법이 기존에 사용되어 오던 데이타 전송 메커니즘과 어떤 차이가 있는 것일까요? 첫 번째로, 복잡한SQL 구문을 작성하지 않고도 포터블 파일을 매우 빠르게 생성할 수 있습니다. 두 번째로, 이 파일을 external table의 input으로 적용해서 일반적인 테이블을 다루듯 다른 테이블로의 데이타 로드 작업을 간단하게 완료할 수 있습니다. 또 아래와 같은 구문을 사용하면 external table로의 데이타 다운로드 성능을 향상시킬 수 있습니다:
create table trans_dump
organization external
(
   type oracle_datapump
   default directory dump_dir
   location ('trans_dump.dmp')
)
parallel 2
as
select * from trans
/
위 명령은 병렬적인 형태로 파일 생성 작업을 수행하도록 합니다. 이 방법은 멀티-CPU 환경에서 유용합니다. 이와 별도로, 동시에 여러 개의 external table을 생성하도록 할 수도 있습니다:
create table trans_dump
organization external
(
   type oracle_datapump
   default directory dump_dir
   location ('trans_dump_1.dmp','trans_dump_2.dmp')
)
parallel 4
as
select * from trans
/
위 명령은 trans_dump_1.dmp와 trans_dump_2.dmp라는 두 개의 파일을 생성합니다. 이 방법은 파일을 여러 개의 물리적 디바이스 또는 컨트롤러로 분산하고 I/O 성능을 향상시키는데 유용합니다.
결론
10g의 transportable tablespace를 적극적으로 활용함으로써, 분석된 데이타가 더 신속하게, 그리고 더 높은 빈도로 사용자에게 제공되는 환경을 구현할 수 있습니다. 또 이 기능은 오프라인 미디어를 통해 이기종 시스템의 데이타베이스로 데이타를 배포하는 데에도 이용됩니다. External table을 이용한 다운로드 기능은 대용량 데이타 처리를 위한 ETL 툴로써 손색이 없습니다.
Furthermore, by making transportable tablespaces viable, 10g makes data refreshes quicker and more frequent so that analyzed data is available to end users sooner. This capability can also be used to publish data via offline media to different databases, regardless of their host systems. Using external table downloads the utility to move large quantities of data as an ETL tool is finally available to the end user.
10g의 테이블스페이스 전송 기능에 대한 자세한 설명은 Oracle Database Administrator's Guide의 Chapter 8, "Transporting Tablespaces Between Databases" 섹션을 참고하시기 바랍니다.
 
 
 
 
제 17 주
Automatic Shared Memory Management
오라클 인스턴스의 메모리 풀에 필요한 만큼의 메모리를 할당하는 작업 때문에 번거로우셨습니까? ? Automatic Shared Memory Management 기능을 이용하여 필요한 영역에 메모리를 자동으로 할당할 수 있습니다.
여러분이 초심자이든, 또는 숙련된 DBA이든 아래와 같은 에러를 최소한 한 번쯤은 경험해 보셨을 것입니다:
ORA-04031: unable to allocate 2216 bytes of shared memory ("shared pool"... ...
또는:
ORA-04031: unable to allocate XXXX bytes of shared memory 
("large pool","unknown object","session heap","frame") 
또는:
ORA-04031: unable to allocate bytes of shared memory ("shared pool",
 "unknown object","joxlod: init h", "JOX: ioc_allocate_pal")
첫 번째 에러의 원인은 명백해 보입니다. shared pool에 할당된 메모리가 사용자 요청에 응답하기에 충분하지 못하기 때문입니다 (이 경우 shared pool의 크기가 문제가 아니라, 바인드된 변수를 사용하지 않는 데 따르는 과도한 파싱 작업으로 인해 발생한 fragmentation이 원인일 수도 있습니다. 이것인 필자가 즐겨 언급하는 주제이긴 하지만, 여기에서는 당면한 과제에 집중하기로 합시다.) 두 번째와 세 번째 에러는 각각 large pool과 Java pool의 공간이 충분하지 못한 것이 원인이 되어 발생합니다.
애플리케이션의 변경 작업을 거치지 않고 이 에러를 해결해야 합니다. 그렇다면 어떤 방법을 사용해야 할까요? 결국 사용 가능한 메모리를 오라클 인스턴스가 사용하는 모든 풀에 어떻게 분배할 것인가의 문제로 귀결됩니다.
어떻게 배분할 것인가?
오라클 인스턴스의 System Global Area (SGA)는 buffer cache, shared pool, Java pool, large pool, redo log buffer 등의 메모리 영역을 포함하고 있습니다. 각각의 풀은 고정된 크기의 운영체제의 메모리 공간을 점유하며, 그 크기는 초기화 매개변수 파일을 통해 DBA가 지정할 수 있습니다.
4종류의 풀(db block buffer cache, shared pool, Java pool, large pool)은 SGA 공간의 대부분을 차지합니다. (redo log buffer는 상대적으로 작은 공간을 사용할 뿐 아니라, 그 성격상 이 문서에서 논의되는 내용과 무관합니다.) DBA는 각각의 영역에 할당된 메모리가 충분한지 점검해야 합니다.
각 영역의 크기를 2GB, 1GB, 1GB, 1GB로 설정하기로 결정한 경우를 가정해 봅시다. 먼저 아래와 같이 초기화 매개변수를 설정하여 데이타베이스 인스턴스의 풀 사이즈를 변경합니다:
db_cache_size = 2g
shared_pool_size = 1g
large_pool_size = 1g
java_pool_size = 1g
이제 이 매개변수를 자세히 분석해 봅시다. 과연 설정된 값이 정확하다는 걸 보증할 수 있을까요?
아마 여러분 모두 나름대로 의심을 품고 있을 것입니다. 실제로 각각의 풀에 필요한 만큼의 공간이 정확히 할당되었다고 자신할 수 있는 이는 아무도 없습니다. 필요한 메모리 공간은 데이타베이스 내부 프로세싱에 따라 결정되며, 이는 시시각각으로 변화하기 때문입니다.
예제를 통해 설명해 보겠습니다. “전형적인” OTLP 데이타베이스 환경에 상대적으로 적은 용량의 메모리가 buffer cache에 할당되어 있습니다. 어느 날, 사용자가 일별 마감 보고서를 작성하기 위해 대규모의 테이블 스캔 작업을 실행합니다. Oracle9i Database는 온라인 상태에서 메모리 할당량을 변경하는 기능을 제공합니다. 전체 메모리 용량이 제한되어 있는 상황에서, DBA는 large pool과 Java pool에 할당된 일부 공간을 buffer cache로 돌리기로 결정합니다:
alter system set db_cache_size = 3g scope=memory;
alter system set large_pool_size = 512m scope=memory;
alter system set java_pool_size = 512m scope=memory;
이 방법은 일시적으로 그 효과를 발휘합니다. 하지만 large pool을 사용하는 RMAN 작업이 야간에 실행되면서 large pool의 용량이 부족해집니다. DBA가 이번에는 db cache의 용량 일부를 large pool에 할당합니다.
RMAN 작업은 완료되었지만, 다음에는 Java pool을 사용하는 배치 프로그램이 실행됩니다. Java pool에 관련한 에러를 확인한 DBA는 Java pool과 db cache의 용량을 확보하기 위해 아래와 같이 실행합니다:
alter system set db_cache_size = 2G scope=memory;
alter system set large_pool_size = 512M scope=memory;
alter system set java_pool_size = 1.5G scope=memory;
다음날 아침, OLTP 작업이 재개되고 DBA는 똑같은 과정을 다시 반복해야 합니다!
이러한 악순환의 고리를 끊어버리기 위해 각각의 풀에 최대한의 용량을 영구적으로 할당하는 방법을 고려할 수 있습니다. 하지만 사용 가능한 실제 메모리보다 많은 용량을 SGA 영역에 할당함으로써 스와핑과 페이징이 빈번하게 발생하는 위험을 감수해야만 합니다. 차라리 수작업으로 메모리를 재할당하는 방법이 (번거롭긴 하지만) 적절해 보입니다.
또 다른 방법으로 각 풀에 합리적인 선의 최소 용량을 할당하는 방법을 생각해 볼 수 있습니다. 하지만 요구되는 용량이 증가할 때마다 성능이 저하될 것입니다.
어떤 방법을 사용하든 SGA에 할당된 전체 메모리 용량은 변하지 않는 반면, 각각의 풀에 할당되는 용량은 자주 변경되어야 합니다. 그렇다면, RDBMS가 사용자의 요구를 감지하고 메모리를 자동으로 재할당한다면 작업이 훨씬 수월해지지 않을까요?
Oracle Database 10g의 Automatic Shared Memory Management가 바로 이러한 기능을 제공합니다. SGA_TARGET 매개변수를 통해 SGA의 전체 사이즈를 설정하고 나면, SGA 내부의 각 풀은 워크로드를 기준으로 다이내믹하게 관리됩니다. 결국 DBA가 해야 할 일은 SGA_TARGET 매개변수를 설정하는 것 밖에 없게 됩니다.
Automatic Shared Memory Management 설정
예를 통해 설명해 보겠습니다. 먼저, SGA의 전체 크기를 결정합니다. 현재 얼마나 많은 용량이 할당되어 있는지 확인하려면 아래와 같이 입력합니다:
SQL> select sum(value)/1024/1024 from v$sga;

SUM(VALUE)/1024/1024
--------------------
                 500
SGA의 현재 크기는 약 500MB로 설정되어 있으므로 이 수치를 SGA_TARGET에 적용하면 됩니다. 다음으로 아래와 같이 구문을 실행합니다:
alter system set sga_target = 500M scope=both;
위와 같이 설정함으로써, 각각의 풀에 대한 용량을 설정할 필요가 없게 됩니다. 이제 각각의 풀에 관련한 매개변수의 값을 0으로 설정하거나, 해당 항목을 완전히 삭제합니다.
shared_pool_size = 0
large_pool_size = 0
java_pool_size = 0
db_cache_size = 0     
데이타베이스를 다시 시작하고 변경된 설정을 적용합니다.
같은 작업을 Enterprise Manager 10g에서 수행할 수도 있습니다. 데이타베이스 홈 페이지에서 “Administration” 탭을 선택한 후 “Memory Parameter”를 클릭합니다. 메모리 관련 매개변수가 수동 설정되어 있는 경우에는 설정된 항목별로 “Enable” 버튼이 표시될 것입니다. “Automatic Shared Memory Management” 옆에 있는 “Enable” 버튼을 눌러 Automatic Shared Memory Management 기능을 활성화합니다. 나머지 작업은 Enterprise Manager에 의해 자동 수행됩니다.
설정 작업을 마친 후 각 풀의 크기를 확인하려면 아래와 같이 실행합니다:
SQL> select current_size from v$buffer_pool;

CURRENT_SIZE
------------
         340

SQL> select pool, sum(bytes)/1024/1024 Mbytes from v$sgastat group by pool;

POOL             MBYTES
------------ ----------
java pool             4
large pool            4
shared pool         148
설정 작업을 마친 후 각 풀의 크기를 확인하려면 아래와 같이 실행합니다:
figure 1
그림 1: 초기 할당 결과
이제 오라클에서 사용 가능한 메모리 크기가 500MB에서 300MB로 줄었다고 가정해 봅시다. 먼저 SGA 영역을 위해 설정된 target size를 변경해야 할 것입니다:
alter system set sga_target = 300M scope=both;
다시 메모리 할당 현황을 점검하면 아래와 같은 결과를 확인할 수 있습니다:
SQL> select current_size from v$buffer_pool;

CURRENT_SIZE
------------
         244

SQL> select pool, sum(bytes)/1024/1024 Mbytes from v$sgastat group by pool;

POOL             MBYTES
------------ ----------
java pool             4
large pool            4
shared pool          44
전체 사용 공간은 240+4+4+44 = 296MB로, 전체 용량(300MB)와 거의 일치합니다. SGA_TARGET을 변경한 후의 메모리 할당 내역이 그림 2와 같습니다.
figure 2
그림 2: SGA size를 300MB로 변경한 후의 할당 결과
풀의 크기는 다이내믹하게 조정됩니다. 워크로드가 증가하면 그에 비례하여 풀의 크기도 증가하고, 다른 풀에 관련된 워크로드가 증가하는 경우에는 줄어들기도 합니다. 이러한 확장/수축 과정은 DBA의 개입 없이 자동적으로 수행됩니다. 앞부분에 설명한 예에서 대량의 large pool을 사용하는 RMAN 작업이 시작되는 경우, large pool은 자동적으로 4MB에서 40MB로 증가합니다. 그림 3에서 보여지는 것처럼, 추가로 필요한 36MB는 db block buffer에서 가져오고, 결과적으로 db block buffer는 줄어들 것입니다.
figure 3
그림 3: large pool의 수요가 증가하는 경우의 자동 할당 결과
변경되는 풀의 크기는 워크로드에 따라 달라집니다. SGA의 전체 크기도 언제나 SGA_TARGET에서 지정한 최대치를 넘지 않으므로, 실제 메모리보다 많은 영역을 할당하여 과도한 페이징과 스와핑을 발생시키는 문제를 방지할 수 있습니다. SGA_MAX_SIZE 매개변수를 조정하면 SGA_TARGET의 값을 가능한 최대치인 SGA_MAX_SIZE까지 다이내믹하게 증가시킬 수 있습니다.
자동 튜닝을 지원하지 않는 Pool.
SGA에서 관리되는 풀 중 일부는 다이내믹한 변경을 허용하지 않으며, 따라서 명시적으로 설정되어야 합니다. 특히 비표준 블록 사이즈를 갖는 buffer pool과 KEEP / RECYCLE 풀의 경우가 그러합니다. 데이타베이스의 블록 사이즈가 8K인 환경에서, 2K, 4K, 16K, 32K 등의 블록 사이즈를 갖는 풀들을 구성하려면, 수작업으로 설정하는 방법 밖에 없습니다. 이렇게 설정된 풀의 크기는 고정된 값을 유지하며, 부하 수준에 따라 확장되거나 수축되지 않습니다. KEEP/RECYCLE 풀을 구성하는 경우에도 마찬가지입니다. log buffer 역시 다이내믹하게 조정되지 않습니다. 또한 log_buffer 매개변수를 통해 설정된 값은 고정된 값을 유지합니다. (10g에서는 “Streams pool”이라는 새로운 유형의 풀을 지원합니다. 이 풀은 streams_pool_size 매개변수를 통해 설정되며, 역시 자동 메모리 튜닝을 지원하지 않습니다.)
여기서 한 가지 의문이 생깁니다. 비표준 블록 사이즈를 갖는 풀을 설정하는 경우, 다른 풀들에 관련한 메모리 자동 관리 기능에는 어떤 영향이 있을까요?
자동 튜닝이 불가능한 매개변수(예: db_2K_cache_size)를 설정하는 경우, 데이타베이스는 SGA_TARGET에서 이 매개변수의 값을 차감한 후 남은 공간을 활용하여 자동 메모리 관리를 수행합니다. 예를 들어, 아래와 같이 설정된 경우를 가정해 봅시다:
sga_target = 500M
db_2k_cache_size = 50M
다른 풀 관련 매개변수는 설정되어 있지 않은 것으로 가정합니다. 이 경우, 2KB buffer pool에 할당된 50MB를 제외한 450MB가 default block size buffer pool(db_cache_size), shared pool, Java pool, large pool 등의 자동 관리에 이용됩니다. 자동 튜닝이 불가능한 매개변수에 변경이 발생하는 경우, 자동 튜닝에 사용되는 메모리 크기 역시 변경됩니다. 예를 들어, db_2K_cache_size의 값을 50MB에서 100MB로 변경하면, shared pool, large pool, default buffer pool에 사용되는 메모리 크기는 450MB에서 400MB로 자동 변경됩니다 (그림 4 참조).
figure 4
그림 4: 자동 튜닝을 지원하지 않는 버퍼 매개변수를 변경한 경우
하지만 메모리가 충분하거나, 위에서 언급한 위험요소를 걱정할 필요가 없다면 Automatic Memory Management 기능을 사용하지 않아도 무방합니다. 이 경우 SGA_TARGET 매개변수를 정의하지 않거나 그 값을 (ALTER SYSTEM 명령을 통해서, 또는 매개변수 파일 편집을 통해서) 0으로 설정하면 됩니다. SGA_TARGET이 0으로 설정되면, 각 풀의 현재 크기가 매개변수 값으로 자동 설정됩니다.
Enterprise Manager의 활용
Enterprise Manager 10g을 이용해서 이 매개변수들을 조작할 수도 있습니다. 데이타베이스 홈 페이지에서 “Memory Parameters”를 클릭하면 그림 5와 같은 화면이 표시됩니다.
figure 5
Enterprise Manager 10g을 이용해서 이 매개변수들을 조작할 수도 있습니다. 데이타베이스 홈 페이지에서 “Memory Parameters”를 클릭하면 그림 5와 같은 화면이 표시됩니다.
붉은색 원으로 표시된 항목을 참고하시기 바랍니다. 데이타베이스는 Automatic Shared Memory Management 모드로 동작하고 있으며 전체 메모리 크기는 564MB로, SGA_TARGET 매개변수에 설정된 값과 동일합니다. 이 화면에서 설정값을 수정하고 Apply 버튼을 누르면 매개변수 값이 자동으로 조정됩니다.
각 풀의 최소 메모리 크기 지정
SGA_TARGET을 600MB로 설정하고 Automatic Shared Memory Management를 이용하는 경우를 가정해 봅시다. 자동 설정된 풀의 크기가 아래와 같습니다:

사이즈 (MB)
Buffer 404
Java 4
Large 4
Shared 148

Java pool과 large pool의 크기(각각 4MB)가 너무 작다고 판단되는 경우, 각 영역의 최소값을 (8MB, 16MB 등으로) 설정하여 온라인 상태에서 적용할 수 있습니다. 아래와 같이 ALTER SYSTEM 명령을 통해 최소값을 명시하면 그 결과가 다이내믹하게 적용됩니다:
alter system set large_pool_size = 16M;
alter system set java_pool_size = 8M;
이제 풀의 크기를 다시 조회하면 아래와 같이 달라진 것을 확인할 수 있습니다:
SQL> select pool, sum(bytes)/1024/1024 Mbytes from v$sgastat group by pool;

POOL             MBYTES
------------ ----------
java pool             8
large pool           16
shared pool         148

SQL> select current_size from v$buffer_pool;

CURRENT_SIZE
------------
         388
재할당된 풀의 메모리 크기가 아래와 같습니다:

사이즈 (MB)
Buffer 388
Java 8
Large 16
Shared 148

Java pool과 large pool은 각각 8MB, 16MB로 재조정되었고, 전체 SGA의 크기는 600MB 이하를 유지하고 있습니다 (buffer pool의 크기가 404MB에서 388MB로 감소했습니다). 물론 Automatic Shared Memory Management는 여전히 동작하고 있습니다. 방금 전에 설정한 값은 풀의 최소 크기를 정의한 것이며, 이제 Java pool과 large pool은 8MB, 16MB 이하의 크기로 줄어들지 않을 것입니다.
결론
Oracle SGA에서 관리되는 각 풀의 메모리 요구량은 시스템 상황에 따라 끊임없이 변화합니다. Oracle Database 10g의 Automatic Shared Memory Management 기능은 필요한 영역에 자원을 다이내믹하게 할당함으로써, 시스템 메모리 자원을 보다 효율적으로 이용할 수 있게 합니다. 이처럼 메모리 관리를 효율화함으로써 메모리 요구량을 줄이고, 하드웨어 비용을 절감할 수도 있습니다.
다음 주 주제: Automatic Database Diagnostic Monitor (ADDM)Chapter 7을 참고하십시오.
 
 
 
 
제18 주
ADDM과 SQL Tuning Advisor
이제 Oracle Database가 직접 제공하는 SQL 튜닝 서비스를 활용해 보십시오! SQL Profile를 이용하여 쿼리 성능을 향상시키고 ADDM을 통해 일반적인 성능 문제를 쉽고 빠르게 해결하는 방법을 배워보십시오.
오늘은 조용한 하루입니다. 데이타베이스에도 문제가 없어 보입니다. DBA는 긴장을 풉니다. 미루어 두었던 RMAN 매개변수, 블록 사이즈 점검 작업을 하기에 좋은 날입니다.
갑자기 개발자 한 명이 DBA의 자리로 달려 옵니다. 그가 사용하는 SQL 쿼리의 성능이 저하되었습니다. 그가 말합니다. “가능한 한 빨리 해결해 주셨으면 좋겠습니다.”
어쩌면 긴장을 너무 빨리 풀었는지도 모릅니다. 데이타베이스의 성능과 보안을 향상시키려는 기존의 계획은 뒤로 밀리고, 오늘도 급한 불을 끄기 위해 하루를 보내야 합니다.
DBA는 반복적인 잡무에서 벗어나 좀 더 전략적인 과제에 집중할 수 있기를 원합니다. 그렇다면 반복적인 업무를 대신해 줄 조수를 하나 고용하면 좋지 않을까요?
Oracle Database 10g에 추가된 Automatic Database Diagnostic Monitor(ADDM)이 바로 이런 역할을 해 줍니다. ADDM은 데이타베이스 성능 통계에 대한 철저한 분석을 통해 성능 병목을 확인하고, SQL 구문에 대한 분석을 통해 성능 향상을 위한 조언을 제공합니다. 또 SQL Tuning Advisor와 연동된 기능을 제공하기도 합니다. 이번 연재에서는, ADDM을 이용한 성능 향상 방안에 대해 설명합니다.
Automatic Database Diagnostic Monitor (ADDM)
제 6 주 연재에서, Automatic Workload Repository (AWR)에 대해 설명한 바 있습니다. AWR은 데이타베이스로부터 상세한 성능 관련 지표를 주기적으로 수집하여 저장합니다. 스냅샷 생성 작업이 완료될 때마다, ADDM이 호출되어 서로 다른 스냅샷의 데이타와 성능 지표를 비교 분석하고 성능 향상을 위한 조언을 제공합니다. 문제가 발견된 후, ADDM은 다른 어드바이저 툴(SQL Tuning Advisor 등)을 호출하여 해결 방법을 찾아내기도 합니다.
예를 통해 ADDM의 기능을 설명해 보도록 하겠습니다. 원인이 확인되지 않은 성능 문제에 대한 진단 작업에 착수한 경우를 가정해 봅시다. DBA는 문제가 되는 SQL 구문이 무엇인지 확인해 둔 상태입니다. 그러나 실제 환경에서는 이와 같은 유용한 단서도 모를 경우도 있습니다.
10g에서 진단 작업을 수행하는 과정에서, drill-down분석을 위해 적절한 시간 간격을 두고 생성된 스냅샷들을 선택할 수 있습니다. Enterprise Manager 10g의 데이타베이스 홈 페이지에서 “Advisor Central”을 선택하고 “ADDM”을 클릭하면 그림 1과 같은 화면이 표시됩니다.
figure 1
그림 1: ADDM 태스크의 생성
이 화면에서 ADDM을 이용한 분석 작업을 생성할 수 있습니다. 성능 문제가 오후 11시에 발생했음을 알고 있는 DBA는, “Period Start”와 “Period End”의 값을 설정하여 해당 시간 대의 스냅샷들을 선택합니다. (붉은색 원으로 표시된) 카메라 모양의 아이콘을 클릭하여 스냅샷 시작 시간과 종료 시간을 지정할 수도 있습니다. 시간대를 설정하고 난 뒤 “OK” 버튼을 누르면 그림 2와 같은 화면이 표시됩니다.
figure 2
그림 2: ADDM 분석 결과
ADDM은 해당 시간대에 관련한 두 가지 성능 문제를 발견해 냈습니다. 일부 SQL 구문이 지나치게 많은 CPU 시간을 사용하고 있으며, 이로 인해 데이타베이스의 성능이 전체적으로 저하되었습니다. 발견된 내용을 근거로, ADDM은 해당 구문에 대한 SQL 튜닝을 수행할 것을 권고하고 있습니다.
각각의 문제 항목을 클릭하면 그림 3에서 보여지는 것과 같은 상세 정보를 얻을 수 있습니다.
figure 3
그림 3: ADDM 분석 결과 상세 정보
위 화면에서 문제의 원인이 된 SQL 구문을 확인할 수 있습니다. ADDM은 SQL Tuning Advisor를 이용해 이 SQL 구문에 대한 분석 작업을 수행할 것을 권고하고 있습니다. “Run Advisor Now” 버튼을 클릭하면 SQL Tuning Advisor가 호출되어 분석 작업을 시작합니다.
그림 2 화면의 “View Report” 버튼을 참고하시기 바랍니다. 개별 웹 페이지 별로 권고 사항을 제시하는 것과 별도로, ADDM은 전체 분석 결과에 대한 텍스트 리포트를 생성합니다. Listing 1에서 생성된 텍스트 리포트의 내용을 확인하실 수 있습니다. 텍스트 리포트는 문제가 되는 SQL 구문과 그 hash value 등의 상세한 정보를 제공합니다. 또 텍스트 리포트의 SQL ID 정보를 이용하여 SQL Tuning Advisor 또는 커맨드 라인을 통한 분석 작업을 수행할 수 있습니다.
ADDM은 AWR 스냅샷이 생성될 때마다 호출되고, 가장 최근의 스냅샷과의 비교를 통해 권고사항을 제시합니다. 따라서 비교해야 할 두 스냅샷이 서로 인접한 경우에는 (이미 보고서가 생성되어 있으므로) 별도로 ADDM 태스크를 실행할 필요가 없으며, 스냅샷이 인접해 있지 않은 경우에만 ADDM 태스크를 실행할 필요가 있습니다.
ADDM의 기능이 단순히 SQL 구문의 분석에 한정되지 않는다는 사실을 명심하시기 바랍니다. 과거 연재에서 확인한 것과 같이, ADDM은 메모리 관리, 세그먼트 관리, redu/undo 등의 영역에 대한 다양한 분석 기능을 제공합니다. 제한된 지면을 통해 ADDM의 기능을 모두 설명하는 것은 어차피 불가능하므로, 지금부터는 SQL Tuning Advisor에 초점을 맞추어 설명을 진행하도록 하겠습니다.
SQL Tuning Advisor를 이용한 Access분석
오라클 데이타베이스의 옵티마이저(optimizer)는 가능한 액세스 경로를 여럿 생성한 뒤, 오브젝트 통계정보를 기준으로 가장 적은 비용이 드는 하나를 선택하는 방식으로 runtime optimization을 수행합니다. 하지만 옵티마이저는 (시간의 제약을 받는 만큼) SQL 구문의 튜닝이 필요한지, 통계가 정확한지, 새로운 인덱스를 생성해야 하는지 등의 여부를 판단하지 않습니다. 반면 SQL Tuning Advisor는 일종의 “전문가 시스템”과 같은 역할을 합니다. 옵티마이저가 “현재 가능한 대안 중 최적의 결과를 얻을 수 있는 것은 무엇인가?”라는 질문에 대한 답변을 제공한다면, SQL Tuning Advisor는 “사용자의 요구사항을 기반으로 고려했을 때, 성능을 향상시키기 위해 할 수 있는 일이 무엇인가?”라는 질문의 답을 제공합니다.
이러한 “전문가 시스템”으로서의 작업은 CPU 등의 자원을 많이 소모합니다. 이러한 이유로 SQL Tuning Advisor는 데이타베이스가 Tuning Mode로 설정된 경우에만 SQL 구문에 대한 분석작업을 수행합니다. Tuning Mode는 튜닝 태스크를 생성하는 과정에서 SCOPETIME 매개변수를 설정함으로써 지정됩니다. 사용자에 대한 영향을 최소화하려면 데이타베이스 활동이 적은 시간대를 선택하여 Tuning Mode를 사용하는 것이 바람직합니다.
이제 예를 통해 설명하도록 하겠습니다. 문제가 되는 SQL 구문이 아래와 같습니다:
select account_no from accounts where old_account_no = 11
실제로 튜닝하기 어렵지 않은 구문이지만, 이해를 돕기 위해 간단한 구문을 사용하였습니다. 어드바이저는 Enterprise Manager 또는 커맨드 라인을 통해 실행할 수 있습니다.
먼저, 커맨드 라인을 이용하는 방법을 알아봅시다. 아래와 같은 방법으로 dbms_sqltune 패키지를 호출하고 어드바이저를 실행합니다.
declare

   l_task_id     varchar2(20);
   l_sql         varchar2(2000);
begin
   l_sql := 'select account_no from accounts where old_account_no = 11';
   dbms_sqltune.drop_tuning_task ('FOLIO_COUNT');
   l_task_id := dbms_sqltune.create_tuning_task (
      sql_text  => l_sql,
      user_name  => 'ARUP',
      scope      => 'COMPREHENSIVE',
      time_limit => 120,
      task_name  => 'FOLIO_COUNT'
   );
   dbms_sqltune.execute_tuning_task ('FOLIO_COUNT');
end;
/
위 패키지는 FOLIO_COUNT라는 이름의 튜닝 태스크를 생성하고 실행합니다. 다음에는 아래와 같이 입력하여 태스크 실행 결과를 확인합니다.
set serveroutput on size 999999
set long 999999
select dbms_sqltune.report_tuning_task ('FOLIO_COUNT') from dual;
실행 결과는 Listing 2에서 확인할 수 있습니다. 어드바이저가 제공하는 권고 사항을 자세히 살펴보시기 바랍니다. 이 경우, 어드바이저는 OLD_ACCOUNT_NO 컬럼에 인덱스를 생성할 것을 권고하고 있습니다. 그 뿐 아니라, 인덱스가 생성된 경우의 비용을 계산하고, 기대되는 성능 향상 효과를 구체적인 형태로 제시하고 있습니다.
예로 든 구문 자체가 단순한 만큼, 굳이 어드바이저를 이용해서 이런 결과를 얻을 필요는 없었을 것입니다. 하지만 보다 복잡한 형태의 쿼리에 성능 문제가 발생한 경우라면, 어드바이저를 통해 수작업으로는 불가능한 성능 개선 효과를 얻을 수 있습니다.
중급 레벨 튜닝: Query Restructuring
조금 더 복잡한 쿼리를 분석하는 경우를 생각해 봅시다:
select account_no from accounts a 
where account_name = 'HARRY' 
and sub_account_name not in 
  ( select account_name from accounts 
    where account_no = a.old_account_no and status is not null);
어드바이저는 다음과 같은 권고 사항을 제시하였습니다:
1- Restructure SQL finding (see plan 1 in explain plans section)
----------------------------------------------------------------
  The optimizer could not unnest the subquery at line ID 1 of the execution
  plan.

  Recommendation
  --------------
    Consider replacing "NOT IN" with "NOT EXISTS" or ensure that columns used
    on both sides of the "NOT IN" operator are declared "NOT NULL" by adding
    either "NOT NULL" constraints or "IS NOT NULL" predicates.

  Rationale
  ---------
    A "FILTER" operation can be very expensive because it evaluates the
    subquery for each row in the parent query. The subquery, when unnested can
    drastically improve the execution time because the "FILTER" operation is
    converted into a join. Be aware that "NOT IN" and "NOT EXISTS" might
    produce different results for "NULL" values.
이번에는 인덱스 생성과 같은 오브젝트 구조 변경을 제안하는 대신, NOT IN 대신 NOT EXIST를 사용하도록 쿼리를 수정할 것을 제안하고 있습니다. 어드바이저는 근본적인 원인을 통해 권장 내용의 근거를 설명하고, 그 결정을 DBA의 판단에 맡깁니다.
고급 튜닝: SQL Profiles
옵티마이저는 쿼리가 사용하는 오브젝트 통계정보(object statistics)를 점검한 뒤 가장 적은 비용을 사용하는 경로를 선택함으로써 execution plan을 생성합니다. 쿼리가 두 개 이상의 테이블을 참조하는 경우, 옵티마이저는 관련된 테이블의 통계를 모두 점검한 뒤 가장 적은 비용을 사용하는 경로를 선택하지만, 테이블 간의 관계에 대해서는 아무 것도 이해하지 못합니다.
예를 들어, DELINQUENT(연체) 상태가 $1,000 이하의 balance를 갖는 한 account가 있다는 경우를 가정해 봅시다. ACCOUNTS 테이블과 BALANCES 테이블을 join하는 쿼리에 status가 DELINQUENT인 데이타만을 필터링하는 조건을 추가함으로써, 보다 적은 수의 결과를 얻을 수 있을 것입니다. 옵티마이저는 이러한 테이블 간의 복잡한 관계를 이해하지 못합니다. 하지만 어드바이저의 경우는 다릅니다. 어드바이저는 데이터로부터 이들 관계정보를 “수집”해 낸 뒤 SQL Profile의 형태로 이를 저장합니다. 이제 SQL Profile에 접근할 수 있게 된 옵티마이저는 테이블의 데이타 분포뿐 아니라 데이타 간의 관계까지도 이해할 수 있게 됩니다. 이렇게 추가로 제공되는 정보를 활용하면 보다 뛰어난 수준의 execution plan을 생성하고 성능을 향상시킬 수 있습니다.
SQL Profile을 이용하면, 쿼리 힌트를 코드에 삽입하는 형태의 SQL 구문 튜닝이 불필요해집니다. 따라서 SQL Tuning Advisor를 이용하여, 코드에 전혀 손을 대지 않고도 패키지 애플리케이션을 튜닝할 수 있습니다.
오브젝트 통계가 하나 또는 그 이상의 오브젝트에 매핑되는 반면, SQL Profile은 쿼리에 매핑됩니다. 같은 테이블(ACCOUNTS와 BALANCES)을 참조하더라도 쿼리가 다르면 프로파일도 달라집니다.
SQL Tuning Advisor는 실행 과정에서 프로파일을 생성한 뒤 이를 “Accept”할 것을 권고합니다. 사용자가 “Accept”하지 않는 이상 프로파일은 구문에 반영되지 않습니다. 아래와 같이 실행하면 아무 때나 프로파일을 accept할 수 있습니다:
begin
   dbms_sqltune.accept_sql_profile (
      task_name   => 'FOLIO_COUNT',
      name        => 'FOLIO_COUNT_PROFILE'
      description => 'Folio Count Profile',
      category    => 'FOLIO_COUNT');
end;
위 명령은 어드바이저가 생성한 FOLIO_COUNT_PROFILE을 위의 예에서 설명한 FOLIO_COUNT 튜닝 태스크에 연관된 구문에 연결합니다. (DBA가 직접 SQL Profile을 생성할 수는 없으며, 어드바이저만이 SQL Profile을 생성할 수 있음을 참고하시기 바랍니다. 사용자는 SQL Profile의 사용 여부만을 선택할 뿐입니다.)
DBA_SQL_PROFILES 딕셔너리 뷰를 통해 생성된 SQL Profile을 확인할 수 있습니다. SQL_TEXT 컬럼은 프로파일이 할당된 SQL 구문을, STATUS 컬럼은 프로파일의 활성화 여부를 표시합니다. (프로파일이 특정 구문에 연결되어 있는 경우라 하더라도, 활성화하기 전에는 execution plan에 반영되지 않습니다.)
ADDM과 SQL Tuning Advisor의 활용
위에 예로 든 세 가지 경우 이외에도, SQL Tuning Advisor는 쿼리에서 사용하는 오브젝트에 통계가 누락되어 있는지의 여부를 확인해 줍니다. 요약하자면, 어드바이저는 다음과 같은 네 가지 유형의 작업을 수행합니다.
  • 최적화를 위한 오브젝트들의 유효하고, 사용 가능한 통계정보를 보유하고 있는지 확인합니다.
  • 성능 향상을 위해 쿼리를 재작성하는 방안을 권고합니다.
  • 접근 경로를 확인하고 인덱스, MV(materialized view) 등을 추가하여 성능을 향상시킬 수 있는 방법을 조사하고 제안합니다.
  • SQL Profile을 생성하고, 이를 특정 쿼리에 연결합니다.
ADDM과 SQL Tuning Advisor가 유용하게 활용되는 경우를, 다음과 같은 세 가지 시나리오를 기준으로 검토해 볼 수 있습니다.
  • 사후조치적 튜닝(Reactive Tuning): 애플리케이션의 성능이 갑자기 저하됩니다. DBA는 ADDM을 사용하여 문제가 되는 SQL 구문을 확인합니다. ADDM의 권고사항에 따라 SQL Tuning Advisor를 실행하고 문제를 해결합니다.
  • 사전예방적 튜닝(Proactive Tuning): 애플리케이션은 정상적으로 동작합니다. DBA는 유지보수를 위해 필요한 작업을 실행하고 쿼리를 좀 더 개선할 방법이 있는지 확인하고자 합니다. DBA는 스탠드얼론 모드로 SQL Tuning Advisor를 실행하고 가능한 대안을 검토합니다.
  • 개발단계 튜닝(Development Tuning): QA 단계 또는 운영 단계보다는, 개발 단계에서 쿼리 튜닝을 수행하는 것이 상대적으로 용이합니다. 이때 어드바이저의 커맨드 라인 버전을 사용하여 개별 SQL 구문을 튜닝할 수 있습니다.
Enterprise Manager의 활용
앞의 예는 SQL Tuning Advisor를 커맨드 라인 모드에서 사용하는 방법을 기준으로 설명되었습니다. 커맨드 라인 모드는 태스크 실행을 위한 스크립트 생성이 용이하다는 장점이 있습니다. 하지만, 사용자에 의해 이미 문제가 보고된 경우라면 Enterprise Manager 10g를 이용하는 방법이 더 효과적일 수 있습니다.
(제 13주) 연재에서, 새롭게 개선된 Enterprise Manager 인터페이스에 대해 소개한 바 있습니다. 이번에는 EM을 활용하여 SQL 구문을 진단하고 튜닝하는 방법을 설명해 보겠습니다. 데이타베이스 홈 페이지 하단의 “Advisor Central” 링크를 클릭하면 다양한 어드바이저를 위한 메뉴를 제공하는 화면이 표시됩니다. 이 화면 상단의 “SQL Tuning Advisor”를 클릭합니다 (그림 4 참조).
figure 4
그림 4: Enterprise Manager의 Advisor Central 화면
이제 SQL Tuning Advisor가 실행됩니다. 다음 화면에서 "Top SQL"을 클릭합니다 (그림 5 참조).
figure 5
그림 5: SQL Tuning Advisor
그림 6에서 보여지는 것과 같은 화면이 표시되며, 이 화면에서 표시되는 그래프를 통해 다양한 wait class에 관련된 정보를 시간대 별로 확인할 수 있습니다.
figure 6
그림 6: Top SQL 화면
그래프의 관심 영역은 회색 사각형으로 표시됩니다. CPU wait이 높게 나타나는 영역에 마우스를 드래그하여 관심영역을 조정합니다. 화면의 하단에는 해당 시간대에 수행된 SQL 구문 관련 정보가 표시됩니다 (그림 7 참조).
figure 7
그림 7: SQL 구문의 Activity 정보
상단(붉은색으로 표시된 부분)에는 가장 많은 CPU 자원을 소모하는 SQL 구문들이 표시되고 있습니다. 각 구문의 SQL ID를 클릭하면 그림 8과 같은 화면이 표시됩니다.
figure 8
그림 8: SQL Details 화면
위 화면을 통해 문제가 되는 SQL 구문을 확인할 수 있습니다. “Run SQL Tuning Advisor” 버튼을 클릭하면 어드바이저가 실행되고 그림 9와 같은 화면이 표시됩니다.
figure 9
그림 9: SQL Tuning Advisor의 스케줄링
어드바이저 스케줄러 화면에서, 태스크의 유형과 분석의 심도를 결정할 수 있습니다. 위 화면에서는 “comprehensive” analysis를 선택하고 어드바이저를 바로 실행하도록 설정하였습니다. 어드바이저의 실행이 완료되면 그림 10과 같은 화면을 통해 어드바이저의 권고 사항을 확인할 수 있습니다.
figure 10
그림 10: Advisor Recommendation 화면
EM을 이용한 SQL Tuning Advisor 실행 방법은, 앞에서 설명한 커맨드 라인 버전을 이용하는 경우와 유사합니다. 하지만, 발생된 문제를 확인하여 드릴다운을 수행하고, 권고사항을 생성한 후 이를 승인하는 전체 과정이, 실제 발생한 문제를 해결하기에 편리한 형태로 구성되어 있다는 점에서 차이가 있습니다.
결론
ADDM은 성능 지표를 자동적으로 분석하고 오라클 전문가가 구현한 베스트 프랙티스 및 방법론을 기반으로 권고사항을 제시해 주는 강력한 성능 관리 툴입니다. 이 기능을 활용하여 발생된 문제와 그 원인을 확인할 수 있을 뿐 아니라, 취해야 할 조치에 대한 조언을 얻을 수 있습니다.
ADDM과 SQL Tuning Advisor에 대한 자세한 정보는 기술백서 Oracle Database 10g: The Self-Managing DatabaseThe Self-Managing Database: Guided Application & SQL Tuning, 그리고 Oracle Database 2 Day DBA 매뉴얼의 제 10장Oracle Database Performance Tuning Guide6장 and 13장을 참고하시기 바랍니다.
 
 
 
 
제 19 주
Scheduler
dbms_job 패키지의 실행 주기 설정을 수작업으로 관리하는 것이 번거로우십니까? 이제 10g데이타베이스가 제공하는 Scheduler를 이용해 보십시오.
여러분들 중 일부는 백그라운드 데이타베이스 작업 스케줄을 설정하기 위해 dbms_job 패키지를 사용하고 있을 것입니다. 하지만 필자가 알기로는 대부분의 DBA들이 dbms_job 패키지를 사용하지 않고 있습니다.
이 패키지는 PL/SQL 코드 세그먼트만을 처리할 수 있으며, 데이타베이스 외부의 운영체제 파일 또는 실행 파일 이미지를 처리할 수 없다는 기능적 한계를 갖고 있습니다. 이 때문에 DBA들은 Unix의 cron, Windows의 AT 명령 등을 사용하여 운영 체제 레벨에서 스케줄링을 설정하는 방법을 선택합니다. 또는 그래픽 사용자 인터페이스를 제공하는 써드 파티 툴을 사용하기도 합니다.
그러나 dbms_job은 이들과 다른 장점들이 있습니다. 이 중 하나는 데이터베이스가 실행되고 있는 경우에만 활성화된다는 것입니다. 만약 데이터베이스가 다운되어 있다면 해당 Job들은 실행되지 않습니다. 데이터베이스 외부에 존재하는 툴들은 수작업을 통해 데이터베이스가 실행되고 있는지 조사되어야 하며, 복잡한 작업일 수 있습니다. Dbms_job의 다른 장점으론 데이터베이스 내부에 존재한다는 것입니다. 그러므로 SQL*Plus와 같은 유틸리티를 이용해 쉽게 접근 가능하다는 것입니다.
Oracle Database 10g Scheduler는 모든 종류의 작업을 지원하는 내장형 작업 스케줄러 유틸리티를 제공합니다. 10g Scheduler의 가장 큰 장점은, 데이타베이스에 포함된 형태로 제공되므로 추가적인 비용이 들지 않는다는 것입니다. 이번 연재에서는 Scheduler의 기능에 대해 자세히 살펴보기로 합니다.
Creating Jobs Without Programs
개념의 이해를 돕기 위해 예를 통해 설명하겠습니다. 아카이브 로그 파일을 다른 파일시스템으로 이동하기 위해 아래와 같은 이름의 셸 스크립트를 생성했다고 가정해 봅시다:
/home/arup/dbtools/move_arcs.sh
별도의 프로그램을 생성하지 않고도 작업 유형으로 “Executable”을 지정하고 스케줄링을 설정할 수 있습니다.
begin
   dbms_scheduler.create_job
   (
      job_name      => 'ARC_MOVE_2',
      schedule_name => 'EVERY_30_MINS',
      job_type      => 'EXECUTABLE',
      job_action    => '/home/arup/dbtools/move_arcs.sh',
      enabled       => true,
      comments      => 'Move Archived Logs to a Different Directory'
   );
end;
/
또 Schedule Name을 설정하지 않고 작업을 생성하는 것도 가능합니다.
begin
   dbms_scheduler.create_job
   (
      job_name        => 'ARC_MOVE_3',
      job_type        => 'EXECUTABLE',
      job_action      => '/home/arup/dbtools/move_arcs.sh',
      repeat_interval => 'FREQ=MINUTELY; INTERVAL=30',
      enabled         => true,
      comments        => 'Move Archived Logs to a Different Directory'
   );
end;
/
앞의 두 가지 예를 통해 dbms_job과 비교했을 때의 Scheduler의 장점을 확인할 수 있습니다. Scheduler를 이용하면 PL/SQL 프로그램뿐 아니라 OS 유틸리티와 프로그램을 실행하는 것이 가능합니다. 이러한 기능을 활용하여 범용적인 데이타베이스 작업 관리 환경을 구현할 수 있습니다. 또 Scheduler는 자연 언어(natural language)를 사용하여 실행 주기를 정의할 수 있다는 매우 중요한 이점을 제공합니다. 스케줄을 매 30분 단위로 실행하고자 하는 경우, (PL/SQL 문법 대신) 자연 언어 형식의 표현을 사용하여 REPEAT_INTERVAL 매개변수를 아래와 같이 정의할 수 있습니다:
'FREQ=MINUTELY; INTERVAL=30'
좀 더 복잡한 예를 들어 설명해 보겠습니다. 운영 중인 애플리케이션이 오전 7시에서 오후 3시까지의 시간대에 집중적으로 사용된다고 가정해 봅시다. Statspack을 실행하면 월요일~금요일 오전 7시 ~ 오후 3시의 시스템 통계를 수집할 수 있습니다. DBMS_JOB.SUBMIT을 사용하여 작업을 생성하는 경우라면, NEXT_DATE 매개변수는 아래와 같이 정의됩니다:
DECODE
(
   SIGN
   (
      15 - TO_CHAR(SYSDATE,'HH24')
   ), 
   1,
      TRUNC(SYSDATE)+15/24,
   TRUNC
   (
      SYSDATE +
      DECODE
      ( 
          TO_CHAR(SYSDATE,'D'), 6, 3, 1
      )
    )
    +7/24
)
위의 코드가 이해하기 쉽습니까? 전혀 그렇지 않습니다.
이번에는 DBMS_SCHEDULER를 이용하는 방법을 살펴 보겠습니다. REPEAT_INTERVAL 매개변수는 아래와 같이 간단하게 정의됩니다:
'FREQ=DAILY; BYDAY=MON,TUE,WED,THU,FRI; BYHOUR=7,15'
이 매개변수를 이용해 실행 주기를 다양한 형태로 설정할 수 있습니다. 몇 가지 예가 아래와 같습니다:
  • 매월 마지막 일요일:
    FREQ=MONTHLY; BYDAY=-1SUN
    
  • 매월 세 번째 금요일:
    FREQ=MONTHLY; BYDAY=3FRI
    
  • 매월 뒤에서 두 번째 금요일
    FREQ=MONTHLY; BYDAY=-2FRI
    
마이너스(-) 기호는 숫자가 뒤에서부터 계산된다는 의미입니다.
설정된 실행 주기가 올바른지 확인하려면 어떻게 해야 할까요? 캘린더 문자열을 이용해서 날짜를 미리 확인할 수 있다면 편리하지 않을까요? EVALUATE_CALENDAR_STRING 프로시저를 이용하여 실행 예정 시각을 미리 검토할 수 있습니다. 위의 예 – 월~금7:00 AM ~ 3:00 PM Statspack 실행 – 에서 설정된 실행 주기를 확인하는 방법이 아래와 같습니다:
set serveroutput on size 999999

declare
   L_start_date    TIMESTAMP;
   l_next_date     TIMESTAMP;
   l_return_date   TIMESTAMP;
begin
   l_start_date := trunc(SYSTIMESTAMP);
   l_return_date := l_start_date;
   for ctr in 1..10 loop
      dbms_scheduler.evaluate_calendar_string(
        'FREQ=DAILY; BYDAY=MON,TUE,WED,THU,FRI; BYHOUR=7,15',
         l_start_date, l_return_date, l_next_date
      );
      dbms_output.put_line('Next Run on: ' ||
          to_char(l_next_date,'mm/dd/yyyy hh24:mi:ss')
      );
      l_return_date := l_next_date;
   end loop;
end;
/
실행 결과는 다음과 같습니다:
Next Run on: 03/22/2004 07:00:00
Next Run on: 03/22/2004 15:00:00
Next Run on: 03/23/2004 07:00:00
Next Run on: 03/23/2004 15:00:00
Next Run on: 03/24/2004 07:00:00
Next Run on: 03/24/2004 15:00:00
Next Run on: 03/25/2004 07:00:00
Next Run on: 03/25/2004 15:00:00
Next Run on: 03/26/2004 07:00:00
Next Run on: 03/26/2004 15:00:00
위 결과로 미루어 설정된 내용에 문제가 없음을 확인할 수 있습니다.
작업과 프로그램의 연계
위의 사례는 특정 프로그램과 연결되지 않은 작업을 생성하는 방법을 설명하고 있습니다. 이번에는 특정 OS 유틸리티를 사용하는 프로그램을 생성하고, 실행 주기를 정의한 스케줄을 설정한 뒤, 이 두 가지를 조합하여 새로운 작업을 생성하는 방법을 설명합니다.
먼저 작업 내에서 프로그램을 사용한다는 사실을 데이타베이스가 인지하도록 해야 합니다. 이 프로그램을 생성하려면, CREATE JOB 권한을 갖고 있어야 합니다.
begin
    dbms_scheduler.create_program
    (
       program_name   => 'MOVE_ARCS',
       program_type   => 'EXECUTABLE',
       program_action => '/home/arup/dbtools/move_arcs.sh',
       enabled        => TRUE,
       comments       => 'Moving Archived Logs to Staging Directory'
    );
end;
/
이제 named program unit을 생성하여, 그 유형을 “executable”로 지정하고 실행될 program unit의 이름을 정의하였습니다.
다음으로, 매 30분 간격으로 실행되는 EVERY_30_MIN이라는 이름의 스케줄을 생성합니다:
begin
    dbms_scheduler.create_schedule
    (
       schedule_name   => 'EVERY_30_MINS',
       repeat_interval => 'FREQ=MINUTELY; INTERVAL=30',
       comments        => 'Every 30-mins'
    );
end;
/
프로그램과 스케줄을 생성한 뒤, 이 두 가지를 연관시킴으로써 새로운 작업을 생성합니다.
begin
   dbms_scheduler.create_job
   (
      job_name      => 'ARC_MOVE',
      program_name  => 'MOVE_ARCS',
      schedule_name => 'EVERY_30_MINS',
      comments      => 'Move Archived Logs to a Different Directory',
      enabled       => TRUE
   );
end;
/
이제 매 30분마다 move_arcs.sh 셸 스크립트를 실행하는 작업이 생성되었습니다. 이 스케줄은 데이타베이스 내부의 Scheduler 기능을 통해 관리되며, 따라서 cron 또는 AT 유틸리티를 사용할 필요가 없습니다.
Classes, Plans, and Windows
작업 스케줄링 시스템에 효과적으로 활용하려면 작업의 우선순위 지정이 가능해야 합니다. 예를 들어 OLTP 워크로드가 실행 중인 시간대에 통계 수집 작업이 갑자기 실행되어 성능을 저하시킬 수 있습니다. 통계 수집 작업이 OLTP 성능에 영향을 미치지 않도록 하기 위해, Scheduler가 제공하는 job classes, resource plans, and Scheduler Windows기능을 활용할 수 있습니다.
Job class는 할당된 자원을 공유하는 “resource consumer group”으로 매핑됩니다. Job class를 생성하기 위해, 먼저 OLTP_GROUP이라는 이름의 resource consumer group을 정의해 보겠습니다.
begin
   dbms_resource_manager.clear_pending_area();
   dbms_resource_manager.create_pending_area();
   dbms_resource_manager.create_consumer_group (
       consumer_group => 'oltp_group',   
       comment => 'OLTP Activity Group'
   );
   dbms_resource_manager.submit_pending_area();
end;
/
다음에는 resource plan을 생성합니다.
begin
   dbms_resource_manager.clear_pending_area();
   dbms_resource_manager.create_pending_area();
   dbms_resource_manager.create_plan
      ('OLTP_PLAN', 'OLTP Database Activity Plan');
   dbms_resource_manager.create_plan_directive(
      plan => 'OLTP_PLAN',
      group_or_subplan => 'OLTP_GROUP',
      comment => 'This is the OLTP Plan',
      cpu_p1 => 80, cpu_p2 => NULL, cpu_p3 => NULL, cpu_p4 => NULL,
      cpu_p5 => NULL, cpu_p6 => NULL, cpu_p7 => NULL, cpu_p8 => NULL,
      parallel_degree_limit_p1 => 4,
      active_sess_pool_p1 => NULL,
      queueing_p1 => NULL,
      switch_group => 'OTHER_GROUPS',
      switch_time => 10,
      switch_estimate => true,
      max_est_exec_time => 10,
      undo_pool => 500,
      max_idle_time => NULL,
      max_idle_blocker_time => NULL,
      switch_time_in_call => NULL
   );
   dbms_resource_manager.create_plan_directive(
      plan => 'OLTP_PLAN',
      group_or_subplan => 'OTHER_GROUPS',
      comment => NULL,
      cpu_p1 => 20, cpu_p2 => NULL, cpu_p3 => NULL, cpu_p4 => NULL,
      cpu_p5 => NULL, cpu_p6 => NULL, cpu_p7 => NULL, cpu_p8 => NULL,
      parallel_degree_limit_p1 => 0,
      active_sess_pool_p1 => 0,
      queueing_p1 => 0,
      switch_group => NULL,
      switch_time => NULL,
      switch_estimate => false,
      max_est_exec_time => 0,
      undo_pool => 10,
      max_idle_time => NULL,
      max_idle_blocker_time => NULL,
      switch_time_in_call => NULL
   );
   dbms_resource_manager.submit_pending_area();
end;
/
마지막으로 앞에서 생성된 resource consumer group을 이용해 job class를 생성합니다.
begin
   dbms_scheduler.create_job_class(
      job_class_name => 'OLTP_JOBS',
      logging_level => DBMS_SCHEDULER.LOGGING_FULL,
      log_history => 45,
      resource_consumer_group => 'OLTP_GROUP',
      comments => 'OLTP Related Jobs'
   );
end;
/
이 프로시저에서 사용된 매개변수들을 설명해 보겠습니다. LOGGING_LEVEL 매개변수는 해당 job class를 위해 얼마나 많은 로그 데이타를 기록할 것인지 정의하는데 사용됩니다. LOGGING_FULL은 job class에 포함된 작업의 모든 활동(생성, 삭제, 실행, 변경 등)을 로그에 기록함을 의미합니다. 로그는 DBA_SCHEDULER_JOB_LOG 뷰를 통해 확인할 수 있으며, LOG_HISTORY 매개변수에 저장된 대로 45일간 보관됩니다 (디폴트 값은 30일입니다). 이 class와 연관된 resource_consumer_group 도 정의되어 있습니다. DBA_SCHEDULER_JOB_CLASSES 뷰를 통해 정의된 job class들을 확인할 수 있습니다.
작업을 생성하는 과정에서, 필요한 경우 해당 작업을 job class에 할당할 수 있습니다. 예를 들어 collect_opt_stats() 저장 프로시저를 실행하여 옵티마이저 통계를 수집하는 COLLECT_STATS 작업을 생성하면서, 아래와 같이 job class를 할당할 수 있습니다.
begin
   dbms_scheduler.create_job
   (
      job_name        => 'COLLECT_STATS',
      job_type        => 'STORED_PROCEDURE',
      job_action      => 'collect_opt_stats',
      job_class       => 'OLTP_JOBS',
      repeat_interval => 'FREQ=WEEKLY; INTERVAL=1',
      enabled         => true,
      comments        => 'Collect Optimizer Stats'
   );
end;
/
위 명령을 실행하면 생성된 작업이 OLTP_JOBS 클래스에 할당됩니다. OLTP_JOBS 클래스에 적용되는 OLTP_GROUP resource plan을 통해 프로세스에 할당되는 CPU 자원, 다른 그룹으로 전환되기 전에 최대 실행 가능한 횟수, 전환되는 그룹 등을 설정할 수 있습니다. 동일한 job class에 할당된 작업은 동일한 resource plan의 적용을 받습니다. 이 기능을 활용하면 서로 다른 유형의 작업이 같은 리소스를 두고 경합을 벌이는 상황을 방지할 수 있습니다.
Scheduler Window는 특정 resource plan이 사용되는 시간대를 의미합니다. 예를 들어, 실시간 의사결정 작업에 관련한 업데이트 배치 작업이 주간에는 높은 우선순위를 갖는 반면 야간에는 낮은 우선순위를 갖는다고 가정해 봅시다. 이 경우 시간대별로 다른 resource plan을 정의하고, 정의된 내용을 Scheduler Window를 이용해 적용할 수 있습니다.
모니터링
실행 중인 작업의 상태는 DBA_SCHEDULER_JOB_LOG 뷰를 통해 확인할 수 있습니다. 이 뷰의 STATUS 컬럼은 작업의 현재 상태를 표시하는데 사용됩니다. 만일 STATUS 컬럼이 FAILED로 표시된다면, DBA_SCHEDULER_JOB_RUNS_DETAILS 뷰를 통해 그 원인을 확인할 수 있습니다.
관리
지금까지 여러 가지 유형의 오브젝트(program, schedule, job, job class 등)를 생성하는 방법에 대해 설명했습니다. 이렇게 생성된 오브젝트를 변경할 필요가 있다면, DBMS_SCHEDULER 패키지가 제공하는 API를 사용하면 됩니다.
Enterprise Manager 10g 홈 페이지에서 Administration 링크를 클릭하면, 그림 1과 같이 Administration 화면이 표시됩니다. Scheduler와 관련된 작업은 우측 하단의 “Scheduler” 항목에 표시됩니다 (그림의 붉은색 원 참조).
figure 1
그림 1: Administration 페이지
이 페이지에서 제공되는 하이퍼링크를 활용하면 작업의 생성, 삭제, 관리와 같은 Scheduler 관련 작업을 쉽게 수행할 수 있습니다. 몇 가지 예를 들어 설명해 보겠습니다. 이미 작업을 생성해 둔 상태이므로, Job 탭을 클릭하면 그림 2와 같은 화면이 표시될 것입니다.
figure 2
그림 2: 작업 스케줄의 확인
COLLECT_STATS 작업을 클릭하여 그 속성을 변경해 보도록 합시다. “Name” 필드를 클릭하면 그림 3과 같은 화면이 표시됩니다.
figure 3
그림 3: 작업 매개변수
지금까지 확인한 것처럼, EM을 이용하면 작업, 스케줄, 옵션 등에 관련한 매개변수를 수정할 수 있습니다. 변경 작업을 완료한 뒤 “Apply” 버튼을 누르면 변경사항은 영구적으로 적용됩니다. “Apply” 버튼을 누르기 전에 “Show SQL” 버튼을 눌러 실행되는 SQL 구문을 확인하는 것도 가능합니다. 또 SQL 구문을 스크립트에 저장하여 나중에 실행하거나, 템플릿 용도로 활용할 수도 있습니다.
결론
Oracle Database 10g의 Scheduler는 dbms_job 인터페이스보다 훨씬 향상된 기능을 제공합니다. Scheduler에 대한 상세한 정보는 Oracle Database Administrator's GuideChapter 25를 확인하시기 바랍니다.
 
 
 
 
제 20 주
그 밖의 유용한 기능
지금까지 소개한 기능 이외에도, Oracle Database 10g는 통계 자동 수집, undo 데이타 보존기간(undo retention)의 “guarantee”, 더욱 쉽고 강력해진 암호화 패키지 등의 유용한 기능을 제공합니다.
드디어 우리의 긴 여행도 막바지에 이르렀습니다. 지난 19주 동안 DBA의 작업을 쉽고 편하게 해 주는 다양한 툴, 팁, 테크닉을 소개해 드렸습니다.
지금까지 Oracle Database 10g의 특출한 기능이라 하더라도 DBA에게 많은 도움이 되지 않는다면 소개하지 않는 것을 원칙으로 했습니다. 마지막 연재도 Oracle 10g의 모든 기능을 섭렵하기에는 지면이 충분하지 않습니다. 이제 그간의 연재를 마무리하면서, 언급될 가치가 있는 Oracle 10g의 중요한 기능을 몇 가지 골라 소개하려 합니다.
자동적인 통계 수집
이미 많은 분들이 알고 계시겠지만, Oracle 10g에 오면서 마침내 Rules-Based Optimized(RBO)는 더 이상 지원되지 않는 (기능은 남아있지만) 것으로 결정되었습니다. Oracle9i Database를 사용하는 IT 조직은 Cost-Based Optimizer(CBO) 환경으로 업그레이드함으로써 계속적인 기술지원을 받을 수 있을 뿐 아니라 query rewrite, partition pruning과 같은 고급 기능을 활용할 수 있게 됩니다. 하지만 이 경우 통계(statistics)가 (정확히 말하면 통계의 부재가) 문제가 됩니다.
CBO가 최적의 실행 경로를 생성하려면 정확한 통계를 필요로 하며, DBA는 통계가 정기적으로 수집될 수 있도록 데이타베이스를 관리해야 합니다. 10g 이전의 환경에서는 (여러 가지 이유 때문에) 정확한 통계의 수집을 위한 DBA의 노력이 수포로 돌아가는 경우가 허다했습니다. 이러한 문제 때문에 CBO가 “제멋대로 동작한다”는, 다시 말해 임의적으로 실행 경로를 바꾸곤 한다는 이론이 제기되기도 했습니다.
10g는 통계의 자동적인 수집 기능을 제공하며, 따라서 이와 같은 문제를 상당부분 해결하였습니다. Oracle9i의 경우 테이블의 데이타가 얼마나 변경되었는지 확인하기 위해서는 테이블의 모니터링 옵션을 활성화하고 (ALTER TABLE... MONITORING) 해당 테이블에 대한 DBA_TAB_MODIFICATIONS 뷰를 조회해야 했습니다.
10g에서는 MONITORING 키워드가 사라진 대신, 초기화 매개변수 STATISTICS_LEVELTYPICAL 또는 ALL로 설정함으로써 통계 수집을 자동화하는 기능을 제공합니다. (디폴트 값은 TYPICAL이며, 따라서 데이타베이스 설치 후 바로 통계 수집 작업이 자동 수행됩니다.) Oracle Database 10g는 사전 정의된 Scheduler (Scheduler에 대해서는 제 19 주에 설명했습니다) 작업 GATHER_STATS_JOB을 통해 통계 수집을 수행합니다. 이 작업은 STATISTICS_LEVEL 매개변수 값에 의해 활성화됩니다.
통계 수집 작업은 매우 많은 자원을 소모합니다. 따라서 이 작업으로 인해 운영 중인 애플리케이션의 성능이 저하되지 않음을 보장할 수 있어야 합니다. 10g는 통계 수집과 같은 자동 수행 작업을 위해 special resource consumer group인 AUTO_TASK_CONSUMER_GROUP을 정의하고 있습니다. 이 consumer group이 수행하는 작업은 디폴트 consumer group에 비해 낮은 우선순위를 가지며, 따라서 자동 수행되는 작업이 시스템의 성능을 저하시킬 가능성을 줄일 수 있습니다.
STATISTICS_LEVELTYPICAL로 설정한 상태에서 통계가 자동 수집되지 않게 하려면 어떻게 해야 할까요? 간단합니다. 아래와 같은 명령을 사용하여 Scheduler 작업을 비활성화하면 됩니다.
BEGIN 
   DBMS_SCHEDULER.DISABLE('GATHER_STATS_JOB');
END;
그렇다면 굳이 이렇게 할 설정할 필요가 있을까요? 여러 가지 이유로 이러한 설정이 필요한 상황이 있을 수 있습니다. 첫 번째로 테이블 내의 많은 레코드가 변경되었지만 데이타의 분포는 변하지 않은 경우를 들 수 있습니다 (데이타 웨어하우스 환경에서 이러한 경우가 많이 발견됩니다). 이 경우 통계를 다시 수집할 필요가 없으며 이전의 통계를 그대로 사용해도 무방합니다. 두 번째로 partition exchange를 이용하여 materialized view(MV)를 refresh하는 환경에서도, exchanged table의 통계를 임포트해서 사용하므로 MV에 대한 통계에 대해 별도의 수집을 원치 않을 경우입니다. 또 전체 통계 수집 작업을 중단하는 대신, 특정 테이블에 대한 자동 통계 수집만을 선택적으로 비활성화하는 것도 가능합니다.
통계 히스토리
옵티마이저 통계 수집 과정에서 문제가 될 수 있는 상황의 하나로 execution plan이 변경되는 경우를 들 수 있습니다. 새로운 통계가 수집되기 전까지 정상적으로 수행되던 쿼리가, 새로운 통계를 기반으로 생성된 execution plan으로 인해 오히려 성능이 악화될 수 있습니다. 이러한 문제는 실제로 빈번하게 발생합니다.
이러한 문제를 방지하기 위해서는, 새로운 통계를 수집하기 전에 현재 통계 정보를 저장합니다. 이와 같이 함으로써, 문제가 생겼을 때 바로 예전의 통계로 복구하거나, 두 통계의 차이점을 분석해서 원인을 확인할 수 있습니다.
예를 들어 REVENUE 테이블에 대한 통계 수집 작업이 5월 31일 오후 10시에 실행된 이후 쿼리 성능이 악화되었다고 가정해 봅시다. 아래 명령을 사용하여 Oracle이 저장한 이전 통계로 복구될 수 있습니다.
begin
   dbms_stats.restore_table_stats (
      'ARUP',
      'REVENUE',
      '31-MAY-04 10.00.00.000000000 PM -04:00');
end;
위 명령은 (TIMESTAMP 데이타 타입을 통해 명시한 대로) 5월 31일 오후 10시 시점을 기준으로 통계를 복구합니다. 이와 같이 함으로써 새로운 통계로 인해 발생한 문제를 해결할 수 있습니다.
복구에 이용할 수 있는 과거 통계의 보존 기간은 매개변수를 통해 설정할 수 있습니다. 현재 설정된 보존 기간을 확인하려면 아래와 같이 질의합니다:
SQL> select DBMS_STATS.GET_STATS_HISTORY_RETENTION from dual;

GET_STATS_HISTORY_RETENTION
---------------------------
                         31
위 결과를 통해 31일간의 통계 정보를 보존할 수 있습니다. 단 이 기간이 반드시 보장되는 것은 아닙니다. 복구 가능한 정확한 시점을 확인하려면 아래와 같이 입력합니다:
SQL> select DBMS_STATS.GET_STATS_HISTORY_AVAILABILITY from dual;

GET_STATS_HISTORY_AVAILABILITY
---------------------------------------------------------------------
17-MAY-04 03.21.33.594053000 PM -04:00
위 결과는 현재 가장 오래된 통계가 5월 17일 오전 3시 21분에 생성된 것임을 알려주고 있습니다.
내장 함수를 이용하여 통계의 보존 기간을 변경할 수 있습니다. 보존 기간을 45일로 변경하려면 다음과 같이 입력합니다:
execute DBMS_STATS.ALTER_STATS_HISTORY_RETENTION (45)
Guaranteed Undo Retention
Oracle9i에서 처음 소개된 Automatic Undo Retention 기능은 ORA-1555 “Snapshot Too Old” 에러가 발생할 가능성을 상당 수준 줄여줍니다. 하지만 이 에러는 (많이 줄어들기는 했지만) 여전히 발생하고 있습니다. 왜일까요?
이 질문에 대답하기 위해서는, 먼저 undo segment가 동작하는 원리를 이해해야 합니다. 오라클 데이타베이스의 데이타가 변경되면, (SGA 내부의) 캐시에 있는 블록이 즉각적으로 변경되면서, 기존의 데이타 이미지(past image)는 undo segment에 저장됩니다. 트랜잭션이 커밋 되면, 기존의 이미지는 더 이상 불필요하며, 따라서 삭제가 가능합니다. 하지만 Undo segment의 모든 공간이 액티브 트랜잭션에 의해 사용되고 있다면 세그먼트 내에서 가장 오래된 익스텐트(extent)에 덮어 쓰기를 시도합니다 (이 과정을 “wrapping”이라 부르기도 하며, V$ROLLSTAT 뷰의 WRAPS 컬럼을 통해 확인됩니다). 하지만, 롱-러닝 트랜잭션(long-running transaction)이 사용되는 경우와 같은 특별한 상황에서, 데이타베이스는 액티브 트랜잭션을 위해 세그먼트를 확장하기도 합니다 (V$ROLLSTAT 뷰의 EXTENDS 컬럼을 통해 확인됩니다). 쿼리가 데이터의 일관성을 보장하기 위해서 undo segment의 익스텐트(extent)에 저장된 이미지를 요구했지만 해당 익스텐트가 이미 재사용된 경우, 쿼리는 ORA-1555 에러를 발생시킵니다.
초기화 매개변수 UNDO_RETENTION을 통해 undo 데이타가 보존되는 기간을 (초 단위로) 설정할 수 있습니다. 이처럼 보존 기간을 설정함으로써, inactive 상태의 undo extent라 하더라도 일정 기간 삭제되지 않음을 보장할 수 있습니다. 이 방법을 사용하면 inactive 상태의 익스텐트(extent)가 재사용되는 상황을 예방하고, 따라서 ORA-1555 에러가 발생할 가능성을 줄일 수 있습니다.
하지만 UNDO_RETENTION 매개변수로 ORA-1555의 발생 가능성을 근본적으로 제거할 수는 없습니다. 세그먼트의 확장이 불가능한 경우, 데이타베이스는 가장 오래된 inactive 익스텐트를 강제로 재사용합니다. 따라서 일부 롱-러닝 트랜잭션으로부터 ORA-1555 에러가 발생할 가능성은 상존합니다.
10g에서는 이러한 문제가 해결되었습니다. 이제는 undo 테이블스페이스를 생성하면서 undo retention “guarantee”를 설정할 수 있습니다. 그 예가 아래와 같습니다:
CREATE UNDO TABLESPACE UNDO_TS1
DATAFILE '/u01/oradata/proddb01/undo_ts1_01.dbf'
SIZE 1024M 
RETENTION GUARANTEE;
마지막 부분의 “RETENTION GUARANTEE”는 undo tablespace로 하여금 만료되지 않은 undo 익스텐트의 보존을 “보장”하도록 설정합니다. 기존 undo tablespace는 ALTER 구문을 이용하여 아래와 같이 수정할 수 있습니다:
ALTER TABLESPACE UNDO_TS2 RETENTION GUARANTEE;
익스텐트의 보존을 “보장”하는 것을 원치 않는 경우(Oracle 9i 방식)에는 다음과 같이 입력합니다:
ALTER TABLESPACE UNDO_TS2 RETENTION NOGUARANTEE;
테이블스페이스에 undo retention이 “guarantee” 되고 있는지의 여부는 아래와 같이 확인할 수 있습니다.
SELECT RETENTION
FROM DBA_TABLESPACES
WHERE TABLESPACE_NAME = 'UNDO_TS1';
End-to-End Tracing
성능 문제를 진단하기 위한 방법의 하나로, sql_trace를 활성화하여 데이타베이스 쿼리를 추적하고 tkprof와 같은 툴을 사용하여 그 결과를 분석하는 방법이 자주 사용됩니다. 하지만, 이 방법은 공유 서버(Shared Server) 아키텍처 상에 구현된 데이타베이스 환경에서는 그 유용성이 심각하게 제한됩니다. 이러한 경우 사용자의 요구사항을 처리하기 위해서 여려 공유서버 프로세스(shared server process)들이 생성됩니다. 사용자 BILL이 데이타베이스에 접근하려는 경우, dispatcher는 사용 가능한 공유 서버 중 하나와 사용자를 연결합니다. 현재 사용 가능한 서버가 존재하지 않는 경우, 새로운 공유 서버가 생성됩니다. 이 세션이 트레이스(trace) 작업을 시작하면, 해당 공유 서버의 프로세스에 의해 호출된 작업들의 트레이스가 수행됩니다.
이제 BILL의 세션이 idle 상태가 되고 LORA의 세션이 active 상태가 되었다고 가정합시다. BILL에게 서비스를 제공하던 공유 서버는 LORA의 세션에 할당되었습니다. 이 시점 이후의 트레이스 정보는 BILL의 세션이 아닌 LORA의 세션을 추적합니다. LORA의 세션이 inactive 상태가 되면, 공유 서버는 다른 active 세션에 할당되며, 다시 BILL 또는 LORA의 세션과는 전혀 무관한 트레이스정보를 만들어 냅니다.
10g에서는 end-to-end tracing 기능을 통해 이러한 문제를 해결하였습니다. 10g에서의 트레이스 작업은 세션 단위로 수행되지 않고 client identifier와 같은 정보를 기준으로 수행됩니다. 새로 추가된 DBMS_MONITOR 패키지가 바로 이러한 목적을 위해 사용됩니다.
예를 들어, identifier “account_update”를 갖는 모든 세션을 트레이스하려는 경우를 가정해 봅시다. 먼저 트레이스를 설정하기 위해서 아래와 같이 입력합니다:
exec DBMS_MONITOR.CLIENT_ID_TRACE_ENABLE('account_update');
위 명령은 “account_update”를 identifier로 하는 모든 세션에 대한 트레이스 작업을 활성화합니다. 데이타베이스에 연결하는 과정에서, BILL은 다음과 같은 명령을 통해 client identifier를 설정합니다:
exec DBMS_SESSION.SET_IDENTIFIER ('account_update')
account_update”를 identifier로 하는 모든 세션을 트레이스 하도록 설정된 상황이므로, 위의 세션 역시 트레이스 되고 user dump destination 디렉토리에 트레이스 파일(trace file)이 생성됩니다. 다른 사용자가 데이타베이스에 연결하면서 client identifier를 “account_update”로 설정하는 경우, 새로운 세션 역시 자동으로 트레이스 됩니다. 결국 아래와 같은 명령을 통해 트레이스를 disable할 때까지, “account_update”를 client identifier로 하는 모든 세션이 트레이스 됩니다:
exec DBMS_MONITOR.CLIENT_ID_TRACE_ENABLE('account_update');
생성된 트레이스 파일은 tkprof 툴을 통해 분석할 수 있습니다. 하지만 각각의 세션이 별도의 트레이스 파일을 생성하는 문제가 있습니다. 문제를 효과적으로 분석하려면, 통합된 형태의 트레이스 파일이 필요할 것입니다.
trcsess 툴을 이용하면 “account_update”를 client identifier로 하는 모든 세션의 트레이스 정보를 추출하여 하나의 파일에 저장할 수 있습니다. 트레이스 정보를 통합하려면 user dump destination 디렉토리로 이동하여 다음과 같이 실행합니다:
trcsess output=account_update_trc.txt clientid=account_update *
위 명령을 실행하면 account_update_trc.txt 파일이 생성됩니다. 이 파일은 일반적인 트레이스 파일과 같은 포맷을 가지며, tkprof 툴을 이용하여 분석될 수 있습니다.
이처럼 end-to-end tracing 기능을 활용하여 트레이스 정보를 보다 쉽게 수집할 수 있습니다. 또 세션 별로 alter session set sql_trace = true 명령을 사용하는 대신, client identifier를 기준으로 트레이스를 enable/disable할 수 있습니다. 같은 패키지를 통해 제공되는 SERV_MOD_ACT_TRACE_ENABLE 프로시저를 사용하면 dbms_application_info패키지를 이용해서 설정된 특정 서비스, 모듈, 또는 액션의 조합으로 트레이스를 수행하는 것도 가능합니다.
데이타베이스 사용 현황
오라클 데이타베이스의 파티셔닝(partitioning) 기능은 별도로 구매해야 하는 옵션입니다. 요즘처럼 예산이 제한되는 상황에서는, 구입을 결정하기 전에 파티셔닝이 정말로 사용자에게 효과적으로 활용되고 있는지 먼저 고민하지 않을 수 없습니다.
이 질문을 사용자에게 던지는 대신 데이타베이스에게 물어보십시오. Automatic Workload Repository(제 6 주 연재에서 설명한 바 있습니다)는 설치된 모든 기능의 사용 정보를 1주 1회의 단위로 수집합니다.
데이타베이스의 사용 패턴을 보여주는 중요한 뷰가 두 가지 존재합니다. 첫 번째로, DBA_HIGH_WATER_MARK_STATISTICS 뷰는 현재 데이타베이스에 사용되는 각 기능의 최대값을 보여줍니다. 실행 결과의 예가 아래와 같습니다.
NAME             HIGHWATER LAST_VALUE DESCRIPTION
--------------- ---------- ---------- ----------------------------------------------------------
USER_TABLES            401        401 Number of User Tables
SEGMENT_SIZE    1237319680 1237319680 Size of Largest Segment (Bytes)
PART_TABLES             12          0 Maximum Number of Partitions belonging to an User Table
PART_INDEXES            12          0 Maximum Number of Partitions belonging to an User Index
USER_INDEXES           832        832 Number of User Indexes
SESSIONS                19         17 Maximum Number of Concurrent Sessions seen in the database
DB_SIZE         7940079616 7940079616 Maximum Size of the Database (Bytes)
DATAFILES                6          6 Maximum Number of Datafiles
TABLESPACES              7          7 Maximum Number of Tablespaces
CPU_COUNT                4          4 Maximum Number of CPUs
QUERY_LENGTH          1176       1176 Maximum Query Length
위에서 확인할 수 있는 것처럼, 이 뷰는 데이타베이스의 사용 패턴에 대한 몇 가지 중요한 정보를 제공합니다. 예를 들어 사용자가 최대 12 개의 partitioned table을 생성했지만, 현재는 하나도 사용되고 있지 않음(LAST_VALUE = 0)을 확인할 수 있습니다. 이 정보는 데이타베이스가 셧다운 되는 경우에도 그대로 유지되며, 마이그레이션 작업을 위한 계획 단계에서 매우 유용하게 활용될 수 있습니다.
하지만 위의 뷰가 제공하는 정보만으로는 부족한 부분이 있습니다. 위 결과를 통해 12개의 partitioned table이 생성되었다는 것을 확인할 수 있지만 이 기능이 마지막으로 사용된 시간은 알 수 없습니다. DBA_FEATURE_USAGE_STATISTICS 뷰를 사용하면 이러한 문제의 해답을 쉽게 찾을 수 있습니다. 파티셔닝 기능에 관련한 뷰의 조회 결과가 아래와 같습니다:
DBID                          : 4133493568
NAME                          : Partitioning
VERSION                       : 10.1.0.1.0
DETECTED_USAGES               : 12
TOTAL_SAMPLES                 : 12
CURRENTLY_USED                : FALSE
FIRST_USAGE_DATE              : 16-oct-2003 13:27:10
LAST_USAGE_DATE               : 16-dec-2003 21:20:58
AUX_COUNT                     :
FEATURE_INFO                  :
LAST_SAMPLE_DATE              : 23-dec-2003 21:20:58
LAST_SAMPLE_PERIOD            : 615836
SAMPLE_INTERVAL               : 604800
DESCRIPTION                   : Oracle Partitioning option is being used -
                                there is at least one partitioned object created.
이 뷰를 통해 파티셔닝 기능이 현재 데이타베이스에서 사용되지 않고 있으며 (CURRENTLY_USED : FALSE), 마지막으로 액세스된 시간이 2003년 12월 16일 오후 9시 20분임을 알 수 있습니다. 사용 현황 정보의 샘플링은 매 604,800초 (7일) 단위로 수행되고 있습니다 (SAMPLE_INTERVAL 컬럼). 그리고 LAST_SAMPLE_DATE 컬럼은 사용 현황 정보가 마지막으로 샘플링 된 시점을 표시하고 있습니다.
커맨드 라인 인터페이스 대신 Enterprise Manager 10g를 사용해서 같은 정보를 확인할 수도 있습니다. 이를 위해 EM에서 Administration 탭으로 이동 한 후 Configuration Management 항목 아래의 “Database Usage Statistics” 링크를 클릭합니다 (그림 1과 2 참조).
figure 1
그림 1: Database Usage Statistics 페이지
figure 2
그림 2: Database Usage Statistics – 기능별 드릴다운
더 쉽고 강력해진 암호화 옵션
DBMS_OBFUSCATION_TOOLKIT (DOTK) 패키지를 기억하십니까? Oracle9i 및 이전 버전에서 암호화를 적용하려면 이 패키지를 사용하는 것 이외에 다른 대안이 없었습니다. DOTK 패키지는 대부분의 보안제품처럼 대부분의 데이타베이스 환경에 적절한 수준의 보안 환경을 제공했지만, 숙련된 해커의 공격에 쉽게 무력화되는 약점이 있었습니다. DOTK 패키지에 결여된 중요한 기능의 하나로, 기존의 DES(Digital Encryption Standard) 및 DES3 (Triple DES)보다 강력한 암호화 기능을 제공하는 Advanced Encryption Standard(AES)를 들 수 있습니다.
10g에는 보다 강력한 암호화 패키지인 DBMS_CRYPTO가 추가되었습니다. 이 내장 패키지는 DOTK에 구현되지 않은 암호화 기능을 모두 포함하고 있을 뿐 아니라, 기존 함수와 프로시저에 대해서도 보다 개선된 기능을 제공합니다. (한 예로, DBMS_CRYPTO에는 최신 암호화 기술인 256 비트 AES 알고리즘이 포함되어 있습니다.) DBMS_CRYPTOENCRYPT 함수(프로시저로도 사용됩니다)는 아래와 같은 매개변수를 사용합니다:

매개변수 설명
SRC 암호화 대상이 되는 입력 데이타입니다. RAW 데이타 타입만을 지원하며, 다른 데이타 타입의 경우 먼저 변환 과정을 거쳐야 합니다. character 변수 p_in_val에 대해 변환작업을 수행하려면 다음과 같이 실행합니다:
utl_i18n.string_to_raw (p_in_val, 'AL32UTF8');
문자열을 character set AL32UTF8, 데이타 타입RAW로 변환하기 위해 UTL_IL8N 패키지가 사용됩니다. DOTK와 달리, DBMS_CRYPTO는 character 변수를 매개변수로 사용하지 않습니다. 또 DOTK 패키지에서 요구하던 것처럼, character에 대한 padding을 수행하여 그 길이를 16의 배수로 만들 필요가 없습니다. ENCRYPT 함수(또는 프로시저)는 자동으로 padding을 수행합니다.
KEY 암호화 키를 정의합니다. 키의 길이는 사용되는 알고리즘에 따라 달라집니다.
TYP 암호화 타입과 padding 방법을 정의합니다. 예를 들어 AES 256-bit 알고리즘, Cipher Block Chaining, PKCS#5 padding을 사용하려는 경우 아래와 같이 입력합니다:
typ => dbms_cryptio.encrypt_aes256 + 
       dbms_cryptio.chain_cbc + 
	   dbms_cryptio.pad_pkcs5

The ENCRYPT ENCRYPT 함수는 암호화된 값을 RAW 데이타 타입으로 반환합니다. 반환된 결과를 문자열로 변환하려면 아래와 같이 실행합니다:
utl_i18n.raw_to_char (l_enc_val, 'AL32UTF8')
which is the reverse of the casting to RAW.
암호화된 결과를 해독하려면 DECRYPT 함수(또는 프로시저로도 사용됩니다)를 사용합니다. 이처럼 DBMS_CRYPTO 패키지를 사용하여. 데이타베이스 환경에 개선된 보안 모델을 구현할 수 있습니다.
결론
Oracle Database 10g가 제공하는 새로운 기능을 모두 설명하는 것은 불가능에 가깝습니다. 하지만 지난 20주에 걸쳐, DBA에게 가장 중요한 의미를 갖는 일부 기능을 선별하여 소개했으며, 이번 주에는 지난 19주 동안 소개되지 않았던 몇 가지 기능을 마지막으로 설명해 드렸습니다. 이번 연재가 여러분에게 유익했기를 바랍니다. 의견이 있으신 경우 언제든 보내주시기 바라며, 여러분이 가장 마음에 들어 하는 기능이 어떤 것이었는지 알려주시면 제게 많은 참고가 될 것입니다.

'oracle' 카테고리의 다른 글

오라클 noarchive -> archive 변환  (0) 2008.03.02
오라클 scott/tiger 접속  (0) 2008.02.18
오라클 에러 1004가지  (1) 2008.02.14
출처 ㅣ http://www.cisco.com/kr





iSCSI 소개

개요


이 백서에서는 iSCSI(Internet Small Computer Systems Interface) 프로토콜에 대한 기본적인 작업 지식을 설명합니다. iSCSI는 TCP/IP 네트워크를 통해 블록 중심형 스토리지 데이터를 맵핑하기 위한 SCSI 전송 프로토콜입니다. 이 문서에서는 주로 iSCSI에 대해 설명하므로 SCSI와 SAN(Storage-Area Network) 프로토콜 및 아키텍처에 대한 배경 지식이 필요합니다. 이러한 영역을 다루는 백서 목록은 이 문서의 끝 부분을 참조하십시오.

이 문서에서 제공하는 iSCSI 프로토콜에 대한 설명은 인터넷 엔지니어링 태스크포스(IETF)의 IP Storage(IPS) iSCSI 초안 10을 기초로 합니다. 자세한 내용을 보려면 이 문서의 끝 부분에 있는 참조 URL을 이용하십시오.
이 문서에는 iSCSI 프로토콜 및 프로세스에 대한 분류와 iSCSI 보안 및 관리상의 고려 사항 및 기본적인 구현 정보가 포함되어 있습니다. 또한 iSCSI 프로토콜과 관련된 용어와 약어에 대한 정의도 포함되어 있습니다.

iSCSI 전망


iSCSI에 대한 기본 개념


개념적으로 iSCSI+TCP+IP는 병렬 SCSI 케이블이나 FCP(Fibre Channel Protocol; 파이버 채널을 통한 SCSI)의 대체 방법이 아니라 Layer3/4 네트워크 전송과 동일한 방법입니다. iSCSI의 기본 개념은 기존 IP 네트워크에의 투자를 활용하여 SAN을 촉진하고 확장하는 것입니다. 이를 위해 호스트와 SAN 노드 사이에서 TCP/IP 프로토콜을 사용하여 SCSI 명령과 데이터를 전송합니다.

일반적으로 SAN에서는 호스트와 스토리지 시스템을 상호 연결하는 별도의 전용 인프라가 필요했습니다. 이러한 상호 연결을 위한 기본 수단은 SCSI 전송을 제공하는 파이버 채널 네트워크이기 때문에 결과적으로 IP 애플리케이션과 관련 스토리지를 지원하기 위해 별도의 병렬 네트워크를 구축해야 합니다. 또한 파이버 채널은 원시 형태로 낮은 대역폭의 WAN 네트워크를 통해 전송될 수 없으므로 특수한 하드웨어와 처리가 필요합니다. 하지만 IP 네트워크에서 iSCSI를 사용하면 파이버 채널 네트워크를 교체하지 않아도 IP로 연결된 호스트가 파이버 채널 기반 SAN에 도달할 수 있습니다.

IP 네트워크 인프라는 서버와 블록 지향형 스토리지 장치를 효율적으로 상호 연결할 수 있다는 장점이 있습니다. IP 네트워크는 비용면에서 효율적이며 보안과 확장성, 호환성, 네트워크 관리 및 스토리지 관리 기능을 제공합니다.
IP 네트워크는 다음과 같은 장점이 있습니다.

  • IP 네트워크는 관리, 보안 및 서비스 품질(QoS)을 위한 미들웨어 및 네트워크 프로토콜에 대한 가용성이 높습니다.
  • IP 네트워크의 설계 및 관리에서 개발된 기술을 IP SAN에 적용할 수 있습니다. 경험 많고 숙련된 IP 네트워킹 직원을 활용하여 이 네트워크를 설치하고 운영할 수 있습니다.
  • IP 네트워크에서는 전체 조직이 표준 IP 인프라, 제품 및 서비스를 사용함으로써 경제적인 이득을 얻을 수 있습니다.
  • iSCSI는 기존의 IP LAN 및 WAN 인프라와 호환됩니다.
  • 거리를 제한하는 것은 IP 네트워크가 아니라 애플리케이션 시간 종료 때문입니다.

    iSCSI의 가치


    기존의 IP 네트워크에 구축하기 때문에 호스트를 스토리지 장치에 연결하기 위해 호스트 어댑터를 추가할 필요가 없고 스토리지 리소스를 보다 잘 이용할 수 있으며 별도의 WAN 인프라가 필요하지 않습니다. iSCSI에서는 SCSI에 대한 전송 수단으로 TCP/IP를 사용하므로 주로 이더넷으로 연결되는 기존의 IP 기반 호스트 연결을 통해 정보를 전달할 수 있습니다. 이외에도 기존의 스토리지 리소스를 보다 잘 활용할 수 있다는 장점이 있습니다. 호스트에서 기존의 IP/이더넷 네트워크 연결을 사용하여 스토리지 요소에 액세스할 수 있으므로 스토리지를 통합하고 활용도를 높이기가 쉬워졌습니다. 앞에서 설명한 대로 과거의 SAN에서는 WAN 연결을 별도로 제공해야 했습니다. 이제 기존의 WAN 연결을 사용하여 호스트가 IP를 통해 스토리지에 액세스함으로써 비용을 상당히 절약할 수 있습니다.

    다른 IP SAN 프로토콜


    IP 네트워크를 통한 스토리지 트래픽 전송에 관해 제안된 다른 초안들도 있습니다. 여기에는 FCIP(Fibre Channel over IP), iFCP(Internet Fibre Channel) 및 iSNS(Internet Storage Name Service) 등이 있습니다. 이러한 프로토콜은 이 문서의 범위를 벗어나지만 참고 자료에서 자세한 내용을 확인할 수 있습니다.

    iSCSI 표준 트랙


    iSCSI 초안은 IETF의 작업 그룹인 IPS(IP Storage)에서 개발 중인 여러 프로토콜 중 하나입니다. 시스코, IBM, HP와 같은 업계 선두 업체들은 초안을 표준화하기 위해 노력하고 있습니다. 현재 릴리즈는 2002년 6월 1일에 발표된 draft-ietf-ips-iscsi-14입니다. iSCSI 초안은 금년 후반기에 인터넷 엔지니어링 스티어링 그룹(IESG)에 제출되어 제안된 표준으로서 검토될 예정입니다. 수많은 공급 업체들이 현재의 초안 표준에 따라 제품을 개발하고 있습니다. 사전 표준 iSCSI 솔루션을 설치할 경우에는 상호 운용성을 이루기 위해 공급 업체 제품이 기반으로 하는 초안을 확인해야 합니다.

    iSCSI의 기초


    이 단원에서는 전체적인 기능을 이해하기 위해 iSCSI의 다양한 레이어와 프로세스에 대해 설명합니다. 따라서 자세한 패킷의 포맷과 구조는 의도적으로 생략되었습니다. 여기서는 주로 iSCSI와 IP를 통한 SCSI 전송에만 중점을 둡니다. 그러나 이 내용을 보다 쉽게 이해할 수 있도록 SCSI 아키텍처에 대해 간단히 설명합니다.

    SCSI 아키텍처


    SCSI는 Small Computer Systems Interface를 나타냅니다. 이 용어는 디스크 드라이브 및 컨트롤러 제조업체인 SASI(Shugart Associates System Interface)에서 처음 사용되었습니다. IBM 입/출력(I/O) 채널을 기반으로 한 SASI 인터페이스는 널리 수용되었습니다. 처음 소개된 1979년 당시는 8비트 병렬 인터페이스만 사용할 수 있었습니다. SASI는 기본적으로 독립적인 병렬 장치를 중소형 컴퓨터에 연결하는 데 사용되었습니다.

    1982년에 SASI를 기반으로 한 공식적인 SCSI 초안이 개발되었으며, 여기에 기능이 추가되어 1세대 SCSI 표준이 되었습니다. 새 기능에는 피어-투-피어 통신, 논리 장치, 중재 등이 있습니다.

    미국 국립 표준기구(ANSI)는 1994년에 SCSI-2를 승인했습니다. SCSI-2는 모든 디스크 드라이버 외에도 많은 주변 장치에 소프트웨어 인터페이스를 제공하는 CCS(Common Command Set)의 확장된 정의를 포함하는 완전한 독립형 문서입니다. SCSI-2는 기본적으로 데이터 처리 성능을 배가하는 차등 인터페이스와 16비트 및 32비트 데이터 버스를 정의하며 SCSI-1과도 호환됩니다.

    SCSI-3 표준은 현재 개발 중입니다. SCSI-3은 SCSI-2를 SCSI 아키텍처 모듈(SAM)이라는 일반적인 프레임워크에 맞춰 보다 작은 계층 모듈로 분해한 표준 모음입니다(그림 1 참조).

    SAM은 SCSI 레이어의 개념, entity 및 상호 작용을 정의하며 클라이언트/서버 모델의 initiator and target entity에 대해 규정합니다. initiator and target entity 간의 상호 작용을 통해 initiator and target entity에 대한 정보를 교환할 수 있습니다(그림 2 참조). 이러한 정보의 예로 논리 장치 번호(LUN)가 있습니다.

    SAM은 일반적인 요건과 구현 요건에 대해 정의합니다. 서비스 요청에는 SCSI 정보 교환을 위한 기본 빌딩 블록인 CDB(Command Descriptor Block)가 포함되어 있어야 합니다. CDB 포맷은 그림 3과 같습니다.

    그림 3
    SCSI CDB(Command Descriptor Block)




    iSCSI 용어

    iSCSI 초안에서는 IP 네트워크에 연결된 장치 또는 게이트웨이를 나타내는 "네트워크 entity"라는 개념을 사용합니다. 이 네트워크 entity는 하나 이상의 네트워크 포털을 포함해야 합니다. 네트워크 entity에 포함된 iSCSI 노드는 네트워크 포털을 사용하여 IP 네트워크에 액세스할 수 있습니다. iSCSI 노드는 네트워크 entity 내에서 해당 iSCSI 이름으로 식별되는 iSCSI initiator and target entity입니다. SAM의 정의에 따르면 SCSI 장치란 노드의 iSCSI 이름입니다. 하나의 iSCSI 노드에는 한 개의 SCSI 장치가 있습니다.

    네트워크 포털은 기본적으로 TCP/IP 프로토콜 스택을 구현하는 네트워크 entity 내의 컴포넌트입니다. initiator entity에 관한 네트워크 포털은 해당 IP 주소로만 식별됩니다. iSCSI 대상 entity의 경우에는 해당 IP 주소와 TCP 수신 포트로 네트워크 포털을 식별합니다. iSCSI 클라이언트 및 서버의 컴포넌트는 그림 4를 참조하십시오. iSCSI 통신을 위해 initiator 네트워크 포털과 대상 네트워크 포털 간의 연결이 설정됩니다. initiator iSCSI 노드와 대상 iSCSI 노드 간의 TCP 연결 그룹이 iSCSI 세션을 구성합니다. SCSI I_T Nexus와 비슷하지만 똑같지는 않습니다.

    그림 4
    iSCSI 클라이언트 및 iSCSI 서버의 컴포넌트




    표준 초안에서는 포털 그룹에 대해서도 정의합니다. iSCSI는 한 세션 내에서 다중 TCP 연결을 지원하므로 여러 네트워크 포털 간을 연결할 수 있습니다. 따라서 포털 그룹은 다른 네트워크 포털 간의 다중 연결로 구성되는 iSCSI 세션을 지원하는 네트워크 포털 집합입니다.

    이름 및 주소 지정


    iSCSI 프로토콜에서는 iSCSI initiator entity와 대상 entity의 이름 및 주소를 지정하기 위해 한 가지 방법을 사용합니다. 즉, 모든 iSCSI 노드와 initiator entity 및 대상 entity는 해당 iSCSI 이름으로 식별됩니다. iSCSI 이름은 IP 주소로 분석되는 호스트 유형 이름이나 세계적으로 통용되는 노드 이름이 아닙니다. 따라서 이 이름은 노드 위치와 상관이 없습니다. iSCSI 이름 포맷에는 iqn(iSCSI 한정자 이름) 포맷과 IEEE EUI 포맷 등 두 가지가 있습니다. iqn 포맷의 iSCSI 이름은 iqn.1987-05.com.Cisco.00.9f9ccf185aa2508c7a168967ccf96e0c.target1과 같습니다.
    iSCSI 이름은 다음과 같은 기능을 제공한다는 점에서 유용합니다.
  • 여러 initiator and target entity가 공통 IP 네트워크 주소를 공유할 수 있는 방법
  • 다중 IP 주소를 통해 여러 initiator 또는 대상 entity를 액세스할 수 있는 방법
  • 노드의 IP 주소와 방화벽에서의 IP 주소 및 포트 맵핑의 여부에 관계 없이 노드를 인식할 수 있는 방법

    iSCSI 프로토콜은 iSCSI 이름에 대해 대소문자 일치 작업 이외의 어떤 처리 작업도 수행하지 않습니다.

    iSCSI initiator entity 이름은 전세계적으로 이 initiator entity를 식별하는 고유 이름입니다. 마찬가지로 iSCSI 대상 entity 이름은 전세계적으로 이 대상 entity를 식별하는 고유 이름을 지정합니다. 이러한 이름은 SAM에서 정의한 SCSI I_T Nexus를 식별하는 데에도 도움이 됩니다.

    iSCSI 노드에 대한 주소 지정은 <domain-name>[:port]라는 표준 기반 IP 주소 지정 체계를 따릅니다. <domain-name>은 도트 십진수 표기법인 IPv4 주소, 콜론으로 구분된 16진수 표기법인 IPv6 주소 또는 완벽한 도메인 이름 중 하나의 포맷을 사용합니다.

    iSCSI 대상 entity의 경우 주소와 함께 포트 번호도 지정할 수 있습니다. 포트를 지정하지 않으면 IANA(Internet Assigned Numbers Authority)에서 할당한 기본 포트 3260이 사용됩니다.

    iSCSI 프로토콜


    iSCSI 프로토콜은 SCSI 원격 프로시저 호출(기준 SAM) 모델을 TCP/IP 프로토콜로 맵핑합니다. iSCSI 프로토콜은 전송하는 SCSI CDB 정보와 상관 없이 자체 개념 레이어를 제공합니다. 이러한 방식으로 SCSI 명령은 iSCSI 요청에 의해 전송되고 SCSI 응답 및 상태는 iSCSI 응답에 의해 처리됩니다. iSCSI 프로토콜 작업도 이와 같은 iSCSI 요청 및 응답 메커니즘에 의해 수행됩니다(그림5 참조).

    그림 5
    iSCSI 프로토콜 스택




    SCSI 프로토콜에서와 같이 iSCSI는 "initiator entity", "대상 entity" 및 프로토콜 데이터 단위(PDU)라는 통신 메시지의 개념을 사용합니다. 마찬가지로 iSCSI 전송 방향은 initiator entity를 기준으로 정의됩니다. 성능을 개선하기 위해 iSCSI는 "단계 축소"를 사용하여 명령 또는 응답과 관련 데이터를 단일 iSCSI PDU로 보낼 수 있습니다.

    iSCSI 세션


    iSCSI 통신 경로에서 가장 높은 수준은 iSCSI initiator entity와 iSCSI 대상 entity 사이에 형성되는 세션입니다. iSCSI에서는 두 종류의 세션이 정의됩니다. 하나는 정상 작동 세션이고 다른 하나는 initiator entity가 사용할 수 있는 대상 entity를 검색하기 위해 사용하는 검색 세션입니다.

    세션은 initiator entity(ISID) 및 대상 entity(TSID) 컴포넌트로 구성된 세션 ID(SSID)로 식별됩니다. 세션 내에 TCP 연결을 추가하고 제거할 수 있지만 모든 연결은 동일한 initiator entity 및 대상 entity iSCSI 노드 간에 설정됩니다. 세션 내의 각 연결은 고유한 연결 ID(CID)를 가집니다. SSID, ISID, TSID 및 CID의 구성에 대해서는 이 문서의 뒤에서 보다 자세하게 설명합니다(그림 6 참조).

    그림 6
    연결이 한 개 이상 설정된 iSCSI 세션




    iSCSI 세션은 iSCSI 로그인 프로세스를 통해 설정됩니다. 이 세션을 사용하여 특정 SCSI I_T Nexus와 연관된 모든 TCP 연결을 식별합니다. 한 세션 내에 한 개 이상의 TCP 연결이 있을 수 있습니다.

    initiator entity가 알려진 포트나 지정된 대상 포트를 통해 원하는 대상에 대한 TCP 연결을 설정하면 로그인 프로세스가 initiator됩니다. initiator and target entity는 서로에 대한 인증을 수행하고 프로토콜에 대해 협상할 수 있습니다. 로그인 단계 중에 iSCSI initiator and target entity는 많은 속성에 대해 협상합니다.

    로그인 단계를 성공적으로 마치면 세션은 "완전 기능 단계"에 들어갑니다. 로그인과 보안에 대해서는 뒤에서 추가로 설명합니다.

    iSCSI 시퀀싱


    iSCSI 프로토콜은 명령, 상태 및 데이터의 순서 및 시퀀싱을 유지 관리하기 위해 여러 가지 레지스터를 설치합니다. 각각의 레지스터는 부호 없는 32비트 정수 카운터입니다. 명령, 상태 및 데이터를 교환하는 동안 이 숫자들은 initiator entity와 대상 entity 간에 적절한 iSCSI PDU 필드로 통신됩니다. 또한 initiator entity나 대상 entity는 NOP-OUT/IN PDU를 이용하여 시퀀싱 및 번호 지정 레지스터를 동기화할 수 있습니다.

    명령 번호 지정


    iSCSI 세션 내에서 모든 명령(initiator entity-투-대상 entity PDU)에는 명령 시퀀스 번호(CmdSN)로 번호가 지정됩니다. CmdSN을 사용하면 한 세션에서 해당 명령을 전달하는 TCP 연결이 무엇이든 관계 없이 모든 명령이 전송된 순서대로 전달됩니다.

    명령 시퀀싱은 첫 번째 로그인 명령에서 시작하여 이후의 각 명령에 대해 1씩 증가합니다. 명령을 해당 CmdSN 순서에 따라 SCSI 레이어에 전달하는 작업은 iSCSI target 레이어에서 담당합니다. 단, 즉시 전달하도록 표시된 명령은 제외됩니다. 이런 경우에는 CmdSN이 증가되지 않으며 iSCSI target entity가 명령을 탐지하는 즉시 바로 SCSI 레이어로 전달합니다.

    initiator and target entity는 CmdSN 외에도 예상 명령 레지스터(ExpCmdSN)와 최대 명령 레지스터(MaxCmdSN)를 유지 관리합니다. target entity는 SCSI 레이어에 전달할 수 있는 가장 높은 번호이며 즉시 실행되지 않는 명령인 CmdSN에 1을 더한 값을 ExpCmdSN으로 설정합니다. initiator entity는 이 번호를 통해 대상이 수신한 마지막 시퀀스 명령을 확인합니다. 여러 TCP 연결을 통해 명령을 보낼 수 있기 때문에 target entity의 대기열에 CmdSN이 ExpCmdSN보다 높은 명령이 있을 수 있습니다. 시퀀스에 맞지 않는 명령이 SCSI 레이어에 전달되지 않도록 이러한 명령은 순서대로 보관됩니다. MaxCmdSN은 initiator entity가 target entity에 추가 명령을 보낼 대기열 공간이 있는지 확인하는 데 사용됩니다. 대기열 용량은 MaxCmdSN - ExpCmdSN + 1로 구할 수 있습니다.

    상태 번호 지정


    명령 번호 지정과 비슷하게 상태 응답에도 상태 시퀀스 번호(StatSN)가 순서대로 지정됩니다. 마찬가지로 initiator entity는 예상 상태 시퀀스 번호(ExpStatSN) 레지스터를 사용하여 target entity에서 수신한 상태 PDU를 확인합니다. StatSN과 ExpStatSN의 차이가 미리 설정된 값을 초과하면 initiator entity는 복구 작업을 시작합니다.

    데이터 시퀀싱

    데이터 및 요청-투-전송(R2T) PDU는 각각 DataSN과 R2TSN을 사용하여 순서가 지정됩니다. 데이터 시퀀싱은 같은 명령 내에서 데이터를 순서대로 전달하기 위해 사용됩니다.

    읽기 작업의 경우 DataSN은 0부터 시작하고 해당 명령 시퀀스에서 이후의 각 데이터 PDU에 대해 1씩 증가합니다. 쓰기 작업인 경우 R2T에 대한 응답인 첫 번째 임의 데이터 PDU 또는 첫 번째 데이터 PDU는 DataSN 0부터 시작하고 이후의 각 데이터 PDU에 대해 1씩 증가합니다. R2TSN은 명령을 시작할 때 0으로 설정되고, target entity가 해당 명령에 대해 보낸 이후의 각 R2T에 대해 1씩 증가합니다.

    iSCSI PDU


    iSCSI 패킷의 TCP 페이로드는 iSCSI PDU를 포함합니다. 모든 iSCSI PDU는 하나 이상의 헤더 세그먼트로 시작하며 다음에 0개 또는 한 개의 데이터 세그먼트가 옵니다.
    첫 번째 세그먼트는 고정 길이 48바이트의 기본 헤더 세그먼트(BHS)입니다. BHS 다음에 추가 헤더 세그먼트(AHS)가 올 수 있습니다. 그림 7에서는 iSCSI PDU의 포맷과 내용을 보여 줍니다. BHS를 제외한 모든 헤더는 옵션입니다. 이 그림은 TCP 페이로드에 iSCSI PDU가 포함된 iSCSI 패킷에 대해 설명합니다.

    그림 7
    iSCSI 패킷 포맷




    iSCSI PDU의 종류에는 iSCSI 요청 및 응답, 텍스트 요청 및 응답, 로그인 요청 및 응답 등이 있습니다. 그림 8에서는 iSCSI 요청 명령 중 하나의 헤더 포맷에 대한 예를 보여 줍니다.

    그림 8
    iSCSI PDU 명령




    iSCSI 오류 처리

    오류 복구는 기본적으로 잘못된 프로세스를 복구하기 위해 충분한 상태 및 데이터를 유지 관리하는 것입니다. iSCSI의 경우 initiator entity는 해결되지 않은 PDU를 다시 작성하기 위해 필요한 명령과 데이터 정보를 보유해야 합니다. 이와 마찬가지로 target entity는 상태 응답 정보와 함께 확인되지 않은 데이터 출력을 유지 관리해야 합니다.

    iSCSI에서 오류 처리를 위해 사용하는 두 가지 메커니즘은 재시도와 재할당입니다. initiator entity는 같은 명령 또는 데이터 PDU를 target entity에 다시 보내서 누락된 CmdSN을 "연결(plug)"하려고 할 수 있습니다. 재할당은 initiator entity와 target entity 간의 TCP 연결이 끊어진 경우에 사용됩니다. 이 경우 initiator entity는 새로운 연결을 통해 "작업 재할당" 작업 관리 PDU를 전송함으로써 target entity에게 해결되지 않은 명령을 새로운 CID에서 계속하도록 명령합니다. target entity에서 이 기능을 지원할 필요는 없으며 로그인할 때 지원 여부를 협상합니다.

    iSCSI 프로세스

    이 단원에서는 전체적인 iSCSI 연결 설정, 교환 및 종료에 대해 설명합니다. 실제 iSCSI 구현에서는 인증, 암호화, 다양한 파라미터 설정, 기타 SCSI 작업 등과 같은 여러 가지 다른 작업을 수행할 수 있습니다. 이 단원의 목적은 iSCSI 단계 및 프로세스 흐름에 대한 기본적인 이해를 제공하기 위한 것입니다.

    로그인 프로세스는 로그인 initiator 요청으로 시작되고 이후에 initiator entity가 추가 로그인 요청으로 응답하는 로그인 부분 응답(옵션)이 수행되는 로그인 단계의 측면에서 설명됩니다. 추가 파라미터 설정이 필요할 때마다 로그인 부분 응답과 추가 로그인 요청을 반복할 수 있습니다. 이 단계 이후에는 target entity로부터 로그인 최종 응답이 수행되어 "로그인 허용"이나 "로그인 거부"를 나타내야 합니다.

    로그인 시작


    iSCSI 로그인은 iSCSI initiator entity와 iSCSI target entity 간의 iSCSI 세션을 설정하는 데 사용됩니다. TCP 연결은 iSCSI 세션에 속해 있어야 하고 iSCSI initiator entity와 iSCSI target entity는 보안 인증 및 기타 운영 파라미터를 교환하고 합의합니다.
    이 프로세스는 initiator entity가 target TCP 수신 포트에서 target entity에 대한 TCP 연결을 열고 CID를 할당하면서 시작됩니다. 그런 다음 initiator entity는 자신이 지원하는 프로토콜 버전, SSID, CID 및 시작하려는 설정 단계 등이 포함된 로그인 요청을 보냅니다. 선택 사항으로 초기 요청에 보안 파라미터나 iSCSI 운영 파라미터를 포함시킬 수도 있습니다. 그림 9에서는 iSCSI 로그인 프로세스를 보여 줍니다.

    그림 9
    iSCSI 로그인 패킷 흐름



    앞에서 설명한 대로 SSID는 ISID와 TSID로 구성됩니다. ISID는 SSID의 처음 6바이트로 1바이트의 유형, 3바이트의 이름 지정 기관 및 2바이트의 한정자 필드로 구성됩니다(표 1 참조). 유형 바이트는 이름 지정 기관의 포맷을 나타냅니다(표 2 참조). ISID의 이름 지정 기관(Naming Authority) 필드는 이 iSCSI initiator entity의 공급업체 또는 조직입니다. 마지막으로 한정자는 부호 없는 16비트 정수로 특정 initiator entity와 target 포털 그룹 조합에 대해 고유해야 합니다.

    TSID는 target entity의 iSCSI 노드에 따라 결정되는 16비트 값입니다. initiator entity가 target entity와의 새 세션을 설정하면 초기 로그인 요청의 TSID는 0으로 설정됩니다. 이 값은 새 세션이 요청되었음을 target entity에게 알립니다. target entity는 TSID를 생성하여 로그인 응답으로 반환합니다. initiator entity가 기존 세션에서 추가 TCP 연결을 설정하려는 경우에는 세션이 생성되었을 때 이전에 성공한 로그인 시도에서 알게 된 TSID를 사용합니다. 값이 0이 아니므로 target entity는 initiator entity가 이미 설정된 세션에 연결을 추가하려고 한다는 것을 알 수 있습니다. initiator entity가 초기 로그인 응답에서 TSID를 수신하면 SSID가 완성되며 이 SSID는 이후의 모든 로그인 PDU에서 세션을 식별하는 데 사용됩니다.
    .

    표 1: 세션 ID 필드

    SSID
    바이트 0
    바이트 1
    바이트 2
    바이트 3
    단어 0
    유형
    이름 지정 기관
    단어 1
    한정자
    TSID

    initiator entity 부분: ISID

    target entity 부분: TSID

    표 2: 유형 바이트 정의

    유형 이름 지정 기관
    0x00 IEEE OUI
    0x01 IANA 기업 번호
    0x02 "임의"
    0x03-0xFF 예약됨

    인증 및 파라미터 설정

    initiator and target entity를 인증해야 하는 경우에는 운영 파라미터를 교환하기 전에 인증을 설정해야 합니다. 인증에는 다양한 방법을 사용할 수 있으며 이에 대해서는 뒤에서 설명합니다. 파라미터 협상은 initiator entity가 보낸 초기 로그인 요청으로 시작됩니다. 초기 entity는 인증이 성공적으로 완료된 후에(필요한 경우) 파라미터 협상을 진행할 수 있습니다. 일부 운영 파라미터는 텍스트 포맷으로 전달됩니다. 이러한 교환에서 사용하는 포맷은 다음과 같습니다.

    발신 포맷: <key>=<value>
    응답 포맷: <key>=<value>|None|Reject|NotUnderstood|Irrelevent|

    <value> 인수는 숫자, 리터럴, 부울(예 또는 아니오) 또는 쉼표로 구분한 리터럴 값의 목록이 될 수 있습니다. 발신자가 <values> 목록을 제공하면 응답자는 지원되는 첫 번째 값으로 응답하거나, 지원되지 않는 경우 "거부(Reject)"해야 합니다. 리터럴 목록의 경우 "없음(None)" 응답은 "없음(None)"이 사용 가능한 값 중 하나로 제공된 경우에만 허용됩니다. 공급업체가 키 앞에 X를 첨부하고 뒤에 해당 도메인 이름을 역으로 붙여 새로운 <key>를 추가할 수도 있습니다.

    연결


    initiator entity가 target entity로부터 로그인 최종 응답을 수신하면 로그인 프로세스가 종결됩니다. "로그인 거부"라는 응답을 받으면 로그인에 실패한 것이며 initiator entity는 CID로 식별되는 해당 TCP 연결을 닫아야 합니다. "로그인 허용" 응답을 수신하면 세션은 완전한 기능 단계로 들어갑니다(초기 로그인 시도인 경우). 완전한 기능 단계에 도달한 경우에만 initiator entity가 iSCSI PDU에 포함된 SCSI 명령과 데이터 정보를 보낼 수 있습니다.

    주어진 iSCSI 세션에 대해 여러 개의 TCP 연결이 설정되어 있으면(다중 로그인) 이후의 데이터 및 응답 PDU는 관련 명령을 보냈던 TCP 연결(CID)을 통해 보내야 합니다. 이런 개념을 "연결 고수(connection allegiance)"라고 합니다. 발신 CID가 실패한 경우 앞에서 설명한 오류 복구 절차로 인해 연결 고수가 다시 설정될 수 있습니다. 반대로 SCSI 작업과 관련된 여러 개의 명령을 다른 TCP 연결을 통해 보낼 수도 있습니다. 또한 관련 없는 SCSI 명령, 데이터 및 상태를 iSCSI 세션에 끼워넣을 수 있습니다. 그러나 각각의 해당 데이터와 응답은 연결 고수 규칙을 따라야 합니다.

    협상되는 운영 파라미터 중 하나는 target entity가 발신 데이터 전송(SCSI Write)을 위해 유도(R2T) 모드 또는 임의 모드로 작동하는지를 나타냅니다. 임의 모드인 경우 initiator entity가 "즉시 데이터"를 명령과 같은 PDU로 보내거나(단계 축소) 별도의 PDU로 보낼 수 있습니다. 각각의 경우에 대해 initiator entity가 보낼 수 있는 데이터의 최대량은 로그인 시 설정할 수 있습니다. 초기 즉시 데이터를 보낸 이후의 모든 데이터 PDU는 R2T 응답에 대한 회신으로 보내야 합니다(유도 모드).

    initiator and target entity는 명령, 데이터 및 응답 교환의 순서를 유지 관리하기 위해 앞에서 설명한 시퀀스 번호 지정 체계를 사용합니다. initiator entity는 비동기적 조건을 확인하면 SNACK 요청을 보낼 수 있습니다. 단일 SNACK은 시퀀스 번호 레지스터에 따라 누락된 연속 데이터 집합을 처리합니다.

    로그아웃 및 종료


    로그아웃 프로세스는 iSCSI 연결 또는 세션을 닫기 위한 적절한 종료 메커니즘입니다. 로그아웃 절차를 시작하는 것은 initiator entity이지만 내부 오류 조건을 나타내는 비동기 iSCSI 메시지를 보내 이 절차를 수행하는 것은 target entity입니다. 어떤 경우든 initiator entity가 로그아웃 요청을 보낸 후에는 다른 요청을 보낼 수 없습니다. target entity에서 로그아웃 응답을 보내면 정리가 완료되었고 이 연결에서 다른 응답을 보낼 수 없다는 것을 나타냅니다. 또한 로그아웃 응답에는 target entity에서 보낸 복구 정보도 포함되어 있습니다. 이런 복구 정보에는 target entity가 복구를 위해 명령 정보를 보류하면서 대기하는 시간 길이(Time2Retain)와 initiator entity가 연결을 다시 설정할 수 있기까지 기다려야 하는 시간 길이(Time2Wait) 등이 있습니다. 마지막으로 TCP FIN을 보내 연결을 종료합니다.

    보안 고려 사항


    과거에는 스토리지 장치 및 스토리지 네트워크와 관련된 보안은 주요 고려 사항이 아니었습니다. 스토리지 장치는 호스트에 직접 연결되거나 사용자가 액세스할 수 있는 네트워크와는 별도의 SAN을 통해 연결되었습니다.

    iSCSI의 경우 다른 IP 기반 SAN 프로토콜과 마찬가지로 스토리지 정보를 개방형 IP 네트워크를 통해 전송하기 때문에 보안 위험이 있습니다. 이런 점을 알고 있기 때문에 IP Storage 작업 그룹에서는 IP 기반 스토리지 통신을 보안하기 위한 초안을 개발했습니다. 이 작업은 "Securing Block Storage Protocols over IP" 초안에 포함되어 있습니다. iSCSI 프로토콜 초안에서는 보안, 인증 및 패킷 보호와 관련하여 두 가지 요소를 지정합니다.

    인증


    iSCSI에서는 로그인 프로세스 중에 target entity가 initiator entity를 인증할 수도 있고 initiator entity가 target entity를 인증할 수도 있습니다. 이러한 인증은 파라미터 설정이나 로그인 허용 전에 수행됩니다. 인증을 이용하는 경우 iSCSI 세션 내의 각 연결이 인증되어야 합니다. iSCSI 초안에는 다음 인증 방법이 정의되어 있으며 로그인 단계에서 "AuthMethod" 키를 통해 설정됩니다.

    KRB5 커버로스 V5
    SPKM1 단순한 공개 키 일반 보안 서비스(GSS) 애플리케이션 프로그래밍 인터페이스(API) 메커니즘
    SPKM2 단순한 공개 키 GSS API 메커니즘
    SRP 보안 원격 암호
    CHAP 챌린지 핸드셰이크(Challenge Handshake) 인증 프로토콜
    없음 인증 없음

    이 메커니즘은 target 장치에 권한 없이 연결하는 것을 방지할 수 있지만 연결한 이후의 PDU 교환에 대해 보호하지는 못합니다.

    패킷 보호

    패킷 보호는 iSCSI 노드 간 통신에서의 무결성, 인증 및 기밀 유지를 보장합니다. iSCSI 연결의 경우 IP 레이어에서 개인 정보를 안전하게 교환하기 위해 IP Security(IPSec)를 사용합니다. 초안을 준수하려면 iSCSI 네트워크 요소는 재생 방지 기능을 비롯하여 ESP(Encapsulating Security Protocol)의 IPSec 터널 모드를 구현해야 합니다. iSCSI 구현의 빠른 속도로 인해 IPSec 시퀀스 번호 확장은 속도에 따라 구현될 수 있고 또 구현되어야 합니다.

    기밀 유지를 위해 3DES(Triple Digital Encryption Standard)를 사용하여 CBC(Cipher Block Chaining) 모드로 IPSec 터널을 암호화합니다. iSCSI 노드는 인증, 보안 연관 협상 및 키 관리를 제공하기 위해 IKE(Internet Key Exchange)를 지원해야 합니다. 별도의 IKE Phase 2 Security Association은 iSCSI 세션 내의 각 TCP 연결을 보호합니다.

    iSCSI 구현


    Cisco SN 5420 및 5428 스토리지 라우터는 iSCSI 초안 8을 지원합니다. 이들 라우터는 iSCSI를 블록 수준 스토리지 액세스를 위한 전송 수단으로 사용합니다. 일반적인 파이버 채널 SAN에서 스토리지 장치/LUN은 IP 네트워크 호스트에 마치 직접 연결되어 있는 것처럼 표시됩니다. iSCSI 작업을 지원하려면 IP 호스트에 iSCSI 드라이버나 iSCSI 네트워크 인터페이스 카드를 설치해야 합니다.

    Cisco SN 5420 스토리지 라우터는 iSCSI target entity로 작동하는 반면 IP 호스트는 iSCSI initiator entity 기능을 합니다. IP 호스트는 Cisco SN 5420 스토리지 라우터에 접속하여 정의된 iSCSI target 주소와 포트를 통해 iSCSI 로그인을 수행합니다. 스토리지 라우터는 교환되는 정보와 설정된 파라미터에 따라 로그인(또는 다중 로그인)을 허용하거나 거부합니다. Cisco SN 5420은 iSCSI target/LUN을 실제의 물리적 target/LUN에 맵핑합니다. iSCSI 로그인에 성공하면 작동이 완전한 기능 단계로 돌입하고 데이터 전송을 할 수 있게 됩니다. 따라서 IP 네트워크에 있는 iSCSI initiator entity가 물리적 스토리지 target/LUN을 사용할 수 있게 됩니다. 그림 10에서는 Cisco SN 5420 구현의 iSCSI target 모드 또는 SCSI 라우터 모드를 보여 줍니다.

    그림 10

    iSCSI 구현 예




    Cisco SN 5420은 iSCSI target 모드 외에도 투명 모드와 iSCSI 다중 initiator entity 모드를 지원합니다. Cisco SN 5420은 주로 분산 환경에서의 스토리지 통합/중앙 집중화 및 원격 백업에 사용됩니다.

    Cisco SN 5420 스토리지 라우터에 대한 자세한 내용은 아래의 Cisco 웹 페이지를 참조하십시오.
    http://www.cisco.com/warp/public/cc/pd/rt/5420/

    요약


    iSCSI의 표준화는 스토리지 업계에서 혁신적인 기술이 될 것입니다. iSCSI는 기존 스토리지 네트워크의 물리적 제약을 제거하므로 가까운 장래에 워크그룹 및 엔터프라이즈 네트워크에 상당한 영향을 끼치는 기술이 될 것이 확실합니다. 표준 기반의 장치가 많이 출시되고 있기 때문에 네트워크 기술자들은 iSCSI 프로토콜 및 이와 관련된 구현 요건에 대해 이해할 필요가 있습니다. 이 문서는 특히 iSCSI 프로토콜에 중점을 두고 있으며 구현 시의 고려 사항에 대해서는 다루지 않습니다. 하지만 성능, 보안, 애플리케이션 요건 등 구현과 관련된 일부 주제는 여기서 설명합니다. 구현 시의 고려 사항으로는 장치 기능, 네트워크 기능, QoS 파라미터, 애플리케이션별 요건 등이 있습니다. 이러한 영역에 대한 정보는 백서, 설계 안내서, 제품 인증서 및 애플리케이션 정보와 같은 추가 자료를 참조하십시오.



    추가 자료는 다음과 같은 웹 사이트에서 살펴볼 수 있습니다.

    http://www.cisco.com
    http://www.ietf.org/html.charters/ips-charter.html
    http://www.snia.org/

    참조 문서


    IETF IPS 작업 그룹, 문서: draft-ietf-ips-iscsi-14
    IETF IPS 작업 그룹, 문서: draft-ietf-ips-security-11
    SCSI-3 아키텍처 모드, 문서 번호: X3.270-1996

windows_xp 다른사양의 컴퓨터 완벽 복구 방법

고스트 사용의 경우 내컴퓨터를 백업하여 복구하면 빠르면 5분정도면 복구를 할수있는데 xp의 경우는 남의 컴퓨터에 내컴의 이미지로 복구를 하면 사용할수 없게 되어있습니다.
그러나 아래와 같은 방법을 사용하면은 모든 컴퓨터를 빠르게 설치 (복제) 할수 있는방법 입니다.

winXP 에서는 복제용 툴이 제공되고 있습니다.
Windows_XP 원본CD의 SupportTools-Deploy.cab 파일이 복제 툴입니다.

사용방법은 간단합니다..

윈도우 설치후 여러가지 프로그램 설치후 Windows_XP 원본CD의 SupportTools-Deploy.cab 파일의 압축을 풀면 Sysprep.exe Setupcl.exe 파일이 나옵니다.
위두개 파일을 C:\WINDOWS\system32\ ( Sysprep ) 만든후 같은 폴더에 붙여넣기합니다.

Sysprep폴더의 Sysprep.exe를 실행합니다.

실행시 대화창이 나오면 (확인)을 누릅니다.

다시뜨는 창에서 (다시봉인)을 누릅니다

다음 창에서 (확인)을누루면 작업완료.

컴퓨터가 ACPI 호환인 경우에는 저절로 종료됩니다.

그렇지 않으면 컴퓨터를 종료해도 좋다는 대화 상자가 나타납니다.


컴퓨터 종료후 고스트나 드라이브이미지나 기타 백업 유틸을 사용해서 윈도우가 설치된 하드 드라이브 백업

** 다른 사양의 컴퓨터복원 완료후 **
플러그 앤 플레이 검색 - 3분 정도 걸립니다.
최소 설치에서 사용자에게 다음을 요구합니다.
EULA에 동의
이름과 조직 지정
도메인 또는 작업 그룹에 추가
국가별 옵션 정보 지정
TAPI 정보 지정
설치할 네트워킹 프로토콜 및 서비스 지정

여타 다른 드라이버는 재 설치하게 됩니다.

주의 : 설치시 사용했던 씨디 KEY를 요구하게 되니 준비해 놓으시기를......

원본 컴퓨터와 대상 컴퓨터에서 IDE 또는 SCSI와 같은 대용량 저장 컨트롤러만 같으면 모뎀, 사운드 카드, 네트워크 카드, 비디오 카드 등 플러그 앤 플레이 장치는 같지 않아도 됩니다. 그러나 Drivers.cab에 포함되지 않은 모든 장치 드라이버는 Sysprep을 실행하기 전에 마스터 설치에 포함되어야 합니다.
또는 처음 실행할 때 제거된 드라이버를 대상 컴퓨터에서 사용할 수 있는지 확인하여 플러그 앤 플레이가 해당 드라이버를 검색하고 설치할 수 있도록 합니다.

쉽게 말하면 하드웨어에 필요한 드라이버는 위와같이 하지않아도 준비하고 있다가 요구할 때 먹여주면 됩니다. 아시겠죠? 친구나 친척, 애인이나 조카가 XP설치를 부탁할때 OK^^
======================
이상입니다.

출처 : 윈비비에스

+ Recent posts