Liệu việc cập nhật một hàng có cùng giá trị có thực sự cập nhật hàng không?


28

Tôi có một câu hỏi liên quan đến hiệu suất. Hãy nói rằng tôi có một người dùng với tên Michael. Thực hiện truy vấn sau:

UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123

Liệu truy vấn có thực sự thực hiện cập nhật hay không, mặc dù nó đang được cập nhật về cùng một giá trị? Nếu vậy, làm thế nào để tôi ngăn chặn nó xảy ra?


1
Tại sao bạn lại thực thi một câu lệnh và đồng thời mong đợi nó không thực thi?
Max Vernon

@MaxVernon Ruby on Rails 'ORM không cập nhật bản ghi nên tôi tò mò liệu PostgreQuery có làm điều tương tự không.
OneSneakyMofo

1
Tôi muốn đề xuất nếu Ruby on Rails đang làm điều đó, có lẽ nó đang thực hiện chọn trước để xem hàng có cần cập nhật hay không.
Max Vernon

Câu trả lời:


35

Do mô hình MVCC của Postgres và theo quy tắc của SQL, một UPDATEphiên bản hàng mới viết cho mỗi hàng không được loại trừ trong WHEREmệnh đề.

Điều này có tác động ít nhiều đến hiệu suất, trực tiếp và gián tiếp. "Cập nhật trống" có cùng chi phí cho mỗi hàng như mọi bản cập nhật khác. Chúng kích hoạt kích hoạt (nếu có) như bất kỳ bản cập nhật nào khác, chúng phải được ghi nhật ký WAL và chúng tạo ra các hàng chết làm đầy bảng và gây ra nhiều công việc hơn cho VACUUMsau này như bất kỳ bản cập nhật nào khác.

Các mục chỉ mục và các cột TOASTed trong đó không có cột nào liên quan được thay đổi có thể giữ nguyên, nhưng điều đó đúng với bất kỳ hàng cập nhật nào. Liên quan:

Hầu như luôn luôn là một ý tưởng tốt để loại trừ các cập nhật trống như vậy (khi có cơ hội thực tế nó có thể xảy ra). Bạn đã không cung cấp một định nghĩa bảng trong câu hỏi của bạn (luôn luôn là một ý tưởng tốt). Chúng ta phải giả sử first_namecó thể là NULL (điều này sẽ không gây ngạc nhiên cho "tên gọi"), do đó truy vấn phải sử dụng so sánh NULL an toàn :

UPDATE users
SET    first_name = 'Michael'
WHERE  id = 123
AND   first_name IS DISTINCT FROM 'Michael';

Nếu first_name IS NULLtrước khi cập nhật, một bài kiểm tra chỉ first_name <> 'Michael'đánh giá NULL và như vậy sẽ loại trừ hàng khỏi bản cập nhật. Lỗi lén lút. Tuy nhiên, nếu cột được xác địnhNOT NULL , hãy sử dụng kiểm tra đẳng thức đơn giản, vì nó rẻ hơn một chút.

Liên quan:


1
Indexes entries and TOASTed columns where none of the involved columns are changed can stay the sameNhưng họ sẽ không được cập nhật để chỉ đến vị trí mới của hàng chứ?
dvtan

1
@dtgq: Không phải với các bản cập nhật NÓNG, nơi chỉ mục có thể tiếp tục trỏ đến vị trí cũ và các lần tải heap phải đi qua chuỗi HOT để có được bộ dữ liệu trực tiếp. Tôi đã thêm các liên kết để giải thích thêm ở trên.
Erwin Brandstetter

1
Điều gì về MVCC kêu gọi cập nhật noop để viết một bộ dữ liệu mới?
jberryman

@jberryman: Không chắc tôi hiểu. Dù bằng cách nào, xin vui lòng đặt câu hỏi của bạn như câu hỏi mới . Bạn luôn có thể liên kết đến cái này cho bối cảnh. Và bạn có thể để lại một bình luận ở đây để liên kết lại (và thu hút sự chú ý của tôi).
Erwin Brandstetter

2
@jberryman: Tôi thực sự không biết lý do tại sao dự án lại đi theo hướng này. Điều đó đã được thiết lập từ lâu. Nhưng tôi cho rằng sẽ rất tốn kém khi kiểm tra mọi hàng cho sự bằng nhau và có một đường dẫn mã riêng cho các hàng không thay đổi. Việc xử lý ID giao dịch sẽ phức tạp hơn - vỏ đặc biệt rollback, xử lý ảnh chụp nhanh, quản lý khóa, WAL, một thứ không ...
Erwin Brandstetter

4

ORM giống như đề nghị hoãn lại của Ruby on Rail, đánh dấu một bản ghi là đã thay đổi (hoặc không) và sau đó khi cần hoặc được gọi, sau đó gửi thay đổi tới cơ sở dữ liệu.

PostgreSQL là một cơ sở dữ liệu chứ không phải ORM. Nó sẽ giảm hiệu suất nếu mất thời gian để kiểm tra xem giá trị mới có giống với giá trị được cập nhật trong truy vấn của bạn không.

Do đó, nó sẽ cập nhật giá trị bất kể nó có giống với giá trị mới hay không.

Nếu bạn muốn ngăn chặn điều này, bạn có thể sử dụng mã như Max Vernon đề xuất trong câu trả lời của anh ấy.


2

Bạn chỉ có thể thêm vào wheremệnh đề:

UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123
    AND (first_name <> 'Michael' OR first_name IS NULL);

Nếu first_nameđược định nghĩa là NOT NULL, OR first_name IS NULLphần có thể được gỡ bỏ.

Điều kiện:

(first_name <> 'Michael' OR first_name IS NULL)

cũng có thể được viết thanh lịch hơn như (trong câu trả lời của Erwin):

first_name IS DISTINCT FROM 'Michael'

Không biết liệu cột có thể là NULL hay không, điều đó có thể gây ra lỗi lén lút.
Erwin Brandstetter

1
@ErwinBrandstetter Tôi đang cập nhật câu trả lời - sau đó tôi thấy bình luận và câu trả lời của bạn!
ypercubeᵀᴹ

cảm ơn vì đã chỉnh sửa, @ypercube - và cho nhận xét về NULL@erwin
Max Vernon

1

Từ quan điểm cơ sở dữ liệu

Câu trả lời cho câu hỏi của bạn là có. Việc cập nhật sẽ diễn ra. Cơ sở dữ liệu không kiểm tra giá trị trước đó, nó chỉ đặt giá trị mới.

Vì điều này xảy ra trong bộ nhớ (và sẽ chỉ được ghi vào các tệp dữ liệu sau khi cam kết được ban hành), hiệu suất sẽ không thành vấn đề.

Từ góc độ ORM

Thông thường bạn sẽ có một Object đại diện cho một hàng cơ sở dữ liệu (nó có thể phức tạp hơn thế nhiều, nhưng hãy giữ cho nó đơn giản). Đối tượng này được quản lý trong bộ nhớ (ở cấp máy chủ ứng dụng) và chỉ phiên bản được cam kết mới nhất của đối tượng đó mới thực sự đưa nó vào cơ sở dữ liệu tại một điểm nhất định.

Điều đó có thể giải thích các hành vi khác nhau.

Bây giờ, chúng ta đừng so sánh một con tàu chở hàng với máy in 3D. Việc bạn có thể gửi máy in 3D bằng tàu chở hàng không có nghĩa là có thể có bất kỳ loại so sánh nào giữa chúng.

Thưởng thức!

Tôi hy vọng điều này làm rõ một số khái niệm.


4
Hiệu suất và vấn đề. Mỗi bản cập nhật phải được ghi trên đĩa (nhật ký và bảng).
ypercubeᵀᴹ

Nó sẽ phụ thuộc vào RDBMS thực tế mà bạn sử dụng. Nhưng hầu hết trong số họ không cam kết mỗi bản cập nhật duy nhất, mà chỉ có khối cam kết cuối cùng họ có trong bộ nhớ. Bạn không bao giờ đọc hoặc viết một hàng trong cơ sở dữ liệu. Bạn đọc / ghi các khối và giữ chúng trong bộ nhớ cho đến khi bạn phải xóa nó ra để đặt một khối mới vào cùng một vị trí. Trong bộ nhớ, không phải mọi thay đổi trong một hàng sẽ được ghi vào đĩa, mà chỉ các nội dung khối khi quá trình "trình ghi cơ sở dữ liệu" được báo hiệu để chuyển khối bộ nhớ đó vào một tệp dữ liệu. Vì vậy, không ... Không phải là vấn đề trừ khi ứng dụng của bạn giữ khối không được chấp nhận quá lâu.
Silvarion

1
câu hỏi là về Postgres, không phải về bất kỳ DBMS tùy ý nào. Và trong khi tất cả các bản cập nhật không phải được viết từng cái một, mỗi lần ghi trên cơ sở dữ liệu phải được ghi vào nhật ký. Nếu một thay đổi không được ghi trên bộ lưu trữ liên tục, DBMS sẽ tồn tại như thế nào khi gặp sự cố hệ thống?
ypercubeᵀᴹ

Vâng, nó ghi vào nhật ký, từ bộ nhớ cũng trong các điểm kiểm tra. Trừ khi bạn có một số lượng lớn người dùng đồng thời, thì đó không phải là vấn đề. Nhật ký được viết theo lô là tốt. Tôi nghĩ rằng chúng ta đang nói về máy chủ. Nếu bạn đang nói về cơ sở dữ liệu Postgres trong máy tính xách tay có ổ cứng 5400RPM, vâng ... bạn sẽ luôn gặp vấn đề về hiệu năng. Vì vậy, câu trả lời cuối cùng sẽ là câu đầu tiên ... Nó phụ thuộc vào quá nhiều thứ.
Silvarion
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.