|
|
| (같은 사용자의 중간 판 2개는 보이지 않습니다) |
| 1번째 줄: |
1번째 줄: |
| = SQL SERVER (Microsoft SQL Server) = | | = SQL SERVER (Microsoft SQL Server) = |
| == [[설치 방법]] == | | == [[설치 방법|설치 및 접속]] == |
| == [[DB생성 및 접속]] ==
| |
| == [[세그먼트 (테이블+인덱스+LOB) 사이즈 조회 ]] == | | == [[세그먼트 (테이블+인덱스+LOB) 사이즈 조회 ]] == |
|
| |
|
| * 스키마별 테이블에 대해 데이터 + 인덱스 + LOB + Row Overflow를 한 번에 집계합니다. | | * 스키마별 테이블에 대해 데이터 + 인덱스 + LOB + Row Overflow를 한 번에 집계합니다. |
|
| |
| === DBA_SEGMENTS 스타일의 테이블 용량 요약 ===
| |
| <source lang=sql>
| |
| SET NOCOUNT ON;
| |
|
| |
| WITH T AS
| |
| (
| |
| SELECT
| |
| ps.object_id,
| |
|
| |
| /* TABLE DATA
| |
| Heap 또는 Clustered Index
| |
| index_id = 0 : Heap
| |
| index_id = 1 : Clustered Index
| |
| */
| |
| SUM(
| |
| CASE
| |
| WHEN ps.index_id IN (0, 1)
| |
| THEN ps.reserved_page_count
| |
| ELSE 0
| |
| END
| |
| ) AS table_reserved_pages,
| |
|
| |
| SUM(
| |
| CASE
| |
| WHEN ps.index_id IN (0, 1)
| |
| THEN ps.used_page_count
| |
| ELSE 0
| |
| END
| |
| ) AS table_used_pages,
| |
|
| |
| /* NONCLUSTERED INDEX */
| |
| SUM(
| |
| CASE
| |
| WHEN ps.index_id > 1
| |
| THEN ps.reserved_page_count
| |
| ELSE 0
| |
| END
| |
| ) AS index_reserved_pages,
| |
|
| |
| SUM(
| |
| CASE
| |
| WHEN ps.index_id > 1
| |
| THEN ps.used_page_count
| |
| ELSE 0
| |
| END
| |
| ) AS index_used_pages,
| |
|
| |
| /* LOB */
| |
| SUM(ps.lob_reserved_page_count) AS lob_reserved_pages,
| |
| SUM(ps.lob_used_page_count) AS lob_used_pages,
| |
|
| |
| /* ROW OVERFLOW */
| |
| SUM(ps.row_overflow_reserved_page_count)
| |
| AS row_overflow_reserved_pages,
| |
|
| |
| SUM(ps.row_overflow_used_page_count)
| |
| AS row_overflow_used_pages,
| |
|
| |
| /* ROW COUNT */
| |
| SUM(
| |
| CASE
| |
| WHEN ps.index_id IN (0, 1)
| |
| THEN ps.row_count
| |
| ELSE 0
| |
| END
| |
| ) AS row_count
| |
|
| |
| FROM sys.dm_db_partition_stats ps
| |
| GROUP BY
| |
| ps.object_id
| |
| )
| |
| SELECT
| |
| sch.name AS schema_name,
| |
| tbl.name AS table_name,
| |
|
| |
| T.row_count,
| |
|
| |
| /* TABLE */
| |
| CAST(T.table_reserved_pages * 8.0 / 1024
| |
| AS DECIMAL(18,2)) AS table_reserved_mb,
| |
|
| |
| CAST(T.table_used_pages * 8.0 / 1024
| |
| AS DECIMAL(18,2)) AS table_used_mb,
| |
|
| |
| /* INDEX */
| |
| CAST(T.index_reserved_pages * 8.0 / 1024
| |
| AS DECIMAL(18,2)) AS index_reserved_mb,
| |
|
| |
| CAST(T.index_used_pages * 8.0 / 1024
| |
| AS DECIMAL(18,2)) AS index_used_mb,
| |
|
| |
| /* LOB */
| |
| CAST(T.lob_reserved_pages * 8.0 / 1024
| |
| AS DECIMAL(18,2)) AS lob_reserved_mb,
| |
|
| |
| CAST(T.lob_used_pages * 8.0 / 1024
| |
| AS DECIMAL(18,2)) AS lob_used_mb,
| |
|
| |
| /* ROW OVERFLOW */
| |
| CAST(T.row_overflow_reserved_pages * 8.0 / 1024
| |
| AS DECIMAL(18,2)) AS row_overflow_reserved_mb,
| |
|
| |
| CAST(T.row_overflow_used_pages * 8.0 / 1024
| |
| AS DECIMAL(18,2)) AS row_overflow_used_mb,
| |
|
| |
| /* TOTAL RESERVED */
| |
| CAST(
| |
| (
| |
| T.table_reserved_pages
| |
| + T.index_reserved_pages
| |
| + T.lob_reserved_pages
| |
| + T.row_overflow_reserved_pages
| |
| ) * 8.0 / 1024
| |
| AS DECIMAL(18,2)
| |
| ) AS total_reserved_mb,
| |
|
| |
| /* TOTAL USED */
| |
| CAST(
| |
| (
| |
| T.table_used_pages
| |
| + T.index_used_pages
| |
| + T.lob_used_pages
| |
| + T.row_overflow_used_pages
| |
| ) * 8.0 / 1024
| |
| AS DECIMAL(18,2)
| |
| ) AS total_used_mb,
| |
|
| |
| /* GB */
| |
| CAST(
| |
| (
| |
| T.table_reserved_pages
| |
| + T.index_reserved_pages
| |
| + T.lob_reserved_pages
| |
| + T.row_overflow_reserved_pages
| |
| ) * 8.0 / 1024 / 1024
| |
| AS DECIMAL(18,2)
| |
| ) AS total_reserved_gb
| |
|
| |
| FROM T
| |
| JOIN sys.tables tbl
| |
| ON T.object_id = tbl.object_id
| |
| JOIN sys.schemas sch
| |
| ON tbl.schema_id = sch.schema_id
| |
|
| |
| WHERE tbl.is_ms_shipped = 0
| |
|
| |
| ORDER BY
| |
| total_reserved_mb DESC;
| |
| </source>
| |
| *결과는
| |
| <source>
| |
| schema_name table_name row_count table_mb index_mb lob_mb overflow_mb total_mb
| |
| ----------- --------------- ------------ --------- --------- ------- ------------ ---------
| |
| dbo ORDERS 35,000,000 4200.50 1850.20 0.00 0.00 6050.70
| |
| dbo CUSTOMER 8,500,000 850.20 620.30 0.00 0.00 1470.50
| |
| app DOCUMENT 1,200,000 300.10 150.50 850.20 10.30 1311.10
| |
| app LOG_DATA 50,000,000 980.20 300.50 0.00 450.80 1731.50
| |
| </source>
| |
| <source>
| |
| TABLE 300 MB
| |
| INDEX 150 MB
| |
| LOB 850 MB
| |
| ROW_OVERFLOW 10 MB
| |
| -------------------
| |
| TOTAL 1311 MB
| |
| </source>
| |
|
| |
|
| == [[테이블/인덱스 사이즈 조회 ]] == | | == [[테이블/인덱스 사이즈 조회 ]] == |
| 177번째 줄: |
9번째 줄: |
|
| |
|
| ==[[실행 계획]]== | | ==[[실행 계획]]== |
| * SSMS에서 쿼리 → 실제 실행 계획 포함 켜기. (Ctrl + M)
| |
| <source lang=sql>
| |
| SET STATISTICS IO ON;
| |
| SET STATISTICS TIME ON;
| |
|
| |
| SELECT *
| |
| FROM dbo.EMP
| |
| WHERE EMPNO = 1000;
| |
|
| |
| </source>
| |
| - 실행하면 결과 창 외에 실행 계획(Execution Plan) 탭이 나타납니다.
| |
|
| |
| <source lang=sql>
| |
| SELECT
| |
| └─ Hash Match
| |
| ├─ Index Scan
| |
| └─ Index Scan
| |
| </source>
| |
| * Index Seek => oracle의 index range/unique scan
| |
|
| |
| * Actual Plan 에서 실제 row 확인하기
| |
| ** SQL Server에서도 Cardinality Estimation 문제가 있는지 보는 것이 중요.
| |
| <source lang=sql>
| |
| Estimated Number of Rows = 10
| |
| Actual Number of Rows = 1,500,000
| |
| </source>
| |
|
| |
| * SSMS 실행계획에서 우클릭하여: Save Execution Plan As... > .sqlplan파일로 저장
| |
| * oracle DBMS_XPLAN 과 비슷
| |
| <source lang=xml>
| |
| <ShowPlanXML ...>
| |
| ...
| |
| </ShowPlanXML>
| |
| </source>
| |
|
| |
| === I/O를 더 자세하게 확인 ===
| |
| <source lang=sql>
| |
| SET NOCOUNT ON;
| |
|
| |
| SET STATISTICS IO ON;
| |
| SET STATISTICS TIME ON;
| |
|
| |
| SELECT O.ORDER_ID,
| |
| O.CUSTOMER_ID,
| |
| O.ORDER_DATE,
| |
| O.AMOUNT
| |
| FROM dbo.ORDERS O
| |
| WHERE O.CUSTOMER_ID = 100
| |
| AND O.ORDER_DATE >= '2026-01-01';
| |
|
| |
| SET STATISTICS IO OFF;
| |
| SET STATISTICS TIME OFF;
| |
| </source>
| |
| <source lang=sql>
| |
| Table 'ORDERS'.
| |
| Scan count 1,
| |
| logical reads 23500,
| |
| physical reads 0,
| |
| read-ahead reads 0
| |
| </source>
| |
| <source lang=sql>
| |
| SQL Server Execution Times:
| |
| CPU time = 850 ms,
| |
| elapsed time = 920 ms.
| |
| </source>
| |
|
| |
| ==== SET STATISTICS IO에서 특히 중요한 항목 ====
| |
| <source>
| |
| Table 'ORDERS'.
| |
|
| |
| Scan count 1,
| |
| logical reads 125000,
| |
| physical reads 0,
| |
| read-ahead reads 120000,
| |
| lob logical reads 0,
| |
| lob physical reads 0,
| |
| lob read-ahead reads 0.
| |
| </source>
| |
|
| |
| * Scan count
| |
| *: 테이블/인덱스에 대한 Scan 횟수입니다.
| |
| *Nested Loop와 같이 반복 접근이 발생하면 눈여겨봐야 합니다.
| |
| * logical reads
| |
| *:-메모리에 있는 데이터 페이지를 읽은 횟수입니다.
| |
| * physical reads
| |
| *: 디스크에서 실제 페이지를 읽은 횟수입니다.
| |
| * SQL Server는 데이터가 버퍼 캐시에 올라가 있을 경우:
| |
| *: physical reads = 0 이어도 logical reads = 1000000 일 수 있습니다.
| |
|
| |
| === 실제 튜닝 템플릿 ===
| |
| <source lang=sql>
| |
| SET NOCOUNT ON;
| |
|
| |
| SET STATISTICS IO ON;
| |
| SET STATISTICS TIME ON;
| |
|
| |
| -- 대상 SQL
| |
| SELECT
| |
| O.ORDER_ID,
| |
| O.CUSTOMER_ID,
| |
| O.ORDER_DATE,
| |
| O.AMOUNT
| |
| FROM dbo.ORDERS O
| |
| WHERE O.CUSTOMER_ID = 100
| |
| AND O.ORDER_DATE >= '2026-01-01';
| |
|
| |
| SET STATISTICS IO OFF;
| |
| SET STATISTICS TIME OFF;
| |
| </source>
| |
| === Oracle 과 비교표 ===
| |
| {| class="wikitable"
| |
| |+ Oracle 과 비교표
| |
| |-
| |
| ! Oracle !! SQL Server
| |
| |-
| |
| |A-Rows || Actual Rows
| |
| |-
| |
| |E-Rows || Estimated Rows
| |
| |-
| |
| |Buffers || logical reads
| |
| |-
| |
| |Physical Reads || physical reads
| |
| |-
| |
| |Execution Time || CPU / elapsed
| |
| |-
| |
| |TABLE ACCESS FULL || Table Scan
| |
| |-
| |
| |INDEX RANGE SCAN || Index Seek
| |
| |-
| |
| |INDEX FULL SCAN || Index Scan
| |
| |-
| |
| |HASH JOIN || Hash Match
| |
| |-
| |
| |NESTED LOOPS || Nested Loops
| |
| |-
| |
| |SORT || Sort
| |
| |}
| |
|
| |
| === Query Store ===
| |
| <source>
| |
| 실행 횟수
| |
| CPU Time
| |
| Duration
| |
| Logical Reads
| |
| Physical Reads
| |
| Memory
| |
| Plan 변경
| |
| </source>
| |
|
| |
| <source lang=sql>
| |
| SELECT TOP 20
| |
| qt.query_sql_text,
| |
| rs.execution_count,
| |
| rs.avg_duration,
| |
| rs.avg_cpu_time,
| |
| rs.avg_logical_io_reads
| |
| FROM sys.query_store_query_text qt
| |
| JOIN sys.query_store_query q
| |
| ON qt.query_text_id = q.query_text_id
| |
| JOIN sys.query_store_plan p
| |
| ON q.query_id = p.query_id
| |
| JOIN sys.query_store_runtime_stats rs
| |
| ON p.plan_id = rs.plan_id
| |
| ORDER BY rs.avg_logical_io_reads DESC;
| |
| </source>
| |
|
| |
|
| == [[힌트 사용 방법 ]] == | | == [[힌트 사용 방법 ]] == |
|
| |
|
| == [[ 튜닝 대상 SQL 찾기]] == | | == [[ 튜닝 대상 SQL 찾기]] == |