Mạng

Một request HTTPS mất bao lâu: DNS, TCP, TLS và việc dùng lại kết nối

Tách một request HTTPS thành DNS, TCP, TLS và chờ phản hồi, đo từng pha bằng curl và Python, rồi tính cái giá của việc mở kết nối mới cho mỗi lời gọi.

Mục lục
  1. 1. Một request HTTPS gồm những pha nào
  2. 2. Đo từng pha bằng curl
  3. 3. Kết nối mới và kết nối dùng lại
  4. 4. Tính cho BanHang: lời gọi cổng thanh toán
  5. 5. HttpClient mới cho mỗi request
  6. 6. Khi request chậm: thời gian nằm ở đâu
  7. Những chỗ hay hiểu sai
  8. Đọc tiếp
  9. Nguồn

Khách bấm "Thanh toán" trên app BanHang. App gửi request tới API, API gọi cổng thanh toán bên ngoài qua HTTPS. Trước khi cổng thanh toán đọc được byte đầu tiên của yêu cầu, hai bên đã đi về vài lượt chỉ để mở kết nối. Bài này tách một request HTTPS thành từng pha, đo từng pha trên một máy ở Việt Nam, rồi tính xem dùng lại kết nối tiết kiệm bao nhiêu cho lời gọi cổng thanh toán.

Đọc nhanh

  • Kết nối mới qua TCP và TLS 1.3 tốn ít nhất 3 RTT trước byte đầu tiên của phản hồi: TCP 1, TLS 1, request 1. TLS 1.2 thêm 1 RTT, DNS thêm khi cache lạnh. Kết nối dùng lại chỉ tốn 1 RTT.
  • Trên máy thử, RTT khoảng 50 ms: 20 request, mỗi request một kết nối mới, mất 4,4 s. Trên một kết nối dùng lại, cùng 20 request mất 1,5 s.
  • Các mốc -w của curl tách được mạng và server: lấy thời gian từ lúc xong TLS đến byte đầu tiên, trừ một RTT, là thời gian nằm phía sau server.
  • Trong .NET, không tạo HttpClient cho mỗi request. Dùng IHttpClientFactory hoặc một HttpClient sống lâu với PooledConnectionLifetime.

Máy thử

Laptop Windows 11 ở Việt Nam, Wi-Fi, resolver 8.8.8.8, đo ngày 2026-10-01. curl.exe 8.21.0 đi kèm Windows, TLS qua Schannel, không có HTTP/2 và HTTP/3, nên mọi phép đo curl là HTTP/1.1. Python 3.14.3 với OpenSSL 3.0.18. .NET SDK 10.0.401. Cloudflare phục vụ docs.gamevibe.win cho máy này từ điểm HKG. Số đo qua Wi-Fi dao động mạnh giữa các lần chạy và đổi theo vị trí của bạn.

1. Một request HTTPS gồm những pha nào

RTT (round-trip time) là thời gian một gói đi từ client tới server và nhận trả lời. Mỗi pha của một request trên kết nối mới tốn một số RTT nguyên, cộng thời gian tính toán ở hai đầu.

  1. DNS. Đổi tên miền thành địa chỉ IP. Tên đã nằm trong cache của hệ điều hành thì gần như miễn phí. Nếu chưa, client hỏi resolver, và resolver có thể phải hỏi tiếp máy chủ có thẩm quyền.
  2. TCP. Client gửi SYN, server trả SYN-ACK. Sau 1 RTT client gửi được dữ liệu.
  3. TLS. Ở TLS 1.3, client gửi ClientHello kèm khóa tạm. Server trả toàn bộ phần của mình, từ ServerHello đến Finished, trong một lượt (RFC 8446, mục 2). Client gửi được dữ liệu sau 1 RTT. RFC 7918 ghi rằng bắt tay đầy đủ ở các bản đến TLS 1.2 cần hai vòng, bốn chặng, trước khi gửi được dữ liệu ứng dụng.
  4. Request và response. Ít nhất 1 RTT cộng thời gian server xử lý. Phản hồi lớn tốn thêm vài RTT vì TCP mở rộng cửa sổ gửi dần dần.
kết nối mới, TLS 1.3 ≈ 1 RTT (TCP) + 1 RTT (TLS) + 1 RTT (request) + thời gian server = 3 RTT + thời gian server
kết nối dùng lại     ≈ 1 RTT (request) + thời gian server

HTTP/3 chạy trên QUIC. QUIC gộp bắt tay vận chuyển và bắt tay mật mã làm một (RFC 9000, mục 7), nên kết nối mới còn 2 RTT trước byte đầu tiên. Cách RFC 9114 (mục 3.1.1) mô tả để client biết server có HTTP/3 là header Alt-Svc trong một phản hồi trước đó. Vì vậy lần kết nối đầu tiên tới một server thường vẫn đi qua TCP.

TLS 1.3 còn có 0-RTT: khi client đã có khóa chung (PSK), thường lấy từ phiên trước, nó gửi dữ liệu ngay trong lượt đầu tiên.

0-RTT không dành cho lệnh thanh toán

RFC 8446 (mục 2.3) ghi rõ dữ liệu 0-RTT không có forward secrecy và không được đảm bảo chống phát lại giữa các kết nối. Ai chép được gói đó có thể gửi lại nguyên văn. Khi không có thông tin gì khác, RFC 8470 (mục 4) cấm client HTTP gửi method không an toàn như POST trong early data, và server có thể từ chối bằng mã 425 Too Early (mục 5.2). Lệnh trừ tiền luôn chờ bắt tay xong, và vẫn cần idempotency key.

2. Đo từng pha bằng curl

Tùy chọn -w của curl in các mốc thời gian, tính bằng giây, cộng dồn từ lúc bắt đầu: time_namelookup (xong DNS), time_connect (xong TCP), time_appconnect (xong TLS), time_starttransfer (byte đầu tiên của phản hồi tới), time_total (xong toàn bộ). Hiệu giữa hai mốc liền nhau là độ dài một pha.

Script PowerShell dưới đây đo ba URL, mỗi URL 15 lần, in trung vị. gamevibe là trang chủ blog này. wiki-cache là trang chủ Wikipedia, trả từ cache ở edge. wiki-api là một lời gọi API mà edge không cache (header x-cache-status: pass), nên phải chuyển tiếp về hệ thống phía sau. Trên Linux, một lần đo tương đương là curl -s -o /dev/null -w '...' URL.

$fmt  = "%{time_namelookup} %{time_connect} %{time_appconnect} %{time_starttransfer} %{time_total}\n"
$urls = [ordered]@{
    "gamevibe"   = "https://docs.gamevibe.win/"
    "wiki-cache" = "https://www.wikipedia.org/"
    "wiki-api"   = "https://vi.wikipedia.org/w/api.php?action=query&meta=siteinfo&format=json"
}

function Median($xs) { ($xs | Sort-Object)[[int](($xs.Count - 1) / 2)] }

foreach ($ten in $urls.Keys) {
    $r = 1..15 | ForEach-Object {
        $t = (curl.exe -s -o NUL -w $fmt $urls[$ten]) -split " " | ForEach-Object { 1000 * [double]$_ }
        [pscustomobject]@{ dns = $t[0]; tcp = $t[1] - $t[0]; tls = $t[2] - $t[1]; cho = $t[3] - $t[2]; tong = $t[4] }
    }
    "{0,-10}  dns {1,4:N0}  tcp {2,4:N0}  tls {3,4:N0}  cho {4,4:N0}  tong {5,4:N0}  (ms, trung vi 15 lan)" -f $ten,
        (Median $r.dns), (Median $r.tcp), (Median $r.tls), (Median $r.cho), (Median $r.tong)
}
gamevibe    dns   21  tcp   49  tls  112  cho   95  tong  298  (ms, trung vi 15 lan)
wiki-cache  dns   28  tcp   59  tls  135  cho   78  tong  434  (ms, trung vi 15 lan)
wiki-api    dns   37  tcp   57  tls  140  cho  287  tong  530  (ms, trung vi 15 lan)
  • tcp gần bằng RTT: 49–59 ms. ping docs.gamevibe.win cho trung bình 47 ms.
  • tls là 112–140 ms, khoảng 2,3–2,5 RTT. Cùng server docs.gamevibe.win, Python với OpenSSL đo được 75 ms (mục 3). Đường truyền và server như nhau, chỉ thư viện TLS phía client khác, nên phần chênh nằm ở phía client. Dùng curl để so các server với nhau, không dùng con số tuyệt đối của nó để kết luận về TLS.
  • cho là một RTT cộng thời gian phía sau server. wiki-cache: 78 − 59 = 19 ms. wiki-api: 287 − 57 = 230 ms. Hai URL đi qua cùng máy cache cp5024 (header x-cache), TCP và TLS như nhau. Khoảng 210 ms chênh lệch nằm hoàn toàn sau edge, nơi request API chờ hệ thống ứng dụng trả lời.
  • tong trừ bốn cột trước là thời gian tải thân phản hồi: 21 ms cho trang blog 15 KB, 134 ms cho trang Wikipedia 94 KB. 94 KB không đi hết trong một lượt TCP.

Hai công thức dùng khi điều tra:

RTT                    ≈ time_connect − time_namelookup
thời gian phía server  ≈ (time_starttransfer − time_appconnect) − RTT

DNS cần phép thử riêng. Đoạn dưới gọi 11 tên miền Wikipedia chưa từng phân giải trên máy, mỗi tên hai lần liền nhau:

$lan1 = @(); $lan2 = @()
foreach ($l in "fi", "da", "cs", "hu", "ro", "tr", "id", "th", "uk", "el", "ms") {
    $u = "https://$l.wikipedia.org/"
    $lan1 += 1000 * [double](curl.exe -s -o NUL -w "%{time_namelookup}" $u)  # ten chua co trong cache
    $lan2 += 1000 * [double](curl.exe -s -o NUL -w "%{time_namelookup}" $u)  # ngay sau do
}
"DNS lan 1: trung vi {0,4:N0} ms" -f ($lan1 | Sort-Object)[5]
"DNS lan 2: trung vi {0,4:N0} ms" -f ($lan2 | Sort-Object)[5]
DNS lan 1: trung vi  118 ms
DNS lan 2: trung vi   21 ms

Lần 1, Windows phải hỏi 8.8.8.8 (ping 43 ms). Lần 2 lấy từ cache. 21 ms còn lại là chi phí riêng của curl.exe trên máy này: socket.getaddrinfo của Python cho tên đã cache chỉ mất 1,2–1,3 ms. Thêm --resolve docs.gamevibe.win:443:<IP> thì time_namelookup xuống dưới 0,1 ms.

3. Kết nối mới và kết nối dùng lại

Script dưới đây làm hai việc. Phần 1 chỉ mở kết nối, đo riêng TCP và bắt tay TLS 1.3, TLS 1.2. Phần 2 gửi 20 request GET / tuần tự, hoặc đóng kết nối sau mỗi request, hoặc giữ một kết nối cho cả 20. HTTP/1.1 mặc định giữ kết nối mở giữa các request (RFC 9112, mục 9.3).

import http.client
import socket
import ssl
import statistics
import time

HOST, N = "docs.gamevibe.win", 20
DIA_CHI = socket.getaddrinfo(HOST, 443, socket.AF_INET, socket.SOCK_STREAM)[0][4]

def ms(t0):
    return (time.perf_counter() - t0) * 1000

# Phần 1: chỉ mở kết nối, TLS 1.3 rồi TLS 1.2, mỗi loại 15 lần
for v in (ssl.TLSVersion.TLSv1_3, ssl.TLSVersion.TLSv1_2):
    ctx = ssl.create_default_context()
    ctx.minimum_version = ctx.maximum_version = v
    tcp, tls = [], []
    for _ in range(15):
        t0 = time.perf_counter()
        sock = socket.create_connection(DIA_CHI)
        tcp.append(ms(t0))
        t0 = time.perf_counter()
        ctx.wrap_socket(sock, server_hostname=HOST).close()  # trả về khi bắt tay xong
        tls.append(ms(t0))
    print(f"{v.name}: tcp {statistics.median(tcp):5.1f} ms, tls {statistics.median(tls):6.1f} ms")

# Phần 2: 20 request GET / tuần tự, đóng kết nối sau mỗi request hoặc giữ lại
CTX = ssl.create_default_context()

def chay(dung_lai):
    t0 = time.perf_counter()
    conn = http.client.HTTPSConnection(HOST, context=CTX)
    for _ in range(N):
        conn.request("GET", "/")  # sau close(), http.client tự mở kết nối mới
        conn.getresponse().read()  # đọc hết body mới gửi tiếp được
        if not dung_lai:
            conn.close()
    conn.close()
    return ms(t0)

do = {False: [], True: []}
for _ in range(5):  # xen kẽ hai cách, 5 vòng, lấy trung vị
    for dung_lai in (False, True):
        do[dung_lai].append(chay(dung_lai))
for dung_lai, ten in ((False, "kết nối mới mỗi lần"), (True, "một kết nối dùng lại")):
    t = statistics.median(do[dung_lai])
    print(f"{N} request, {ten}: {t:5.0f} ms ({t / N:5.1f} ms/request)")
TLSv1_3: tcp  49.0 ms, tls   75.0 ms
TLSv1_2: tcp  50.0 ms, tls  127.3 ms
20 request, kết nối mới mỗi lần:  4431 ms (221.6 ms/request)
20 request, một kết nối dùng lại:  1528 ms ( 76.4 ms/request)

TLS 1.2 chậm hơn TLS 1.3 khoảng 52 ms, xấp xỉ một RTT, đúng lượt đi về mà TLS 1.3 bỏ đi. Cả hai đều vượt số RTT nguyên khoảng 26–27 ms. Đó là thời gian tính toán ở hai đầu (trao đổi khóa, chữ ký, xác minh chuỗi chứng chỉ) cộng độ dao động của Wi-Fi.

Request trên kết nối dùng lại mất 76,4 ms: một RTT, thời gian edge trả trang, và thời gian tải 15 KB. Request trên kết nối mới mất thêm 145,2 ms. TCP và TLS 1.3 chiếm 49,0 + 75,0 = 124 ms trong đó. Phần còn lại gồm DNS từ cache, việc dựng và đóng socket, và dao động giữa các lần đo. Qua bốn lần chạy script, mức tiết kiệm mỗi request là 134–162 ms và TLS 1.2 chậm hơn TLS 1.3 từ 52 đến 68 ms.

4. Tính cho BanHang: lời gọi cổng thanh toán

Giả định API BanHang chạy ở Hà Nội, cổng thanh toán đặt server ở Singapore. RTT 30 ms giữa hai nơi là giá trị ví dụ, không phải số đo. Thời gian xử lý của cổng như nhau ở mọi trường hợp nên để riêng ra.

kết nối mới, TLS 1.3:  TCP 1 + TLS 1 + request 1 = 3 RTT = 3 × 30 = 90 ms
kết nối mới, TLS 1.2:  TCP 1 + TLS 2 + request 1 = 4 RTT = 4 × 30 = 120 ms
kết nối dùng lại:                       request 1 = 1 RTT = 30 ms

Dùng lại kết nối bớt 60 ms mỗi lời gọi so với TLS 1.3, 90 ms so với TLS 1.2. Khách thấy trực tiếp con số này ở màn hình thanh toán.

Lúc cao điểm API nhận khoảng 300 request mỗi giây. Lấy trần trên: mỗi request gọi ra ngoài qua HTTPS một lần. Thực tế lời gọi cổng thanh toán ít hơn nhiều, vì BanHang có khoảng 13.000 đơn mỗi ngày. Ước lượng khi mở kết nối mới cho mỗi lời gọi:

  • 300 lần bắt tay TCP và TLS mỗi giây, ở cả API lẫn cổng thanh toán. Với pool dùng lại, số lần bắt tay chỉ bằng số kết nối trong pool, mỗi PooledConnectionLifetime một lần.
  • Theo định luật Little, số việc đang dở bằng tốc độ đến nhân thời gian mỗi việc: lúc nào cũng có 300 × 0,06 s = 18 lời gọi chỉ đứng chờ bắt tay.
  • Bên chủ động đóng kết nối giữ socket ở trạng thái TIME_WAIT thêm một lúc. Trên máy thử là khoảng 120 giây (mục 5), và dải cổng động của Windows có 16.384 cổng (netsh int ipv4 show dynamicport tcp). 300 kết nối mỗi giây cần giữ 300 × 120 = 36.000 cổng cùng lúc, hơn gấp đôi số cổng có. Tới một địa chỉ IP của cổng thanh toán, cổng nguồn cạn sau khoảng 16.384 / 300 ≈ 55 giây. Khi hết cổng, lời gọi mới lỗi ngay ở bước mở kết nối.

Chặng từ app di động tới API khó giữ kết nối lâu như vậy: khi máy đổi từ Wi-Fi sang 4G, địa chỉ IP đổi và kết nối TCP cũ không dùng được nữa. Mỗi lần mở lại tốn DNS cộng 3 RTT, và HTTP/3 bớt được 1 RTT ở đây.

5. HttpClient mới cho mỗi request

Tài liệu .NET nêu cơ chế: mỗi HttpClient tự tạo handler thì có connection pool riêng, request tới cùng server qua handler khác phải mở kết nối mới, và cổng TCP không được trả lại ngay khi kết nối đóng. Đoạn dưới đo lỗi đó. Lưu thành KetNoi.cs, chạy bằng dotnet run KetNoi.cs (file-based app, cần .NET SDK 10).

using System.Diagnostics;

const string Url = "https://docs.gamevibe.win/";
const int N = 20;

using (var mo = new HttpClient()) await mo.GetStringAsync(Url); // làm nóng JIT, không tính giờ

var sw = Stopwatch.StartNew();
for (int i = 0; i < N; i++)
{
    using var client = new HttpClient();        // SAI: mỗi HttpClient một connection pool riêng
    using var res = await client.GetAsync(Url);
}
Console.WriteLine($"HttpClient mới mỗi request: {sw.ElapsedMilliseconds,5} ms");

var dungChung = new HttpClient(new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.FromMinutes(2) });
sw.Restart();
for (int i = 0; i < N; i++) { using var res = await dungChung.GetAsync(Url); }
Console.WriteLine($"Một HttpClient dùng chung:  {sw.ElapsedMilliseconds,5} ms");

Chạy, rồi đếm socket TIME_WAIT tới địa chỉ của docs.gamevibe.win. Trước khi chạy, số này là 0.

$ips = (Resolve-DnsName docs.gamevibe.win -Type A).IPAddress
dotnet run KetNoi.cs
@(Get-NetTCPConnection -State TimeWait -RemotePort 443 | Where-Object { $ips -contains $_.RemoteAddress }).Count
HttpClient mới mỗi request:  4281 ms
Một HttpClient dùng chung:   1542 ms
21

21 socket ứng với 21 kết nối đã đóng: 20 trong vòng lặp sai, 1 của lần làm nóng. Vòng dùng chung không đóng kết nối nào. Ở một lần chạy khác, đếm lại mỗi 10 giây, 21 socket biến mất sau khoảng 120 giây. Qua bảy lần chạy, cách sai mất 4,3–10,1 s, cách đúng 1,4–3,2 s.

BanHang đăng ký client cho cổng thanh toán một lần, theo cách tài liệu IHttpClientFactory hướng dẫn khi dùng chung với SocketsHttpHandler. Đoạn dưới trích từ Program.cs, đã biên dịch và chạy trong một app thử với .NET SDK 10.0.401.

builder.Services.AddHttpClient<CongThanhToanClient>(c =>
    {
        c.BaseAddress = new Uri("https://cong-thanh-toan.example/");
        c.Timeout = TimeSpan.FromSeconds(10);
    })
    .ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
    {
        PooledConnectionLifetime = TimeSpan.FromMinutes(2) // đóng kết nối sau 2 phút để lần sau phân giải DNS lại
    })
    .SetHandlerLifetime(Timeout.InfiniteTimeSpan);         // việc thay kết nối đã do PooledConnectionLifetime lo

public sealed class CongThanhToanClient(HttpClient http)
{
    public Task<HttpResponseMessage> TaoGiaoDichAsync(object yeuCau, CancellationToken ct) =>
        http.PostAsJsonAsync("v1/giao-dich", yeuCau, ct);
}

HttpClient chỉ phân giải DNS khi tạo kết nối và không theo TTL của bản ghi. Không có PooledConnectionLifetime, một kết nối sống mãi sẽ bám IP cũ khi cổng thanh toán đổi IP. Mốc 2 phút là giá trị ví dụ của tài liệu Microsoft, nên chọn theo tần suất DNS đổi. Tài liệu cũng cảnh báo không giữ typed client trong singleton, trừ khi handler là SocketsHttpHandler có PooledConnectionLifetime như trên.

6. Khi request chậm: thời gian nằm ở đâu

DNS. Resolve-DnsName docs.gamevibe.win cho TTL 300 giây. Mỗi lần bản ghi hết hạn, request kế tiếp trả thêm một lần hỏi resolver, ở máy thử khoảng 118 ms. Resolver xa hoặc TTL vài giây biến chi phí đó thành thường xuyên.

Chuỗi chứng chỉ. Server gửi chuỗi chứng chỉ trong lượt TLS đầu tiên. Từ Python 3.13 đo được cỡ chuỗi:

import socket
import ssl

ctx = ssl.create_default_context()
for host in ["docs.gamevibe.win", "github.com", "www.microsoft.com"]:
    with ctx.wrap_socket(socket.create_connection((host, 443)), server_hostname=host) as s:
        chuoi = s.get_unverified_chain()  # Python 3.13+: các chứng chỉ server gửi, dạng DER
        print(f"{host}: {len(chuoi)} chứng chỉ, {sum(map(len, chuoi))} byte")
docs.gamevibe.win: 3 chứng chỉ, 2492 byte
github.com: 3 chứng chỉ, 2718 byte
www.microsoft.com: 3 chứng chỉ, 5879 byte

Với TCP, cả ba chuỗi nằm gọn trong cửa sổ gửi ban đầu mà RFC 6928 cho phép, tối đa min(10 × MSS, max(2 × MSS, 14.600 byte)). Chuỗi vượt mức đó buộc server chờ ACK, tốn thêm một RTT. QUIC chặt hơn: trước khi xác thực địa chỉ client, server chỉ được gửi tối đa gấp ba số byte đã nhận (RFC 9000, mục 8.1). Gói Initial của client tối thiểu 1.200 byte (mục 14.1). Nếu client chỉ gửi một gói như vậy, server có khoảng 3 × 1.200 = 3.600 byte cho lượt đầu, và chuỗi 5.879 byte phải chờ client gửi thêm.

Nagle và delayed ACK. Nagle (RFC 1122, mục 4.2.3.4) giữ gói nhỏ lại khi còn dữ liệu chưa được ACK. Đầu kia được hoãn ACK tối đa 0,5 giây (mục 4.2.3.2). Hai cơ chế gặp nhau thì request nhỏ có thể chờ vô ích. Cả hai client trong bài đều tắt Nagle: http.client đặt TCP_NODELAY trong HTTPConnection.connect, SocketsHttpHandler tạo socket với NoDelay = true trong ConnectToTcpHostAsync. Code tự viết trên socket thô thì cần kiểm tra.

Danh sách kiểm tra khi lời gọi cổng thanh toán chậm:

  1. Chạy curl -w 10–15 lần từ chính máy API, lấy trung vị. So tcp với ping: cả hai lớn thì vấn đề nằm ở đường mạng hoặc khoảng cách.
  2. dns vẫn lớn ở các lần lặp lại thì cache không giữ được tên: TTL quá ngắn hoặc resolver chậm.
  3. tls vượt 2 RTT thì xem phiên bản TLS, cỡ chuỗi chứng chỉ, CPU ở hai đầu.
  4. cho trừ RTT lớn thì gửi con số đó, kèm thời điểm đo, cho bên vận hành cổng thanh toán.
  5. Đếm socket TIME_WAIT tới địa chỉ cổng thanh toán (Get-NetTCPConnection trên Windows, ss -tan state time-wait trên Linux). Con số tăng theo lưu lượng nghĩa là kết nối không được dùng lại.

Những chỗ hay hiểu sai

  • "HTTPS chậm vì mã hóa tốn CPU." Trên máy thử, bắt tay TLS 1.3 chỉ dài hơn một RTT khoảng 26 ms. Phần lớn chi phí là các lượt đi về, và dùng lại kết nối bỏ được chúng.
  • "Dispose HttpClient sau mỗi request là dọn dẹp đúng cách." Dispose đóng luôn connection pool. Đoạn đo ở mục 5 để lại 21 socket TIME_WAIT, mỗi socket giữ một cổng khoảng 2 phút.
  • "Một HttpClient static là đủ." Nó không phân giải DNS lại khi kết nối còn sống. Cần đặt PooledConnectionLifetime.
  • "time_starttransfer cao nghĩa là server chậm." Mốc đó cộng dồn cả DNS, TCP và TLS. Phải trừ time_appconnect và một RTT.

Đọc tiếp

Nguồn

Đọc tiếp

Trong SQL Server

Đọc kế hoạch thực thi

Câu SQL thành kế hoạch thực thi ra sao, lấy kế hoạch ước lượng và thực tế ở đâu, đọc thuộc tính nào trước, và vì sao cùng một thủ tục lúc nhanh lúc chậm.

53 phút đọc