2011-01-20 30 views
11

cách nhanh nhất để làm điều này là gì:Cách nhanh nhất để chèn vào bảng SQL Server từ mã .NET?

  • Một bảng, không có tài liệu tham khảo mà tôi không thể điền trước (tức là có một chìa khóa tham khảo ở đó, nhưng tôi có tất cả các dữ liệu điền vào)
  • LOTS dữ liệu . Chúng tôi nói về hàng trăm triệu hàng mỗi ngày, đến bằng động qua API
  • Yêu cầu phải/được xử lý ngay khi có thể trong kịch bản gần thời gian thực (tức là không viết lên tệp để tải lên một tệp mỗi ngày) . 2 giây là sự chậm trễ tối đa bình thường
  • máy riêng biệt cho dữ liệu/ứng dụng và SQL Server

Những gì tôi làm bây giờ:

  • tổng hợp lên đến 32 * 1024 hàng vào một mảng, sau đó xếp hàng nó.
  • Đọc hàng đợi trong 2-3 chủ đề. Chèn vào cơ sở dữ liệu bằng SqlBulkCopy.

Tôi nhận được khoảng 60k-75k hàng được nhập mỗi giây, không đủ nhưng khá gần. Tôi rất thích đánh 250.000 hàng.

Hiện tại, không có gì thực sự được sử dụng. Tôi nhận được 20% thời gian "mạng I/O" khối, có một lõi 80% nạp CPU bên. Đĩa được viết ra 7mb-14mb, chủ yếu là nhàn rỗi. Độ dài hàng đợi trung bình trên RAID 10 của 6 raptors là .... 0.25.

Bất kỳ ai cũng có ý tưởng làm thế nào để tăng tốc độ này? Nhanh hơn máy chủ (cho đến nay nó là ảo, 8gb ram, 4 lõi, vật lý đĩa đi qua cho dữ liệu).


Thêm một số giải thích:

  • Đây là một R2 Enterprise SQL Server 2008 trên một máy chủ 2008 R2. máy có 4 lõi, 8gb ram. Tất cả 64 bit. 80% tải trung bình đến từ máy này cho thấy khoảng 20% ​​tải CPU.
  • Bảng đơn giản, không có khóa chính, chỉ là chỉ mục trên tham chiếu quan hệ (tham chiếu công cụ) và dấu thời gian (trong bộ công cụ, vì vậy đây không phải là bắt buộc).
  • Các trường trên bảng là: dấu thời gian, tham chiếu công cụ (không có khóa ngoại lệ bắt buộc), kiểu dữ liệu (char 1, một trong số ký tự cho biết dữ liệu nào được đăng), giá (tăng gấp đôi) và âm lượng (int). Như bạn có thể thấy đây là một bảng RẤT mỏng. Dữ liệu được đề cập là dữ liệu đánh dấu cho các công cụ tài chính.
  • Câu hỏi cũng là về phần cứng, v.v. - chủ yếu là vì tôi không thấy nút cổ chai thực sự. Tôi đang chèn vào nhiều giao dịch và nó mang lại cho tôi một lợi ích, nhưng một lợi ích nhỏ. Đĩa, CPU không hiển thị tải trọng đáng kể, mạng io chờ đợi cao (300ms/giây, 30% tại thời điểm này), nhưng điều này là trên cùng một nền tảng ảo hóa chạy JSUT hai máy chủ và có đủ lõi để chạy tất cả. Tôi khá nhiều đang mở để "mua một máy chủ", nhưng tôi muốn xác định nút cổ chai đầu tiên .... đặc biệt là cho rằng vào cuối ngày tôi không nắm lấy những gì nút cổ chai là. Ghi nhật ký không liên quan - chèn hàng loạt KHÔNG đi vào nhật ký dữ liệu dưới dạng dữ liệu (không có chỉ số nhóm).

Trợ giúp phân vùng theo chiều dọc, ví dụ: một byte (nhỏ xíu) có thể chia vũ trụ công cụ cho ví dụ 16 bảng và do đó tôi thực hiện tối đa 16 lần chèn cùng một lúc?Như thực tế dữ liệu đến từ các sàn giao dịch khác nhau, tôi có thể tạo một phân vùng cho mỗi trao đổi. Đây sẽ là một lĩnh vực phân chia tự nhiên (thực ra là trong nhạc cụ, nhưng tôi có thể sao chép dữ liệu này ở đây).


Một số giải thích rõ hơn: Tốc độ thậm chí còn cao hơn (90k), hiện bị hạn chế rõ ràng bởi IO mạng giữa các máy, có thể là chuyển mạch VM.

Điều tôi làm bây giờ là kết nối trên mỗi hàng 32k, đặt bảng tạm thời, chèn vào với SqlBUlkdCopy, THEN sử dụng câu lệnh ONE sql để sao chép vào bảng chính - giảm thiểu bất kỳ thời gian khóa nào trên bảng chính.

Hầu hết thời gian chờ đợi hiện vẫn đang trên mạng IO. Dường như tôi chạy vào các vấn đề mà VM khôn ngoan. Sẽ chuyển sang phần cứng vật lý trong những tháng tới;)

+0

Địa ngục, bạn có chắc chắn lưu trữ dữ liệu trong DB quan hệ là giải pháp bạn thực sự cần? Bạn không thể lưu trữ dữ liệu lúc đầu trong một số loại tệp nhật ký và khi bạn sẽ phân tích dữ liệu, hãy chạy một số loại quy trình tổng hợp để nó chỉ trích xuất thông tin có liên quan đến cơ sở dữ liệu của bạn? –

+0

Có, nhưng tôi rất thích không. Có rất nhiều thứ đang diễn ra ở đây, và nó cũng là một ví dụ lập trình tốt. Ngoài ra, khi extracing các bản ghi để sử dụng hoạt động tôi có thể phải thực hiện 1-2 tỷ hàng càng sớm càng tốt từ định dạng nhị phân nén vào dữ liệu quan hệ. Chỉ cần buộc để có được các giới hạn ở đây. – TomTom

+0

Điều này đặc biệt đúng bởi vì cuối cùng tôi không thực sự thấy lý do tại sao nó không chèn nhanh hơn. Ngay cả một lõi không được sử dụng hết, đĩa không, và tôi có mạng I/O như điều kiện chờ đợi. Tôi không chuyển nhiều dữ liệu. Đây là một điều nhỏ để tôi suy nghĩ về ...;) và sửa chữa. – TomTom

Trả lời

0

Bạn có thể sử dụng phân vùng ngang không? Xem: http://msdn.microsoft.com/en-us/library/ms178148.aspx & http://msdn.microsoft.com/en-us/library/ms188706.aspx

Bạn cũng có thể muốn nhìn vào câu hỏi này, và có thể thay đổi các mô hình phục hồi: Sql Server 2008 Tuning with large transactions (700k+ rows/transaction)

Một số câu hỏi: gì phiên bản của SQL Server bạn đang sử dụng?

Tại sao một lõi ở mức 80%? Đó có thể là nút cổ chai, vì vậy có lẽ điều đáng để điều tra.

Bạn đang sử dụng hệ điều hành nào và có phải là 64 bit không?

+0

Chỉnh sửa câu hỏi để phản hồi. Lưu ý rằng lõi 80% là mức trung bình được lấy từ tải hệ thống. Máy có 4 lõi và cho thấy trung bình tải CPU 20%. – TomTom

1

Có bất kỳ chỉ mục nào trên bảng mà bạn có thể làm mà không có không? EDIT: hỏi trong khi bạn đang gõ.

Có thể biến giá thành số nguyên và sau đó chia cho 1000 hoặc bất kỳ điều gì trên truy vấn không?

+0

Trên thực tế, có một cơ hội để làm cho giá cả một int và một tinyint cho loại mã hóa - đó là suy nghĩ của. Vì giá không được lập chỉ mục, nên nó có tạo nên sự khác biệt lớn không? Tuy nhiên, hãy thử. – TomTom

+0

Trình khách .NET thực thi ở đâu? – Tim

+0

Cùng một máy, máy ảo riêng biệt. Di chuyển đến phần cứng vật lý sớm - không phải cho perofmrmance, nhưng tôi Havean vấn đề với đồng hồ "di chuyển" trong một VM, đó là xấu nếu dữ liệu bạn nhận được có tăng dần dấu thời gian 25ms và thư viện nhận được nó đánh dấu dữ liệu là "suspicuious" cho timestamp sự chậm trễ. – TomTom

1

Bạn đã thử thêm pk vào bảng chưa? Điều đó có cải thiện tốc độ không?

Ngoài ra còn có cách thiết lập để sử dụng bảng kiểm đếm để nhập dữ liệu csv từ http://www.sqlservercentral.com/articles/T-SQL/62867/ (gần phía dưới, yêu cầu đăng ký miễn phí nhưng đáng giá).

Bạn có thể muốn thử điều đó và kiểm tra hiệu suất của nó ... với bảng kiểm đếm được lập chỉ mục một cách chính xác.

+0

pk chưa được thêm .... sẽ dùng thử. csv là xấu, tôi không muốn ứng dụng có thể thao tác với hệ thống tệp máy chủ. – TomTom

+2

Thường xuyên thêm bất kỳ hình thức chỉ mục hoặc khóa chính nào sẽ làm chậm tốc độ chèn xuống. – andora

3

Nếu bạn quản lý 70 nghìn hàng mỗi giây, bạn sẽ rất may mắn cho đến nay. Nhưng tôi nghi ngờ đó là vì bạn có một lược đồ rất đơn giản.

Tôi không thể tin rằng bạn hỏi về loại tải trên

  • máy chủ ảo
  • mảng đơn
  • đĩa SATA

Mạng lưới và CPU được chia sẻ, IO là bị hạn chế: bạn không thể sử dụng tất cả tài nguyên. Bất kỳ thống kê tải nào bạn thấy đều không hữu ích. Tôi nghi ngờ tải mạng bạn thấy là lưu lượng giữa 2 máy chủ ảo và bạn sẽ trở thành IO bị ràng buộc nếu bạn giải quyết

Trước khi tiếp tục, hãy đọc 10 lessons from 35K tps này. Anh ta không sử dụng hộp ảo.

Đây là những gì tôi muốn làm, giả sử không có SAN và không có khả năng DR nếu bạn muốn tăng lưu lượng.

  • Mua 2 máy chủ phyical lớn, CPU RAM loại irreleveant, tối đa RAM, đi x64 cài đặt
  • Đĩa + điều khiển = cọc nhanh nhất, SCSI nhanh nhất. Hoặc một stonking lớn NAS
  • 1000MB + NIC
  • RAID 10 với 610 đĩa cho một log file cho cơ sở dữ liệu của bạn chỉ
  • Còn lại đĩa RAID 5 hoặc RAID 10 cho file dữ liệu

Để tham khảo, tải cao điểm của chúng tôi là 12 triệu hàng mỗi giờ (16 lõi, 16 GB, SAN, x64) nhưng chúng tôi có độ phức tạp trong tải. Chúng tôi không có năng lực.

+0

Xin lỗi, IO Không bị giới hạn. Đĩa đơn mảng sata không phải là quá ít nếu đĩa là 10k raptors, có 10 đĩa chỉ được sử dụng bởi máy chủ sql VÀ đĩa không bận rộn bởi thông tin của bộ điều khiển đột kích. Đây là một vấn đề mạng - thời gian chờ đợi sclearly chỉ vào mạng IO;) – TomTom

1

Từ các câu trả lời tôi đã đọc ở đây, có vẻ như bạn thực sự gặp sự cố phần cứng thay vì vấn đề về mã. Lý tưởng nhất, bạn sẽ nhận được hiệu suất của bạn tăng bằng cách làm cho có thêm đĩa I/O hoặc băng thông mạng, hoặc bằng cách chạy chương trình trên cùng một máy ảo lưu trữ cơ sở dữ liệu.

Tuy nhiên, tôi muốn chia sẻ ý tưởng rằng chèn tham số bảng thực sự lý tưởng cho việc truyền dữ liệu lớn; mặc dù SqlBulkCopy có vẻ nhanh như vậy nhưng nó ít linh hoạt hơn nhiều.

tôi đã viết một bài viết về chủ đề ở đây: http://www.altdevblogaday.com/2012/05/16/sql-server-high-performance-inserts/

Câu trả lời chung là bạn xấp xỉ muốn tạo ra một loại bảng:

CREATE TYPE item_drop_bulk_table_rev4 AS TABLE (
    item_id BIGINT, 
    monster_class_id INT, 
    zone_id INT, 
    xpos REAL, 
    ypos REAL, 
    kill_time datetime 
) 

Sau đó, bạn tạo một thủ tục lưu trữ để sao chép từ tham số bảng vào bảng thực tế trực tiếp, do đó, có ít bước ở giữa hơn:

CREATE PROCEDURE insert_item_drops_rev4 
    @mytable item_drop_bulk_table_rev4 READONLY 
AS 

INSERT INTO item_drops_rev4 
    (item_id, monster_class_id, zone_id, xpos, ypos, kill_time) 
SELECT 
    item_id, monster_class_id, zone_id, xpos, ypos, kill_time 
FROM 
    @mytable 

Mã SQL Server phía sau trông giống như sau:

DataTable dt = new DataTable(); 
dt.Columns.Add(new DataColumn("item_id", typeof(Int64))); 
dt.Columns.Add(new DataColumn("monster_class_id", typeof(int))); 
dt.Columns.Add(new DataColumn("zone_id", typeof(int))); 
dt.Columns.Add(new DataColumn("xpos", typeof(float))); 
dt.Columns.Add(new DataColumn("ypos", typeof(float))); 
dt.Columns.Add(new DataColumn("timestamp", typeof(DateTime))); 

for (int i = 0; i < MY_INSERT_SIZE; i++) { 
    dt.Rows.Add(new object[] { item_id, monster_class_id, zone_id, xpos, ypos, DateTime.Now }); 
} 

// Now we&#039;re going to do all the work with one connection! 
using (SqlConnection conn = new SqlConnection(my_connection_string)) { 
    conn.Open(); 
    using (SqlCommand cmd = new SqlCommand("insert_item_drops_rev4", conn)) { 
     cmd.CommandType = CommandType.StoredProcedure; 

     // Adding a "structured" parameter allows you to insert tons of data with low overhead 
     SqlParameter param = new SqlParameter("@mytable", SqlDbType.Structured); 
     param.Value = dt; 
     cmd.Parameters.Add(param); 
     cmd.ExecuteNonQuery(); 
    } 
} 
1

Tất cả đều chậm. Một số thời gian trước đây, chúng tôi đã giải quyết một vấn đề tương tự (chèn vào DB hàng chục nghìn dữ liệu giá, như tôi nhớ nó là khoảng 50K mỗi khung thời gian, và chúng tôi có khoảng 8 khung thời gian mà tất cả xung đột tại: 00, do đó, nó đã được khoảng 400K hồ sơ) và nó hoạt động rất nhanh cho chúng tôi (MS SQL 2005). Hãy tưởng tượng nó sẽ hoạt động như thế nào trong ngày hôm nay (SQL 2012):

<...init...> 
if(bcp_init(m_hdbc, TableName, NULL, NULL, DB_IN) == FAIL) 
    return FALSE; 

int col_number = 1; 

// Bind columns 
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.SymbolName, 0, 16, (LPCBYTE)"", 1, 0, col_number++) == FAIL) return FALSE; 
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.Time, 0, 4, 0, 0, 0, col_number++) == FAIL) return FALSE; 
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.Open, 0, 8, 0, 0, 0, col_number++) == FAIL) return FALSE; 
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.High, 0, 8, 0, 0, 0, col_number++) == FAIL) return FALSE; 
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.Low, 0, 8, 0, 0, 0, col_number++) == FAIL) return FALSE; 
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.Close, 0, 8, 0, 0, 0, col_number++) == FAIL) return FALSE; 
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.Volume, 0, 8, 0, 0, 0, col_number++) == FAIL) return FALSE; 


<...save into sql...> 
BOOL CSymbolStorage::Copy(SQL_SYMBOL_DATA *sd) 
{ 
    if(!m_bUseDB) 
     return TRUE; 

    memcpy(&m_sd, sd, sizeof(SQL_SYMBOL_DATA)); 

    if(bcp_sendrow(m_hdbc) != SUCCEED) 
     return FALSE; 

    return TRUE; 
} 
Các vấn đề liên quan