مطالب
Garbage Collector در #C - قسمت دوم
در این مطلب قصد داریم به تفاوت‎های بین Stack و Heap در Memory و زبان #C بپردازیم.

به زبان ساده، وقتی شما متغیر جدیدی را ایجاد میکنید، با توجه به نوع (Type) آن متغیر، "مقدار" متغیر شما در Stack یا Heap قرار خواهد گرفت.

Stack

Stack نوعی ساختمان داده‌است که در آن، داده‌ها بصورت خطی قرار گرفته و اصطلاحا ساختار LIFO ( مخفف Last in, First Out ) دارند، بدین معنا که همیشه آخرین داده‌ای که داخل Stack قرار داده‌اید، اولین داده‌ای است که قادر به خواندن آن خواهید بود. وقتی در ساختار Stack داده‌ای را قرار میدهیم، اصطلاحا آن را Push کرده و وقتی میخواهیم آخرین داده را با توجه به ساختار خطی آن بخوانیم، داده را Pop میکنیم.


این ساختمان داده، داخل Memory پیاده سازی شده است و تعدادی از متغیرهایی را که ما داخل کد ایجاد میکنیم، در این نوع ساختمان داده از Memory نگهداری میشوند.

شرط قرار گرفتن مقدار یک متغیر داخل Stack این است که متغیر از نوع Value Type باشد. در زبان #C، بطور کلی Struct و Enum‌ها Value Type هستند و بصورت پیشفرض داخل Stack قرار میگیرند. تمامی ValueType‌ها در #C، بطور implicit از System.ValueType ارث بری میکنند.

Type‌های زیر، Value Type‌های پیشفرض تعریف شده‌ی در زبان #C هستند که به آن‌ها Simple Type نیز گفته میشوند:


Represents   Type
 Boolean value  bool
8-bit unsigned integer
 byte
 16-bit Unicode character  char
128-bit precise decimal values with 28-29 significant digits   decimal
 64-bit double-precision floating point type  double
 32-bit single-precision floating point type  float
 32-bit signed integer type  int
 64-bit signed integer type  long
 8-bit signed integer type  sbyte
 16-bit signed integer type  short
 32-bit unsigned integer type  uint
 64-bit unsigned integer type  ulong
16-bit unsigned integer type   ushort


اگر سورس هرکدام از این تایپ‌ها مانند  Int32 را در ریپازیتوری CoreFX مایکروسافت بررسی کنید، متوجه خواهید شد که تمامی این تایپ‌ها از نوع Struct تعریف شده‌اند و همانطور که گفتیم، بطور پیش‌فرض، Struct‌ها داخل Stack قرار خواهند گرفت.

طول عمر متغیرهایی که داخل Stack قرار گرفته‌اند، منحصر به پایان اجرای یک متد است. بدین معنا که بعد از به پایان رسیدن یک متد، تمامی متغیرهای مورد استفاده در آن متد، از حافظه Stack بطور خودکار حذف خواهند شد. متغیرهایی که داخل Stack قرار میگیرند، نوع و حجم مقادیرشان بر اساس Type ای دارند، در زمان Compile-Time مشخص است.

متغیرهای محلی (Local Variable ها)، پارامترهای ورودی متد و مقدار بازگشتی یک متد، جز مواردی هستند که مقادیرشان داخل Stack قرار میگیرد:
public static int Add(int number1, int number2)
{
    // number1 is on the stack (function parameter)
    // number2 is on the stack (function parameter)

    int sum = number1 + number2;
    // sum is on the stack (local variable)

    return sum;
}

در زبان #C و در مرحله Compile-Time، کدها به زبان IL (مخفف Intermediate Language) ترجمه میشوند که با نام‌های MSIL (مخفف Microsoft Intermediate Language ) و CIL (مخفف Common Intermediate Language ) نیز، این زبان شناخته میشود. ساختار این زبان Stack-based بوده و با شناخت آن، با مفهوم Stack نیز بهتر میتوانیم آشنا شویم.

IL زبانی است که CLR (مخفف Common Language Runtime) را که همان Runtime مایکروسافت است، شناخته و اجرا میکند. قابل ذکر است که Runtime مایکروسافت Open-Source بوده و سورس آن با نام CoreCLR در گذشته از این آدرس و در حال حاضر با نام Runtime از این آدرس قابل دسترسی است.

با استفاده از برنامه هایی مانند dotPeek یا dnSpy یا ILDASM یا ابزار آنلاینی مانند Sharplab  و ... میتوانید کدهای IL حاصل از dll‌های برنامه خود را ببینید. این ابزارها با یکدیگر تفاوت زیادی ندارند و تنها مزیت dnSpy به نسبت بقیه، قابلیت دیباگ کردن کدهای IL توسط آن میباشد و همچنین ILDASM با نصب Visual Studio، از این مسیر بدون نیاز به نصب برنامه اضافه ای قابل دسترسی است:
C:\Program Files (x86)\Microsoft SDKs\Windows\{version}\Bin\ildasm.exe

همانطور که پیش‌تر گفتیم، طول عمر Stack محدود به پایان یک متد است. به این نوع Stack که هنگام صدا زدن یک متد ایجاد میشود و شامل ورودی‌های متد، متغیرهای محلی و آدرس خروجی هستند، Stack Frame یا Activation Frame گفته میشود.

 

اگر متد Add بالا را با پارامترهای 2 و 5 صدا بزنیم، خروجی IL حاصل از آن، که این دو عدد را بعنوان ورودی گرفته و جمع آنها را بعنوان خروجی میدهد، به این صورت خواهد بود ( قسمت هایی از خروجی جهت سادگی، حذف شده است) :
.method private hidebysig static int32 Add(int32 number1, int32 number2) cil managed
{
  .locals init (int32 V_0, int32 V_1)
  
  IL_0001:  ldarg.0 // Stack is: [2]
  IL_0002:  ldarg.1 // Stack is: [2, 5]
  IL_0003:  add     // Stack is: [7]
  IL_0004:  stloc.0 // Stack is: [] and V_0's value is: 7
  
  IL_0005:  ldloc.0 // Stack is: [7]
  IL_0006:  stloc.1 // Stack is: [] and V_1's value is: 7

  IL_0009:  ldloc.1 // Stack is: [7]
  IL_000a:  ret     // Return [7]
}

میتوانید لیست دستورات مورد استفاده در CIL را از اینجا ببینید.

در ادامه، خط به خط، خروجی حاصل را بررسی میکنیم:

1- در زبان IL، میتوانید مقادیر حاصل از اعمال محاسباتی یا متدهای دیگر را داخل متغیرهای محلی ذخیره کنید، به شرط اینکه آنها را در ابتدا مشخص سازید.
    • با استفاده از locals. که به معنای local variables است، میتوانید متغیرهای مورد نیازتان را در طول عمر این متد، معرفی کنید. دادن نام برای این متغیرها اجباری نیست (V_0 و V_1) و صرفا جهت خوانایی استفاده میشوند.


2- از کلمه کلیدی ldarg (مخفف Load Argument) برای لود کردن آرگومان یا همان پارامتر ورودی متد، داخل Stack استفاده میشود.
    • ldarg.0 به معنای لود کردن پارامتر ورودی اول، داخل Stack است و با فراخوانی آن، Stack Frame دارای یک عضو که مقدار آن 2 است، میشود.
    • ldarg.1 به معنای لود کردن پارامتر ورودی دوم، داخل Stack است و با فراخوانی آن، Stack Frame دارای دو عضو که مقادیر آن 2 و 5 است، میشود.

3- با استفاده از کلمه کلیدی add، مقادیر موجود در Stack با یکدیگر جمع میشوند و Stack Frame دارای یک عضو که مقدار آن 7 است، میشود.

4- با استفاده از کلمه کلیدی stloc (مخفف Store Local)، آخرین عضو موجود در Stack، داخل متغیر محلی ذکر شده، قرار گرفته و ذخیره میشود.
    • stloc.0 به معنای ذخیره سازی آخرین مقدار موجود در Stack یعنی عدد 7، داخل متغیر 0 یعنی همان V_0 میباشد. 

5- با استفاده از کلمه کلیدی ldloc (مخفف Load Local)، میتوان متغیر محلی ذخیره شده را داخل Stack قرار داد.
    • ldloc.0 به معنای Load کردن مقدار ذخیره شده متغیر محلی 0 که همان V_0 است، داخل Stack میباشد.

6- در نهایت، مقدار 7، داخل متغیر 1 یا همان V_1 با دستور stloc.1 بار دیگر ذخیره، با ldloc.1 لود شده و با استفاده از دستور ret، برگشت داده میشود.

* نکته: اگر کدها را بطور دقیق بررسی کرده باشید، احتمالا فکر کرده اید که چه نیازی به ایجاد یک متغیر اضافی و ریختن نتیجه داخل آن و سپس برگشت دادن نتیجه، در مرحله 6 است؟!
در زبان #C، کدهای شما در زمان Release و همچنین JIT-Compilation، طی چندین مرحله Optimize میشوند و یکی از این مراحل، حذف این متغیرهای اضافی جهت Optimization و Performance است؛ پس از این بابت نگرانی وجود ندارد.

* نکته: احتمالا تا به اینجا دلیل بوجود آمدن StackOverflowException را متوجه شده باشید. فضای Stack محدود است. این فضا در سیستم‌های 32 بیت برابر با 1 مگابایت و در سیستم‌های 64 بیت برابر با 4 مگابایت است (Reference). اگر حجم متغیرهایی که روی استک Push میشوند، این محدودیت را رد کنند و یا اگر یک متد بطور دائم خودش را صدا بزند (Recursive) و هیچگاه از آن خارج نشود، با خطای StackOverflowException مواجه میشوید.

Heap


.Heap: a group of things placed, thrown, or lying one on another


در مقابل ساختار ترتیبی و منظم Stack، ساختار Heap قرار دارد. Heap قسمتی از حافظه است که ساختار، ترتیب و Layout خاصی ندارد.
این نوع حافظه بر خلاف Stack، منحصر به یک متد نیست و اصطلاحا Global بوده و در هر قسمتی از برنامه قابل دسترسی است. تخصیص حافظه در این قسمت از حافظه اصطلاحا Dynamic بوده و هر نوع داده ای را در هر زمانی میتوان داخل آن ذخیره کرد.

 string‌ها نمونه‌ای از typeهایی هستند که داخل Heap نگه داری میشوند. دقت کنید وقتی میگوییم نگه داری میشود، منظور «مقدار» یک متغیر است.

وقتی یک متغیر از نوع string را ایجاد میکنیم، مقدار آن داخل Heap و Memory-Address آن متغیر روی Heap، در Stack نگه داری میشود:
public static void SayHi()
{
    string name = "Moien";
}

در این مثال، چون string یک class است، مقدار آن داخل heap ذخیره شده و آدرس آن قسمت (segment) از memory، روی Stack قرار میگیرد:
.method private hidebysig static void SayHi() cil managed
{
  .locals init (string V_0)

  IL_0001:  ldstr      "Moien" // Stack is: [memory-address of string in heap]
  IL_0006:  stloc.0
  
  IL_0007:  ret
}

به متغیرهایی که مقادیرشان داخل Heap ذخیره میشوند، Reference-Type گفته میشود.

* نکته: در این مثال متغیری به نام name ایجاد شده که از آن هیچ استفاده‌ای نشده است. در زمان JIT-Compilation، با توجه با Optimization‌های موجود در سطح CLR، این متد بطور کلی اضافه تشخیص داده شده و از آن صرفنظر خواهد شد.



Boxing and Unboxing


به فرایند تبدیل یک Value-Type مانند int که بصورت پیشفرض داخل Stack ذخیره میشود، به یک object که در داخل Heap ذخیره میشود، Boxing گفته میشود. انجام این عمل باعث allocation بر روی memory میشود که سربار زیادی دارد. 

با انجام عمل Boxing، قادر خواهیم بود تا بعنوان مثال یک عدد را بر خلاف روال عادی آن، روی Heap ذخیره کنیم:
public static void Boxing()
{
    const int number = 5;
    
    object boxedNumber = number;          // implicit boxing using implicit cast
    object boxedNumber = (object)number;  // explicit boxing using direct cast
}

در ابتدا عدد 5 روی Stack ذخیره شده بود، اما با Box کردن آن، یعنی قرار دادن مقدار آن داخل یک object، مقدار از Stack به Heap انتقال داده شده و allocation اتفاق خواهد افتاد:
.method public hidebysig static void Boxing() cil managed
{
  .locals init (object V_0)
  
  IL_0001:  ldc.i4.5                                // Stack is: [5]
  IL_0002:  box        [System.Runtime]System.Int32 // Stack is: [memory-address of 5 in heap]
  
  IL_0007:  stloc.0
  IL_0008:  ret
}

به عکس این عمل، یعنی تبدیل یک Reference-Type به یک Value-Type، اصطلاحا Unboxing گفته میشود:
public static void Unboxing()
{
    object boxedNumber = 5;
    
    int number = (int)boxedNumber;
}

که نتیجه آن، به این صورت خواهد بود:
.method public hidebysig static void Unboxing() cil managed
{
  .locals init (object V_0, int32 V_1)
  
  IL_0001:  ldc.i4.5                                  // Stack is: [5]
  IL_0002:  box        [System.Runtime]System.Int32   // Stack is: [memory-address of 5 in heap]
  IL_0007:  stloc.0                                   // Stack is: []
                                                      
  IL_0008:  ldloc.0                                   // Stack is: [memory-address of 5 in heap]
  IL_0009:  unbox.any  [System.Runtime]System.Int32   // Stack is: [5]
  IL_000e:  stloc.1                                   // Stack is: []
  
  IL_000f:  ret
}

تلاش تیم‌های مایکروسافت طی سال‌های اخیر، باعث افزایش Performance فوق العاده در NET Core. و ASP.NET Core شده است. یکی از دلایل این Performance، جلوگیری بسیار زیاد از allocation در کدهای خود NET. است، که این امر به واسطه اولویت قرار دادن استفاده از Structها میسر گردیده است.

برخلاف Stack که طول عمر متغیرهای موجود در آن، در انتهای یک متد پایان می‌یابند، متغیرهای allocate شده‌ی در Heap به این شکل نبوده و در صورت حذف نکردن آنها بصورت دستی، تا پایان طول عمر اجرای برنامه داخل memory باقی خواهند ماند. اینجا، جاییست که Garbage Collector در NET. وارد عمل میشود.
اشتراک‌ها
اطلاعات خود در مورد ارث‌بری را محک بزنید
امشب به تاریخ ۲۰ مهر، سؤالی در مورد ارث‌بری، interface و abstract class در سایت محبوب همگی ما، StackOverFlow منتشر شد. سؤال حاکی از رفتار عجیب قطعه کد زیر در نسخه ۸ سی‌شارپ است. تا به اینجا برای سؤال مذکور، یک پاسخ با اعتماد به نقس بالایی ارائه شد و پس از مدت اندکی حذف گردیده است.

interface I {
    string M1() => "I.M1";
    string M2() => "I.M2";
}

abstract class A : I {}

class C : A {
    public string M1() => "C.M1";
    public virtual string M2() => "C.M2";
}

class Program {
    static void Main() {
        I obj = new C();
        System.Console.WriteLine(obj.M1());
        System.Console.WriteLine(obj.M2());
    }
}
نظر شما در مورد نحوه عملکرد چیست؟ در صورت استفاده از کلمه کلیدی‌های new و override چه خروجی‌هایی خواهیم گرفت.
اگر برای شما هم این مبحث جذاب شده است، به سؤال اصلی  مراجعه کنید.
اطلاعات خود در مورد ارث‌بری را محک بزنید
اشتراک‌ها
بهبود کارآیی IDEهای Jetbrains

سال‌ها است که  IDEهای Jetbrains برای اینکه در سکوهای کاری متفاوت قابل اجرا باشند، با جاوا نوشته می‌شوند. برای مثال Rider که یک IDE مخصوص دات نت است نیز با جاوا نوشته شده‌است و مابقی آن‌ها نیز به همین صورت. اگر به مسیر C:\Program Files\JetBrains\JetBrains Rider 2018.3.4\bin\rider64.exe.vmoptions مراجعه کنید، فایل با پسوند vmoptions در حقیقت تنظیمات Java Virtual Machine یا JVM را به همراه دارد. این فایل طوری تنظیم شده‌است که کمترین منابع را مصرف کند؛ به همین جهت شاید در حین کار کردن با این IDEها احساس کنید که کند هستند. تنظیمات JVM مخصوص جاوای ویندوز و جاوای مک و لینوکس را در اینجا و اینجا می‌توانید مطالعه کنید.
اگر بر این اساس فایل rider64.exe.vmoptions را بخواهیم تکمیل کنیم، می‌توان به تنظیمات زیر رسید:

-Xms1024m
-Xmx3072m
-Xss64m
-XX:ReservedCodeCacheSize=512m
-XX:+UseCompressedOops
-XX:NewRatio=2
-Dfile.encoding=UTF-8
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=250
-XX:NewSize=512m
-XX:MaxNewSize=512m
-XX:PermSize=512m
-XX:MaxPermSize=1024m
-XX:+UseParNewGC
-XX:ParallelGCThreads=4
-XX:MaxTenuringThreshold=1
-XX:SurvivorRatio=8
-XX:+UseCodeCacheFlushing
-XX:+AggressiveOpts
-XX:+CMSClassUnloadingEnabled
-XX:+CMSIncrementalMode
-XX:+CMSIncrementalPacing
-XX:+CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction=65
-XX:+CMSScavengeBeforeRemark
-XX:+UseCMSInitiatingOccupancyOnly
-XX:-TraceClassUnloading
-XX:+AlwaysPreTouch
-XX:+TieredCompilation
-XX:+DoEscapeAnalysis
-XX:+UnlockExperimentalVMOptions
-XX:LargePageSizeInBytes=256m
-XX:+DisableExplicitGC
-XX:+ExplicitGCInvokesConcurrent
-XX:+PrintGCDetails
-XX:+PrintFlagsFinal
-XX:+CMSPermGenSweepingEnabled
-XX:+UseAdaptiveGCBoundary
-XX:+UseSplitVerifier
-XX:CompileThreshold=10000
-XX:+OptimizeStringConcat
-XX:+UseStringCache
-XX:+UseFastAccessorMethods
-XX:+UnlockDiagnosticVMOptions
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-Djdk.http.auth.tunneling.disabledSchemes=""
-XX:+HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow
-Xverify:none
برای ویرایش فایل rider64.exe.vmoptions، چون در مسیر C:\Program Files قرار دارد، نیاز است برای مثال نوت‌پد یا هر ویرایشگر متنی دیگر را با دسترسی ادمین اجرا کنید تا بتوانید تغییرات انجام شده را ذخیره نمائید. برای مثال اگر از notepad++ استفاده کنید، خودش این مساله را تشخیص داده و درخواست اجرای با دسترسی ادمین را نمایش می‌دهد.

پس از این تغییرات اگر Rider را اجرا کنید، حداقل نسبت به قبل دو برابر RAM مصرف خواهد کرد. همچنین بار اولی که برنامه را اجرا می‌کنید، چون تعدادی از این تنظیمات بر روی نحوه‌ی JIT تاثیرگذار هستند، کمی طول می‌کشد تا کار کامپایل جدید آن صورت گیرد و از دفعات آتی اجرای آن، بهبود کارآیی را احساس خواهید کرد.

علاوه بر موارد فوق، فایل C:\Program Files\JetBrains\JetBrains Rider 2018.3.4\bin\idea.properties را نیز می‌توانید جهت اعمال تغییرات زیر ویرایش کنید:

idea.max.intellisense.filesize=3500
idea.cycle.buffer.size=2048
بهبود کارآیی IDEهای Jetbrains
اشتراک‌ها
15 نکته برای بالا بردن سرعت برنامه‌های ASP.NET

Performance of your ASP.NET web application is important. There is a lot of evidence to suggest that slow loading times and clunky interaction, will drive customers elsewhere. Even in the case of internal applications where the users have no option but to use the application, their satisfaction is tightly coupled to speed.

There are a ton of ways to improve the performance of a website, let's look at fifteen of them. 

15 نکته برای بالا بردن سرعت برنامه‌های ASP.NET
مطالب
پیدا کردن لیست SQL server های نصب شده در یک شبکه


با آمدن SQL server 2008 استفاده از کتابخانه SQL-DMO برای انجام یک سری از امور بر روی اس کیوال سرور با استفاده از برنامه نویسی منسوخ شد. یکی از توانایی‌های این کتابخانه لیست کردن سرورهای اس کیوال (قابل دسترسی) موجود در شبکه بود.
برای مثال توسط این کتابخانه به صورت زیر می‌توان اینکار را انجام داد:
در قطعه کد زیر فرض بر این است که ارجاعی به کتابخانه sqldmo را در برگه com مربوط به project->add reference اضافه کرده‌اید:

using SQLDMO;
using System.Collections.Generic;

public static List<string> GetSQLServersList2()
{
List<string> result = new List<string>();
ApplicationClass sqlApp = new ApplicationClass();
NameList lst = sqlApp.ListAvailableSQLServers();
for (int i = 1; i <= lst.Count; i++)
result.Add(lst.Item(i));
lst = null;
sqlApp = null;

return result;

}

با منسوخ شدن این کتابخانه COM (که تنها تا اس کیوال سرور 2005 پشتیبانی می‌شود)، در نگارش‌های جدید (و قدیم) اس کیوال سرور، با استفاده از قطعه کد زیر می‌توان لیست تمام SQL server های نصب شده در یک شبکه به همراه instance های آنها را بدست آورد.

using System.Collections.Generic;
using System.Data;
using System.Data.Sql;

public class CListServers
{
public static List<string> GetSQLServersList()
{
List<string> result = new List<string>();

// Retrieve the enumerator instance and then the data.
var instance = SqlDataSourceEnumerator.Instance;
var table = instance.GetDataSources();

// Display the contents of the table.
foreach (DataRow row in table.Rows)
{
result.Add(string.Format("{0}\\{1}", row[0], row[1]));
}

return result;
}
}

راه دیگر:
کتابخانه COM یاد شده (SQL-DMO) در SQL server 2008 با کتابخانه SMO جایگزین شده است.
در این حالت خواهیم داشت:

using System.Collections.Generic;
using System.Data;
using Microsoft.SqlServer.Management.Smo;

public class CListServers
{
public static List<string> GetSQLServersListSMO()
{
List<string> result = new List<string>();
DataTable dt = SmoApplication.EnumAvailableSqlServers(false);
if (dt.Rows.Count > 0)
{
foreach (DataRow dr in dt.Rows)
{
result.Add(dr["Name"].ToString());
}
}
return result;
}
}

تقریبا کلیه اعمالی که از طریق management studio قابل انجام هستند با کمک این کتابخانه نیز از طریق برنامه نویسی می‌توان به آن‌ها پرداخت. برای مثال تهیه اسکریپت کلیه جداول ، تریگرها و غیره.

مطالب
SQL Indexing

دلیل استفاده از ایندکس چیست؟

این سوالی است که ممکن است هر توسعه دهنده‌ای به آن در ابتدا پاسخ دهد: «جهت بالابردن سرعت و کارآیی!» حال اگر بپرسیم چگونه؟ توضیحات چندان دقیقی ارائه نمی‌شود.

ایندکس چیست؟

ایندکس شیءای از دیتابیس است می‌تواند برروی یک یا چند ستون ایجاد شود (تا 16 ستون). هنگامیکه ایندکسی ایجاد می‌گردد، ساختار داده‌ای (BTree) جهت بهینه سازی عملیات مقایسه نیز ایجاد می‌شود. اس کیو ال سرور بدون داشتن ایندکس، برای دریافت اطلاعات درخواستی مجبور است کل ردیف‌های جدول را جستجو نماید. این کار مانند این است که شما بدون اطلاع از شماره صفحه (محل) عنوان درخواستی، به دنبال آن در صفحات یک کتاب باشید. حال اگر به ایندکس (فهرست) کتاب مراجعه کنید به سرعت و حداقل اتلاف وقت می‌توانید محل یا شماره صفحه‌ی عنوان مورد نظر را، بدون جستجوی کلیه‌ی صفحات کتاب، پیدا کنید و به آن مراجعه کنید. ایندکس جدول نیز اجازه می‌دهد بدون جستجوی کلیه رکوردها، رکورد مورد نظر را دریافت نمایید.
مثال:
SELECT [computer_id],[nic_device_id],[nic_vendor_id],[nic_desc]
FROM [eXpress].[dbo].[nics]

فرض کنید در جدول بالا ایندکس گذاری انجام نشده باشد و قصد داشته باشید رکوردهایی را دریافت نمایید که در آن‌ها computer_id>5100 باشد. اس کیو ال سرور مجبور است کلیه رکوردهای جدول را جهت اعمال شرط بررسی نماید.

حال، برروی ستون computer_id ایندکسی را اعمال می‌نماییم و شرط computer_id>5100 را مجدد بررسی می‌کنیم. اس کیو ال از محل رکوردهای با مقادیر بزرگتر از 5100 اطلاع دارد و از جستجوی کل جدول اجتناب می‌کند. چرا؟ بدلیل اینکه براساس این ستون مرتب شده است.

انواع ایندکس

دو نوع ایندکس اصلی وجود دارد: ایندکس خوشه‌ای و ایندکس غیرخوشه‌ای

ایندکس خوشه‌ای

نحوه‌ی ذخیره سازی فیزیکی رکوردها را تغییر می‌دهد. هنگامیکه یک ایندکس خوشه‌ای را ایجاد می‌کنید، بر روی یک ستون (یا ترکیبی از چند ستون)، اس کیو ال سرور رکوردها را براساس ستون/ها بصورت صعودی مرتب شده (مانند یک دیکشنری که کلیه کلمات بصورت الفبایی قرار گرفته‌اند) ذخیره می‌نماید.

بوسیله ایندکس زیر تمام رکوردها براساس ستون computer_id مرتب شده ذخیره می‌گردند.
CREATE CLUSTERED INDEX [IX_CLUSTERED_COMPUTER_ID] 
ON [dbo].[nics] ([computer_id] ASC)

همانطور که اشاره شد، رکوردها بصورت مرتب شده براساس ستون انتخاب شده‌ی در جدول نگهداری می‌شوند. اما این مرتب سازی توسط ساختار BTree به‌شرح زیر انجام خواهد شد. جدول زیر را در نظر داشته باشید:

فرض کنید بعد ایندکس گذاری ستون StudId جدول فوق، درخت BTree زیر ایجاد می‌گردد که این ساختار به‌صورت جداگانه‌ای بر روی دیسک ذخیره می‌گردد. در این درخت، مقدار گره سمت چپ ریشه از آن کمتر و مقدار گره سمت راست ریشه از آن بیشتر است (البته عکس این فرض نیز امکان پذیر است).

و سپس کوئری‌های زیر را صادر می‌کنید:

Select * from student where studid = 103;
Select * from student where studid = 107;
بدون ایندکس گذاری، کوئری اول، بعد از 3 عمل مقایسه و کوئری دوم بعد از 8 عمل مقایسه پیدا می‌شود.
با ایندکس گذاری، کوئری اول، بعد از اولین عمل مقایسه و کوئری دوم بعد از 3 عمل مقایسه پیدا می‌شود؛ به‌شرح زیر:
  1. مقایسه 107 با 103 و انتقال به گره سمت راست
  2. مقایسه 107 با 106 و انتقال به گره سمت راست
  3. مقایسه 107 با 107 و یافتن مقدار درخواستی و بازگشت رکورد

در صورتیکه تعداد رکوردها کم باشند، تفاوت کارآیی جداول دارای ایندکس و بدون ایندکس قابل لمس نخواهد بود. 

ایندکس غیرخوشه‌ای

این نوع ایندکس، تغییری در نحوه‌ی ذخیره سازی رکوردها انجام نمی‌دهند. ولی شیء دیگری را که شامل ستون/هایی که قرار است ایندکس شوند و اشاره‌گر به رکورد (RID) هستند، در جدول ایجاد می‌کند. برای مثالی از ایندکس غیرخوشه‌ای در دنیای واقعی، می‌توان به فهرست انتهای کتاب‌ها که شامل عناوین و شماره صفحه‌ی مربوطه می‌باشد، اشاره کرد.

نکته: RID به موقعیت فیزیکی رکورد اشاره خواهد کرد و شامل شناسه، شماره صفحه و تعداد رکوردهای در یک صفحه می‌باشد.

برای درک بهتر به سناریوی زیر دقت کنید:

کتابی داریم که شامل 1200 صفحه می‌باشد و فهرست مطالب آن شامل عناوین و شماره صفحات عناوین می‌باشد. حال اگر عنوان درخواستی A در صفحات 700، 300، 800 قرار داشته باشد، برای رفتن به این صفحات، مراحل زیر را برای هر یک طی خواهید کرد:

  1. یافتن شماره صفحه عنوان درخواستی با مراجعه به فهرست انتهای کتاب.
  2. در ادامه شما صفحه‌ای را در میانه‌ی کتاب، باز می‌کنید؛ چون عدد 700 مقداری از نصف 1200 برزگتر است.
  3. چند صفحه به جلو رفته، شماره صفحه 750 خواهد بود و هنوز به شرط مورد نظر نرسیده‌اید.
  4. پس مجددا چند صفحه به عقب بازگشته تا به صفحه‌ی مورد نظر، 700، برسید.

مراحل فوق برای یافتن عنوان A واقع شده‌ی در صفحه 700 انجام شد که همین مراحل نیز برای سایر صفحات می‌تواند انجام شود. در این مثال، صفحه فهرست مطالب کتاب،  به ایندکس غیرخوشه‌ای تعبیر خواهد شد.

این نوع ایندکس‌ها جهت ستون هایی مفید هستند که مقادیر آن تکرار خواهد شد؛ مانند جدولی با بیش از چند میلیون رکورد که دارای ستون نوع حساب است، ولی تعداد نوع حساب منحصر بفرد محدودی را خواهد داشت. فرض کنید مقادیر منحصر بفرد، ستون نوع حساب A، B، C باشد. زمانیکه برروی این ستون ایندکس گذاری غیرخوشه‌ای انجام می‌شود، فهرست ما دارای سه عنوان خواهد بود که هر عنوان به صفحات مربوط به همان عنوان اشاره خواهد کرد. به این ترتیب هنگامیکه برروی نوع حساب عملیات جستجو انجام شود، اس کیو ال می‌داند رکوردهای نوع حساب مثلا A در کدام صفحات قرار دارد و به‌سرعت رکوردهای متناظر را پیدا می‌نماید.

A: 300, 700, 800
B: 100, 110
C: 600, 1200

ایندکس غیرخوشه ای توسط دستور زیر ایجاد می‌گردد:

CREATE NONCLUSTERED INDEX [IX_NONCLUSTERED_COMPUTER_ID] 
ON [dbo].[nics] ([computer_id] ASC)

نکته: یک جدول می‌تواند بیش از یک ایندکس غیرخوشهای و فقط و فقط یک ایندکس خوشهای داشته باشد.

ارتباط ایندکس خوشه‌ای و غیر خوشه‌ای

اشاره‌گر به رکورد (RID) در یک جدول دارای ایندکس خوشه‌ای، کلید ایندکس خوشه‌ای خواهد بود.

مزایا و معایب ایندکس

مزایا:
جدولی بدون ایندکس خوشه‌ای، heap table شناخته می‌شود. یک جدول هیپ، داده‌ی مرتب شده نخواهد داشت و به منظور دریافت اطلاعات، اس کیو ال سرور مجبور است کل ردیف‌های جدول را بررسی نماید که این عملیات Scan نامیده می‌شود. ولی در صورت استفاده از ایندکس خوشه‌ای برروی یک ستون، اس کیو ال، جهت یافتن اطلاعات مورد جستجو با توجه به BTree عملیات جستجو را از ریشه شروع، از شاخه‌ها عبور کرده و به برگ که همان اطلاعات درخواستی است می‌رسد که این عملیات Seek نامیده می‌شود. عملیات Seek طبیعتا از Scan سریعتر است.
ایندکس غیرخوشه‌ای، شامل مجموعه‌ای از ستون‌ها و ارجاعاتی به رکوردها یا کلید ایندکس خوشه‌ای است (ارتباط بین ایندکس غیر خوشه‌ای با خوشه‌ای). به‌دلیل حجم کم این نوع ایندکس، می‌تواند ردیف‌ها یا کلیدهای ایندکس خوشه ای بیشتری در صفحه‌ی ایندکس وجود داشته باشد که باعث افزایش کارآیی I/O می‌گردد.

معایب:
ایندکس گذاری، در طی عملیات درج، ویرایش و حذف، باعث سربار می‌گردد. هنگامیکه تغییری بر روی رکوردهای جدول انجام می‌شود، سبب تغییراتی نیز بر روی ایندکس‌ها می‌گردد (هنگامیکه برگه‌ای از کتابی جدا شود، نیاز است شماره صفحات و فهرست انتهایی کتاب مجددا به‌روز گردد) که این تغییرات باعث ایجاد هزینه می‌شود. بنابراین خیلی اهمیت دارد که هنگام طراحی ایندکس گذاری به سربارها نیز توجه کنید. به‌عنوان مثال هنگامیکه توسط دستور Delete رکوردی را از جدولی حذف نمایید، نیاز است رکوردها مجددا مرتب شوند که این یک سربار است.
ایندکس گذاری ، سرباری بنام bookmark lookup دارد. bookmark lookup فرآیندی جهت یافتن سایر ستون‌هایی است که در ایندکس گذاری وجود ندارند و براساس RID هستند.
اشتراک‌ها
به ویژوال استودیو 2022 خوش آمدید - توسط Scott Hanselman و دوستان

Want to learn about the latest and greatest in the 64-bit Visual Studio 2022? Join Scott Hanselman and Visual Studio product team as they take Visual Studio 2022 for a spin.


00:00 Intro 
00:39 Why you should care about Visual Studio 2022? 
02:20 Performance improvements in Visual Studio 2022 
04:39 Why 64-bit now? 
08:00 IntelliCode, type less code more 
11:35 Hot reload for C++ 
13:47 New for WPF and WinForms (Hot Reload, Design time data, XAML
17:20 Hot Reload in ASP.NET 

20:27 Profiling .NET apps in Visual Studio 2022 

23:19 Cross platform apps with WSL and CMake in Visual Studio 2022 

26:07 Testing your .NET app on Linux 

28:00 Easily create CI/CD pipelines using GitHub actions with Visual Studio  2022

30:40 Balloon drop! 

به ویژوال استودیو 2022 خوش آمدید - توسط Scott Hanselman و دوستان
مطالب
بررسی کارآیی کوئری‌ها در SQL Server - قسمت دوم - جمع آوری اطلاعات آماری کوئری‌ها توسط Extended Events
همانطور که در قسمت قبل نیز بررسی کردیم، Management Studio برای جمع آوری اطلاعات آماری کوئری‌های زنده بسیار مفید است؛ اما تهیه‌ی آن دستی است. باید کوئری را اجرا کرد و سپس مراحلی را طی نمود تا به نتایج آماری حاصل از کوئری‌ها رسید و همچنین دست آخر باید از نتایج آن نیز یک خروجی دستی را تهیه کرد. روش دیگری نیز برای جمع آوری اطلاعات آماری کوئری‌ها در SQL Server توسط Extended Events/Trace وجود دارد که به ازای هر کوئری، قابل استخراج است. علاوه بر آن می‌توان از Dynamic management objects و یا Query store نیز استفاده کرد. این دو برخلاف Extended Events/Trace، اطلاعات تجمعی گروهی از کوئری‌ها را بازگشت می‌دهند. همچنین در اینجا performance monitor نیز می‌تواند مورد استفاده قرار گیرد؛ اما محدوده‌ی دید آن کل بانک اطلاعاتی است.


Extended Events/Trace

Extended Events، زیر ساخت مدیریت رخ‌دادها در SQL Server است. برای مثال در نگارش 2016 آن بیش‌از 300 رخ‌داد در SQL Server تعریف شده‌اند و زمانیکه در مورد اجرای کوئری‌ها بحث می‌کنیم، این رخ‌دادها بیشتر مدنظر ما هستند:
sql_statement_completed
sp_statement_completed
rpc_completed
sql_batch_completed
کار آن‌ها دریافت اطلاعاتی در مورد logical reads، میزان مصرف CPU، مدت زمان اجرای کوئری‌ها و امثال آن‌ها است. در این بین، دو مورد اول بیش از همه مورد استفاده قرار می‌گیرند.
علاوه بر این‌ها، رخ‌دادهای بسط یافته‌ی زیر را نیز می‌توان مورد استفاده قرار داد:
query_post_compilation_showplan
query_post_execution_showplan
query_pre_execution_showplan
اما به علت هزینه‌بر بودن تولید execution plan به ازای هر کوئری، آنچنان مورد استفاده قرار نمی‌گیرند.


استفاده از Extended Events برای جمع آوری اطلاعات آماری کوئری‌ها

برای آزمایش نحوه‌ی کار با Extended Events، ابتدا رویه‌ی ذخیره شده‌ی زیر را ایجاد می‌کنیم:
USE [WideWorldImporters];
GO

DROP PROCEDURE IF EXISTS [Application].[usp_GetCountryInfo];
GO

CREATE PROCEDURE [Application].[usp_GetCountryInfo]
    @Country_Name NVARCHAR(60)
AS

SELECT *
FROM [Application].[Countries] [c]
    JOIN [Application].[StateProvinces] [s]
    ON [s].[CountryID] = [c].[CountryID]
WHERE [c].[CountryName] = @Country_Name;
GO
این کوئری شبیه به کوئری‌است که در قسمت قبل مورد استفاده قرار گرفت؛ با این تفاوت که به همراه یک * SELECT است که استفاده‌ی از آن توصیه نمی‌شود و در اینجا بیشتر جهت بررسی کارآیی این کوئری، تعریف شده‌است.
سپس یک سشن Extended Events سفارشی را به صورت زیر ایجاد می‌کنیم:
/*
Create XE session to capture sql_statement_completed
and sp_statement_completed
*/
IF EXISTS (
SELECT *
FROM sys.server_event_sessions
WHERE [name] = 'QueryPerf')
BEGIN
    DROP EVENT SESSION [QueryPerf] ON SERVER;
END
GO

CREATE EVENT SESSION [QueryPerf] 
ON SERVER 
ADD EVENT sqlserver.sp_statement_completed(WHERE ([duration]>(1000))),
ADD EVENT sqlserver.sql_statement_completed(WHERE ([duration]>(1000)))
ADD TARGET package0.event_file(SET filename=N'C:\Temp\QueryPerf\test.xel',max_file_size=(256))
WITH (
  MAX_MEMORY=16384 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,
  MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB,
  MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=OFF,STARTUP_STATE=OFF);
GO
در این سشن، رخ‌دادهای sp_statement_completed و sql_statement_completed مورد استفاده قرار گرفته‌اند. هر کدام نیز بر اساس مدت زمان اجرای کوئری، فیلتر شده‌اند. در اینجا عدد 1000، یعنی یک میلی ثانیه که عدد بسیار کوچکی است؛ اما برای دمو، مفید است. نتیجه‌ی عملیات نیز در مسیر C:\Temp\QueryPerf ذخیره خواهد شد.

سپس نیاز است تا این سشن را که QueryPerf نام دارد، در قسمت management->extended events، اجرا و آغاز کرد:


در ادامه ابتدا بر روی بانک اطلاعاتی WideWorldImporters، کلیک راست کرده و یک پنجره‌ی new query جدید را ایجاد می‌کنیم:
WHILE 1 = 1
BEGIN
   EXECUTE [Application].[usp_GetCountryInfo] N'United States';
END
در این پنجره با یک حلقه‌ی بی‌پایان، رویه‌ی ذخیره شده‌ای را که ایجاد کردیم، بارها و بارها اجرا خواهیم کرد (نکته‌ی «عدم نمایش ردیف‌های بازگشت داده شده‌ی توسط کوئری در حین جمع آوری اطلاعات آماری» قسمت قبل را هم مدنظر داشته باشید).

سپس مجددا یک پنجره‌ی new query دیگر را باز می‌کنیم:
WHILE 1 = 1
BEGIN
    SELECT
        [s].[StateProvinceName],
        [s].[SalesTerritory],
        [s].[LatestRecordedPopulation],
        [s].[StateProvinceCode]
    FROM [Application].[Countries] [c]
        JOIN [Application].[StateProvinces] [s]
        ON [s].[CountryID] = [c].[CountryID]
    WHERE [c].[CountryName] = 'United States';
END
این کوئری شبیه به رویه‌ی ذخیره شده‌ای است که ایجاد کردیم؛ اما یک کوئری Ad Hoc و غیر پارامتری می‌باشد.

کوئری‌های هر دو پنجره را به صورت مجزایی اجرا کنید. سپس در قسمت management->extended events، بر روی سشن QueryPerf کلیک راست کرده و گزینه‌ی View live data را انتخاب کنید:


این زنده‌ترین خروجی یک سشن رخ‌دادهای بسط یافته‌است. کار کردن با آن نسبت به روشی که در قسمت قبل بررسی کردیم، ساده‌تر و سریعتر است و همچنین گزارش آن به صورت خودکار تولید می‌شود.

یک نکته: در اینجا در قسمت Details، اگر بر روی هر ردیف کلیک کنید، امکان انتخاب و نمایش آن در لیست بالای صفحه توسط گزینه‌ی Show Column in table وجود دارد.

در آخر در قسمت management->extended events، بر روی سشن QueryPerf کلیک راست کرده و گزینه‌ی Stop Session را انتخاب کنید. اکنون اگر به پوشه‌ی C:\Temp\QueryPerf مراجعه کنید، فایل xel حاوی اطلاعات این گزارش را نیز می‌توانید مشاهده نمائید (به ازای هربار اجرای این سشن، یک فایل جدید را تولید می‌کند).


 این فایل توسط Management Studio قابل گشودن و بررسی است و دقیقا همان نمای گزارش live data را به همراه دارد.
اشتراک‌ها
کنفرانس مجازی blazor day

The blazor day is the online event around Blazor technologies. Originally, this event was organized by three MVP friends, Adrien, Christophe, and Denis. Their objective of this event is to share their passion for .NET and more particularly Blazor. Joined by the famous Charline to upgrade the event to the next level. Blazor is part of the ASP.NET Core product which is offered in Open Source by Microsoft.... 

کنفرانس مجازی blazor day