2009-08-19 21 views

Trả lời

8

Nếu bạn định sử dụng nó trong nhiều truy vấn, bạn có thể đặt làm mặc định thông qua thuộc tính cấu hình connection.isolation.

<property name="connection.isolation">ReadUncommitted</property> 

Kiểm tra documentation on this property.

+0

Điều này làm việc cho tôi! Thay đổi mục nhập web.config từ ReadCommitted thành ReadUncommitted đã loại bỏ các ngoại lệ DEADLOCK. Tôi hiểu rằng những lần đọc bây giờ có thể là 'bẩn thỉu' nhưng tôi nghĩ đó là một lựa chọn tốt hơn là chặn và giết chết phiên/kinh nghiệm của người dùng. Nếu họ xem dữ liệu bẩn, giả sử nó chưa được cập nhật trên chế độ xem và nó sẽ có lượt xem trang tiếp theo. Giải pháp tốt. Mặc dù nó không giải quyết vấn đề thực tế, và các trang vẫn mất nhiều thời gian để tải - nó đã loại bỏ các lỗi DEADLOCK. – dankeshawn

19

SetLockMode(LockMode.None) hoặc connection.isolation ReadUncomitted KHÔNG thêm NOLOCK vào truy vấn của bạn.

Ayende đi vào correct answer on his blog:

Nếu bạn đang sử dụng <sql-query> bạn có thể làm như sau:

<sql-query name="PeopleByName"> 
    <return alias="person" 
        class="Person"/> 
    SELECT {person.*} 
    FROM People {person} WITH(nolock) 
    WHERE {person}.Name LIKE :name 
</sql-query> 

Lưu ý WTIH(nolock) nối vào khoản FROM.

+2

Nhưng việc đặt mức cách ly giao dịch kết nối thành 'đọc không được cam kết' tương đương với việc thêm (nolock) vào mỗi bảng trong truy vấn, phải không? – codeulike

+0

Sự khác biệt là tôi giả sử rằng NOLOCK chỉ định một bảng cụ thể trong khi đọc không được cam kết chỉ định mọi bảng trong lựa chọn. Tôi không thể tưởng tượng đó là một vấn đề rất thường xuyên ... nhưng có thể. – PJUK

+0

@codeulike Tôi sẽ tranh luận không, Vui lòng tham khảo http://technet.microsoft.com/en-us/library/ms187373.aspx này; Vì nó có thể giúp với chức năng tương tự nhưng nó không giống nhau và nolock giúp bạn đọc dữ liệu đôi khi được gọi là đọc bẩn, nhưng đó là điều giúp bạn cải thiện tốc độ trong khi xử lý hàng triệu hàng hoặc bản ghi .. nHibernate !! – MarmiK

15

Tôi sẽ giải thích cách thực hiện điều này để bạn có thể thêm NOLOCK (hoặc bất kỳ gợi ý truy vấn nào khác), trong khi vẫn sử dụng ICriteria hoặc HQL và không cần phải biết các truy vấn của bạn vào ánh xạ hoặc cấu hình nhà máy phiên.

Tôi đã viết điều này cho NHibernate 2.1. Có một số cảnh báo chính với nó, chủ yếu là do lỗi trong NHibernate khi "use_sql_comments" được bật (xem bên dưới). Tôi không chắc chắn nếu những lỗi này đã được cố định trong NH 3, nhưng hãy thử nó ra. CẬP NHẬT: Lỗi chưa được sửa chữa như NH 3.3. Kỹ thuật và cách giải quyết mà tôi mô tả ở đây vẫn hoạt động.

Thứ nhất, tạo ra một đánh chặn, như thế này:

[Serializable] 
public class QueryHintInterceptor : EmptyInterceptor 
{ 
    internal const string QUERY_HINT_NOLOCK_COMMENT = "queryhint-nolock: "; 

    /// <summary> 
    /// Gets a comment to add to a sql query to tell this interceptor to add 'OPTION (TABLE HINT(table_alias, INDEX = index_name))' to the query. 
    /// </summary> 
    internal static string GetQueryHintNoLock(string tableName) 
    { 
     return QUERY_HINT_NOLOCK_COMMENT + tableName; 
    } 

    public override SqlString OnPrepareStatement(SqlString sql) 
    { 
     if (sql.ToString().Contains(QUERY_HINT_NOLOCK_COMMENT)) 
     { 
      sql = ApplyQueryHintNoLock(sql, sql.ToString()); 
     } 

     return base.OnPrepareStatement(sql); 
    } 

    private static SqlString ApplyQueryHintNoLock(SqlString sql, string sqlString) 
    { 
     var indexOfTableName = sqlString.IndexOf(QUERY_HINT_NOLOCK_COMMENT) + QUERY_HINT_NOLOCK_COMMENT.Length; 

     if (indexOfTableName < 0) 
      throw new InvalidOperationException(
       "Query hint comment should contain name of table, like this: '/* queryhint-nolock: tableName */'"); 

     var indexOfTableNameEnd = sqlString.IndexOf(" ", indexOfTableName + 1); 

     if (indexOfTableNameEnd < 0) 
      throw new InvalidOperationException(
       "Query hint comment should contain name of table, like this: '/* queryhint-nlock: tableName */'"); 

     var tableName = sqlString.Substring(indexOfTableName, indexOfTableNameEnd - indexOfTableName).Trim(); 

     var regex = new Regex(@"{0}\s(\w+)".F(tableName)); 

     var aliasMatches = regex.Matches(sqlString, indexOfTableNameEnd); 

     if (aliasMatches.Count == 0) 
      throw new InvalidOperationException("Could not find aliases for table with name: " + tableName); 

     var q = 0; 
     foreach (Match aliasMatch in aliasMatches) 
     { 
      var alias = aliasMatch.Groups[1].Value; 
      var aliasIndex = aliasMatch.Groups[1].Index + q + alias.Length; 

      sql = sql.Insert(aliasIndex, " WITH (NOLOCK)"); 
      q += " WITH (NOLOCK)".Length; 
     } 
     return sql; 
    } 

    private static SqlString InsertOption(SqlString sql, string option) 
    { 
     // The original code used just "sql.Length". I found that the end of the sql string actually contains new lines and a semi colon. 
     // Might need to change in future versions of NHibernate. 
     var regex = new Regex(@"[^\;\s]", RegexOptions.RightToLeft); 
     var insertAt = regex.Match(sql.ToString()).Index + 1; 
     return sql.Insert(insertAt, option); 
    } 
} 

Sau đó, tạo ra một số phương pháp mở rộng thoải mái ở đâu đó:

public static class NHibernateQueryExtensions 
{ 
    public static IQuery QueryHintNoLock(this IQuery query, string tableName) 
    { 
     return query.SetComment(QueryHintInterceptor.GetQueryHintNoLock(tableName)); 
    } 

    public static ICriteria QueryHintNoLock(this ICriteria query, string tableName) 
    { 
     return query.SetComment(QueryHintInterceptor.GetQueryHintNoLock(tableName)); 
    } 
} 

Tiếp theo, nói với NHibernate để sử dụng đánh chặn của bạn:

config.SetInterceptor(new QueryHintInterceptor()); 

Cuối cùng, hãy bật thuộc tính use_sql_commentsuse_sql_comments trong cấu hình NHibernate của bạn.

Và bạn đã hoàn tất! Bây giờ bạn có thể thêm những gợi ý nolock như thế này:

var criteria = Session.CreateCriteria<Foo>() 
    .QueryHintNoLock("tableFoo") 
    .List<Foo>(); 

tôi dựa công việc này xung quanh các kỹ thuật được mô tả ở đây: http://www.codewrecks.com/blog/index.php/2011/07/23/use-sql-server-query-hints-with-nhibernate-hql-and-icriteria/

NHibernate showstopping Bugs:

Thứ nhất, có this bug với NHibernate mà bạn sẽ cần phải sửa chữa. (Bạn có thể khắc phục lỗi này bằng cách sửa trực tiếp nguồn NHibernate hoặc by doing what I did và tạo Phương ngữ của riêng bạn để sửa chữa sự cố).

Thứ hai, có một lỗi khác dường như xảy ra khi bạn thực hiện truy vấn được phân trang, trên bất kỳ trang nào sau trang đầu tiên và bạn đang sử dụng phép chiếu. Sql được tạo ra bởi NHibernate là hoàn toàn sai lầm xung quanh mệnh đề "OVER".Ở giai đoạn này tôi không biết làm thế nào để sửa lỗi này nhưng tôi đang làm việc trên nó. CẬP NHẬT: Tôi có chi tiết cách khắc phục lỗi này here. Giống như các lỗi khác, điều này cũng có thể được sửa chữa hoặc bằng cách sửa chữa mã nguồn NHibernate hoặc bằng cách tạo ra lớp Dialect của riêng bạn.

+0

Thủ tục này xây dựng một sql động. Điều gì về bộ nhớ đệm lợi ích làm điều này? – Luciano

5

Điều này không thêm NOLOCK vào truy vấn của bạn mà tôi có thể nói, nhưng nó sẽ cung cấp chức năng tương tự - đó là để thực hiện đọc bẩn chỉ bên trong một giao dịch.

Session.BeginTransaction(IsolationLevel.ReadUncommitted); 

tôi đã sử dụng SQL Profiler để xem những gì lệnh trên sẽ làm gì nhưng nó không thay đổi bất cứ điều gì về các truy vấn hoặc thêm NOLOCK với họ (nhibernate sử dụng sp_executesql đối với hầu hết các truy vấn của tôi). Dù sao thì tôi cũng đã chạy với nó, và nó xuất hiện tất cả các deadlocks đã biến mất. Phần mềm của chúng tôi đã chạy theo cách này trong 3 ngày nay mà không bị deadlocks. Trước khi thay đổi này tôi thường có thể tái tạo các deadlocks trong vòng 15 phút. Tôi không thuyết phục được điều này 100% nhưng sau một tuần thử nghiệm khác, tôi sẽ biết nhiều hơn.

này đã làm việc cho người khác cũng như: http://quomon.com/NHibernate-deadlock-problem-q43633.aspx

+0

Trong khi giải pháp này giải quyết vấn đề ban đầu, nó có thể tạo ra một vấn đề khác: bây giờ bạn có một giao dịch rõ ràng mở, vì vậy với hành vi xả mặc định, bạn đột nhiên nhận được thêm CPU sử dụng và có thể roundtrips để DB một nơi nào đó mà bạn có vẻ không có họ trước đây. Chỉ cần ghi nhớ điều này. –

0

Bạn có thể giải quyết nó bằng cách sử dụng Interceptor.

var session = SessionFactory.OpenSession(new NoLockInterceptor()); 

Đây là triển khai cho lớp NoLockInterceptor. Về cơ bản lớp NoLockInterceptor sẽ chèn gợi ý "WITH (NOLOCK)" sau mỗi tên bảng trong truy vấn chọn, được tạo bởi nHibernate.


public class NoLockInterceptor : EmptyInterceptor 
{ 
    public override SqlString OnPrepareStatement(SqlString sql) 
     { 
      //var log = new StringBuilder(); 
      //log.Append(sql.ToString()); 
      //log.AppendLine(); 

      // Modify the sql to add hints 
      if (sql.StartsWithCaseInsensitive("select")) 
      { 
       var parts = sql.ToString().Split().ToList(); 
       var fromItem = parts.FirstOrDefault(p => p.Trim().Equals("from", StringComparison.OrdinalIgnoreCase)); 
       int fromIndex = fromItem != null ? parts.IndexOf(fromItem) : -1; 
       var whereItem = parts.FirstOrDefault(p => p.Trim().Equals("where", StringComparison.OrdinalIgnoreCase)); 
       int whereIndex = whereItem != null ? parts.IndexOf(whereItem) : parts.Count; 

       if (fromIndex == -1) 
        return sql; 

       parts.Insert(parts.IndexOf(fromItem) + 3, "WITH (NOLOCK)"); 
       for (int i = fromIndex; i < whereIndex; i++) 
       { 
        if (parts[i - 1].Equals(",")) 
        { 
         parts.Insert(i + 3, "WITH (NOLOCK)"); 
         i += 3; 
        } 
        if (parts[i].Trim().Equals("on", StringComparison.OrdinalIgnoreCase)) 
        { 
         parts[i] = "WITH (NOLOCK) on"; 
        } 
       } 
       // MUST use SqlString.Parse() method instead of new SqlString() 
       sql = SqlString.Parse(string.Join(" ", parts)); 
      } 

      //log.Append(sql); 
      return sql; 
     } 
} 
Các vấn đề liên quan