วิธีเพิ่มความเร็วคิวรีด้วยพาร์ติชันคีย์ในที่จัดเก็บตารางสีฟ้า


10

เราจะเพิ่มความเร็วของแบบสอบถามนี้ได้อย่างไร

เรามีผู้บริโภคประมาณ100 คนภายในระยะเวลาของ1-2 minutesการดำเนินการค้นหาต่อไปนี้ หนึ่งในการดำเนินการเหล่านี้หมายถึงการเรียกใช้ 1 ฟังก์ชันการสิ้นเปลือง

        TableQuery<T> treanslationsQuery = new TableQuery<T>()
         .Where(
          TableQuery.CombineFilters(
            TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sourceDestinationPartitionKey)
           , TableOperators.Or,
            TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, anySourceDestinationPartitionKey)
          )
         );

แบบสอบถามนี้จะให้ผลลัพธ์ประมาณ5,000 รายการ

รหัสเต็ม:

    public static async Task<IEnumerable<T>> ExecuteQueryAsync<T>(this CloudTable table, TableQuery<T> query) where T : ITableEntity, new()
    {
        var items = new List<T>();
        TableContinuationToken token = null;

        do
        {
            TableQuerySegment<T> seg = await table.ExecuteQuerySegmentedAsync(query, token);
            token = seg.ContinuationToken;
            items.AddRange(seg);
        } while (token != null);

        return items;
    }

    public static IEnumerable<Translation> Get<T>(string sourceParty, string destinationParty, string wildcardSourceParty, string tableName) where T : ITableEntity, new()
    {
        var acc = CloudStorageAccount.Parse(Environment.GetEnvironmentVariable("conn"));
        var tableClient = acc.CreateCloudTableClient();
        var table = tableClient.GetTableReference(Environment.GetEnvironmentVariable("TableCache"));
        var sourceDestinationPartitionKey = $"{sourceParty.ToLowerTrim()}-{destinationParty.ToLowerTrim()}";
        var anySourceDestinationPartitionKey = $"{wildcardSourceParty}-{destinationParty.ToLowerTrim()}";

        TableQuery<T> treanslationsQuery = new TableQuery<T>()
         .Where(
          TableQuery.CombineFilters(
            TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sourceDestinationPartitionKey)
           , TableOperators.Or,
            TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, anySourceDestinationPartitionKey)
          )
         );

        var over1000Results = table.ExecuteQueryAsync(treanslationsQuery).Result.Cast<Translation>();
        return over1000Results.Where(x => x.expireAt > DateTime.Now)
                           .Where(x => x.effectiveAt < DateTime.Now);
    }

ในระหว่างการประหารชีวิตเมื่อมีผู้บริโภค 100 คนดังที่คุณเห็นคำขอจะทำคลัสเตอร์และสร้างแบบฟอร์ม

ป้อนคำอธิบายรูปภาพที่นี่

ในช่วงหนามแหลมเหล่านี้คำขอมักจะใช้เวลามากกว่า 1 นาที:

ป้อนคำอธิบายรูปภาพที่นี่

เราจะเพิ่มความเร็วของแบบสอบถามนี้ได้อย่างไร


ผลลัพธ์ 5000 รายการดูเหมือนว่าคุณกรองไม่เพียงพอในแบบสอบถาม เพียงการถ่ายโอน 5,000 ผลลัพธ์ไปยังโค้ดจะใช้เวลาเครือข่ายหนึ่งตัน ไม่เป็นไรหรอกว่าคุณจะยังคงทำการกรองต่อไป | ทำการกรองการประมวลผลในคิวรีเสมอ เป็นการดีที่แถวที่มีดัชนีและ / หรือเป็นผลลัพธ์ของมุมมองที่คำนวณ
Christopher

วัตถุ "การแปล" เหล่านั้นใหญ่หรือไม่ ทำไมคุณไม่ชอบรับพารามิเตอร์บางอย่างแทน gettin` เช่น db ทั้งหมด?
Hirasawa Yui

@HirasawaYui ไม่มีขนาดเล็ก
l

คุณควรทำการกรองมากกว่านี้การดึงผลลัพธ์ 5000 รายการออกมาไร้ความหมาย มันเป็นไปไม่ได้ที่จะบอกโดยไม่ทราบว่าข้อมูลของคุณ แต่ฉันว่าคุณจะต้องหาวิธีที่จะแบ่งพาร์ติชันในแบบที่มีความหมายมากขึ้นหรือแนะนำการกรองบางอย่างในแบบสอบถาม
4c74356b41

มีพาร์ติชั่นต่างกันกี่อัน?
Peter Bons

คำตอบ:


3
  var over1000Results = table.ExecuteQueryAsync(treanslationsQuery).Result.Cast<Translation>();
        return over1000Results.Where(x => x.expireAt > DateTime.Now)
                           .Where(x => x.effectiveAt < DateTime.Now);

นี่คือปัญหาข้อใดข้อหนึ่งคุณกำลังเรียกใช้แบบสอบถามและกรองข้อมูลจากหน่วยความจำโดยใช้ "wheres" เหล่านี้ ย้ายตัวกรองไปก่อนที่แบบสอบถามจะทำงานซึ่งน่าจะช่วยได้มาก

อันดับที่สองคุณต้องระบุแถวที่ จำกัด เพื่อดึงข้อมูลจากฐานข้อมูล


สิ่งนี้ไม่ได้สร้างความแตกต่าง
l

3

มี 3 สิ่งที่คุณสามารถพิจารณาได้:

1 . ก่อนอื่นกำจัดWhereข้อที่คุณทำกับผลการสืบค้น มันจะดีกว่าที่จะรวมคำสั่งในการค้นหาให้มากที่สุด (ดียิ่งขึ้นถ้าคุณมีดัชนีใด ๆ ในตารางของคุณรวมไว้ด้วย) ในตอนนี้คุณสามารถเปลี่ยนการค้นหาได้ดังนี้:

var translationsQuery = new TableQuery<T>()
.Where(TableQuery.CombineFilters(
TableQuery.CombineFilters(
    TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sourceDestinationPartitionKey),
    TableOperators.Or,
    TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, anySourceDestinationPartitionKey)
    ),
TableOperators.And,
TableQuery.CombineFilters(
    TableQuery.GenerateFilterConditionForDate("affectiveAt", QueryComparisons.LessThan, DateTime.Now),
    TableOperators.And,
    TableQuery.GenerateFilterConditionForDate("expireAt", QueryComparisons.GreaterThan, DateTime.Now))
));

เนื่องจากคุณมีข้อมูลจำนวนมากที่จะเรียกคืนได้ดีกว่าที่จะเรียกใช้แบบสอบถามของคุณในแบบคู่ขนาน ดังนั้นคุณควรแทนที่วิธีdo whileลูปภายในExecuteQueryAsyncด้วยParallel.ForEachฉันเขียนตามStephen Toub Parallel.While ; วิธีนี้จะลดเวลาดำเนินการค้นหา นี่เป็นตัวเลือกที่ดีเพราะคุณสามารถลบได้Resultเมื่อคุณโทรออกด้วยวิธีนี้ แต่มีข้อ จำกัด เล็กน้อยที่ฉันจะพูดถึงเรื่องนี้หลังจากโค้ดส่วนนี้:

public static IEnumerable<T> ExecuteQueryAsync<T>(this CloudTable table, TableQuery<T> query) where T : ITableEntity, new()
{
    var items = new List<T>();
    TableContinuationToken token = null;

    Parallel.ForEach(new InfinitePartitioner(), (ignored, loopState) =>
    {
        TableQuerySegment<T> seg = table.ExecuteQuerySegmented(query, token);
        token = seg.ContinuationToken;
        items.AddRange(seg);

        if (token == null) // It's better to change this constraint by looking at https://www.vivien-chevallier.com/Articles/executing-an-async-query-with-azure-table-storage-and-retrieve-all-the-results-in-a-single-operation
            loopState.Stop();
    });

    return items;
}

และจากนั้นคุณสามารถเรียกมันได้ในGetวิธีการของคุณ:

return table.ExecuteQueryAsync(translationsQuery).Cast<Translation>();

ในขณะที่คุณสามารถดูวิธีการมันไม่ได้เป็น async (คุณควรเปลี่ยนชื่อ) และParallel.ForEachเข้ากันไม่ได้กับการผ่านในวิธีการ async นี่คือเหตุผลที่ฉันใช้ExecuteQuerySegmentedแทน แต่จะทำให้มัน performant มากขึ้นและใช้ประโยชน์ทั้งหมดของวิธีการไม่ตรงกันคุณสามารถแทนที่ด้านบนForEachห่วงกับActionBlockวิธีการในDataflowหรือParallelForEachAsyncวิธีขยายจากแพคเกจ AsyncEnumerator Nuget

2.เป็นตัวเลือกที่ดีในการเรียกใช้คิวรีแบบขนานอย่างอิสระจากนั้นรวมผลลัพธ์แม้ว่าการปรับปรุงประสิทธิภาพจะมีมากที่สุด 10 เปอร์เซ็นต์ก็ตาม สิ่งนี้จะช่วยให้คุณมีเวลาในการค้นหาข้อความค้นหาที่เป็นมิตรที่ดีที่สุด แต่อย่าลืมที่จะรวมข้อ จำกัด ทั้งหมดของคุณไว้ในนั้นและทดสอบทั้งสองวิธีเพื่อให้ทราบว่าปัญหาของคุณดีกว่าแบบใด

3 . ฉันไม่แน่ใจว่ามันเป็นคำแนะนำที่ดีหรือไม่ แต่ทำได้และดูผลลัพธ์ ตามที่อธิบายไว้ในMSDN :

บริการ Table บังคับให้หมดเวลาเซิร์ฟเวอร์ดังนี้:

  • การดำเนินการของแบบสอบถาม: ในช่วงระยะหมดเวลาแบบสอบถามอาจดำเนินการได้สูงสุดไม่เกินห้าวินาที หากแบบสอบถามไม่เสร็จสมบูรณ์ภายในช่วงเวลาห้าวินาทีการตอบสนองจะรวมโทเค็นความต่อเนื่องในการดึงรายการที่เหลืออยู่ในคำขอถัดไป ดู Query Timeout และ Pagination สำหรับข้อมูลเพิ่มเติม

  • แทรกอัปเดตและลบการดำเนินการ: ช่วงเวลาการหมดเวลาสูงสุดคือ 30 วินาที สามสิบวินาทีเป็นช่วงเวลาเริ่มต้นสำหรับการดำเนินการแทรกอัปเดตและลบทั้งหมด

หากคุณระบุการหมดเวลาที่น้อยกว่าการหมดเวลาเริ่มต้นของบริการช่วงเวลาการหมดเวลาของคุณจะถูกนำมาใช้

ดังนั้นคุณสามารถเล่นกับหมดเวลาและตรวจสอบว่ามีการปรับปรุงประสิทธิภาพใด ๆ


2

ขออภัยแบบสอบถามด้านล่างแนะนำการสแกนแบบเต็มตาราง :

    TableQuery<T> treanslationsQuery = new TableQuery<T>()
     .Where(
      TableQuery.CombineFilters(
        TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sourceDestinationPartitionKey)
       , TableOperators.Or,
        TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, anySourceDestinationPartitionKey)
      )
     );

คุณควรแบ่งออกเป็นสองตัวกรองคีย์พาร์ติชันและค้นหาแยกต่างหากซึ่งจะเป็นการสแกนพาร์ติชันที่สองและดำเนินการอย่างมีประสิทธิภาพยิ่งขึ้น


เราเห็นว่าอาจมีการปรับปรุง 10% ด้วยสิ่งนี้ แต่ก็ไม่เพียงพอ
l

1

ดังนั้นความลับไม่เพียง แต่ในรหัสเท่านั้น แต่ยังอยู่ในการตั้งค่าตารางพื้นที่เก็บข้อมูล Azure ของคุณ

a) หนึ่งในตัวเลือกที่โดดเด่นเพื่อเพิ่มประสิทธิภาพการสืบค้นของคุณใน Azure คือการแนะนำแคช นี่จะลดเวลาตอบสนองโดยรวมของคุณลงอย่างมากและหลีกเลี่ยงปัญหาคอขวดในช่วงชั่วโมงเร่งด่วนที่คุณกล่าวถึง

b) นอกจากนี้เมื่อทำการค้นหาเอนทิตีจาก Azure วิธีที่เร็วที่สุดในการทำเช่นนั้นคือทั้ง PartitionKey และ RowKey เหล่านี้เป็นเขตข้อมูลที่จัดทำดัชนีเพียงอย่างเดียวใน Table Storage และแบบสอบถามใด ๆ ที่ใช้ทั้งสองอย่างนี้จะถูกส่งคืนในเวลาไม่กี่มิลลิวินาที เพื่อให้แน่ใจว่าคุณใช้ทั้ง PartitionKey & RowKey

ดูรายละเอียดเพิ่มเติมได้ที่นี่: https://docs.microsoft.com/en-us/azure/storage/tables/table-storage-design-for-query

หวังว่านี่จะช่วยได้


-1

หมายเหตุ: นี่เป็นคำแนะนำในการเพิ่มประสิทธิภาพการสืบค้นฐานข้อมูลทั่วไป

เป็นไปได้ว่าออมกำลังทำอะไรที่โง่ เมื่อทำการปรับให้เหมาะสมมันก็โอเคที่จะลดเลเยอร์ที่เป็นนามธรรม ดังนั้นฉันขอแนะนำให้เขียนคำค้นใหม่ในภาษาข้อความค้นหา (SQL?) เพื่อให้ง่ายต่อการดูว่าเกิดอะไรขึ้นและยังเพิ่มประสิทธิภาพได้ง่ายขึ้น

กุญแจสำคัญในการเพิ่มประสิทธิภาพการค้นหาคือการเรียงลำดับ! การจัดเรียงตารางมักจะถูกกว่ามากเมื่อเปรียบเทียบกับการสแกนทั้งตารางในทุกแบบสอบถาม! ดังนั้นถ้าเป็นไปได้เก็บตารางที่เรียงลำดับตามคีย์ที่ใช้ในแบบสอบถาม ในโซลูชันฐานข้อมูลส่วนใหญ่สามารถทำได้โดยการสร้างคีย์ดัชนี

กลยุทธ์อื่นที่ใช้งานได้ดีหากมีชุดค่าผสมไม่กี่ชุดคือให้แต่ละคิวรีเป็นตารางแยกต่างหาก (ชั่วคราวในหน่วยความจำ) ที่ทันสมัยอยู่เสมอ ดังนั้นเมื่อมีบางสิ่งแทรกอยู่ก็จะ "แทรก" ลงในตาราง "มุมมอง" โซลูชันฐานข้อมูลบางตัวเรียกว่า "มุมมอง" นี้

กลยุทธ์ที่ดุร้ายมากขึ้นคือการสร้างแบบจำลองแบบอ่านอย่างเดียวเพื่อกระจายโหลด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.