به رسم هرساله، سایت معتبر Db-Engines از بین بیش از سیصد بانک اطلاعاتی موجود دنیا، دیتابیسی که در سال گذشته بیشترین محبوبیت را در بین جامعه توسعه دهندگان و مهندسان داده داشته است در ابتدای سال جدید میلادی معرفی کرد.
هیات دولت در جلسه خود به ریاست حجتالاسلام والمسلمین حسن روحانی، به ادامه بررسی پیشنهادهای کارگروه منتخب درخصوص «لایحه بانک مرکزی جمهوری اسلامی ایران» پرداخت و مواد دیگری از این لایحه را به تصویب رساند.
به گزارش ایسنا، براساس موادی از این لایحه که به تصویب هیأت وزیران رسید، واحد پول ایران، تومان و برابر با 10 ریال تعیین شد. تغییر واحد پولی ایران به تومان، باید به تصویب مجلس شورای اسلامی و تأیید شورای نگهبان نیز برسد.
طراحی روابط و ارجاعات در RavenDB
مدیریت روابط در RavenDB
یکی از اصول طراحی مدلها در RavenDB، مستقل بودن اسناد یا documents است. به این ترتیب کلیه اطلاعاتی که یک سند نیاز دارد، داخل همان سند ذخیره میشوند (به این نوع شیء، Root Aggregate هم گفته میشود). اما این اصل سبب نخواهد شد تا نتوان یا نباید ارتباطی را بین اسناد تعریف کرد. بنابراین سؤال مهم اینجا است که چه اطلاعات مرتبطی باید داخل یک سند ذخیره شوند و چه اطلاعاتی باید به سند دیگری ارجاع داده شوند. برای پاسخ به این سؤال سه روش ذیل را باید مدنظر داشت:
الف) Denormalized references
فرض کنید در دنیای رابطهای دو جدول سفارش و مشتری را دارید. در این حالت، جدول سفارش تنها شماره آی دی اطلاعات مشتری را از جدول مشتری یا کاربران سیستم، در خود ذخیره خواهد کرد. به این ترتیب از تکرار اطلاعات مشتری در جدول سفارشات جلوگیری میگردد. اما اگر اطلاعات پرکاربرد مشتری را در داخل جدول سفارش قرار دهیم به آن denormalized reference گفته میشود.
ایجاد denormalized reference یکی از روشهای مرسوم در دنیای NoSQL و RavenDB است؛ خصوصا جهت سهولت نمایش اطلاعات. به این ترتیب ارجاع به سندهای دیگر کمتر شده و ترافیک شبکه نیز کاهش مییابد. برای مثال در اینجا نام و آدرس مشتری را داخل سند ثبت شده قرار میدهیم و از سایر اطلاعات او (که اهمیت نمایشی ندارند) مانند کلمه عبور و امثال آن صرفنظر خواهیم کرد.
اینجا است که یک سری از سؤالات مطرح خواهند شد مانند : «اگر آدرس مشتری تغییر کرد، چطور؟»
بنابراین بهترین حالت استفاده از روش denormalized references محدود خواهد شد به موارد ذیل:
الف) قید اطلاعاتی که به ندرت تغییر میکنند. برای مثال نام یک شخص یا نام یک کشور، استان یا شهر.
ب) ثبت اطلاعات تکراری که در طول زمان تغییر میکنند، اما باید تاریخچهی آنها حفظ شوند. برای مثال اگر آدرس مشتری تغییر کرده است، واقعا اجناس سندهای قبلی او، صرفنظر از آدرس جدیدی که اعلام کرده است، به آدرس قبلی او ارسال شدهاند و این تاریخچه باید در سیستم حفظ شوند.
ج) اطلاعاتی که ممکن است بعدها حذف شوند؛ اما نیاز است سابقه اسناد قبلی تخریب نشوند. برای مثال کارخانهای را درنظر بگیرید که امسال یک سری چینی خاص را تولید میکند و میفروشد. سال بعد خط تولید خود را عوض کرده و سری اجناس دیگری را شروع به تولید و فروش خواهد کرد. در بانکهای اطلاعاتی رابطهای نمیتوان اجناسی را که در جداول دیگر ارجاع دارند، به این سادگیها حذف کرد. در اینجا باید از روشهایی مانند تعریف فیلد بیتی IsDeleted برای مخفی کردن ظاهری رکوردهای موجود کمک گرفت. اما در دنیای رابطهای، اطلاعات مهم محصول را در سند اصلی ثبت کنید. بعد هر زمانیکه نیازی به محصول نبود، کلا تعریف آنرا حذف نمائید.
ب) Includes
Includes در RavenDB برای پوشش مشکلات denormalization ارائه شده است. در اینجا بجای اینکه یک شیء کپی اطلاعات پرکاربرد شیءایی دیگر را در خود ذخیره کند، تنها ارجاعی (یک Id رشتهای) از آن شیء را در سند مرتبط ذخیره خواهد کرد.
public class Order { public string CustomerId { get; set; } public LineItem[] LineItems { get; set; } public double TotalPrice { get; set; } } public class Customer { public string Name { get; set; } public string Address { get; set; } public short Age { get; set; } public string HashedPassword { get; set; } }
var order = session.Include<Order>(x => x.CustomerId) .Load("orders/1234"); // این کوئری از کش سشن خوانده میشود و کاری به سرور ندارد var cust = session.Load<Customer>(order.CustomerId);
حتی میتوان چند سند مرتبط را با هم بارگذاری کرد؛ با حداقل رفت و برگشت به سرور:
var orders = session.Include<Order>(x => x.CustomerId) .Load("orders/1234", "orders/4321"); foreach (var order in orders) { // این کوئریها سمت کلاینت هستند و به سرور ارسال نمیشوند var cust = session.Load<Customer>(order.CustomerId); }
var orders = session.Query<Order>() .Customize(x => x.Include<Order>(o => o.CustomerId)) .Where(x => x.TotalPrice > 100) .ToList(); foreach (var order in orders) { // این کوئریها سمت کلاینت اجرا میشوند var cust = session.Load<Customer>(order.CustomerId); }
Includeهای یک به چند
اکنون فرض کنید به کلاس سفارش، آرایه تامین کنندهها نیز افزوده شده است (رابطه یک به چند):
public class Order { public string CustomerId { get; set; } public string[] SupplierIds { get; set; } public LineItem[] LineItems { get; set; } public double TotalPrice { get; set; } }
var orders = session.Include<Order>(x => x.SupplierIds) .Load("orders/1234", "orders/4321"); foreach (var order in orders) { foreach (var supplierId in order.SupplierIds) { // از کش سشن خوانده میشود var supp = session.Load<Supplier>(supplierId); } }
Includeهای چند سطحی
در اینجا کلاس سفارشی را در نظر بگیرید که دارای خاصیت ارجاع دهنده نیز هست. این خاصیت به شکل یک کلاس تعریف شده است و نه به شکل یک آی دی رشتهای:
public class Order { public string CustomerId { get; set; } public string[] SupplierIds { get; set; } public Referral Refferal { get; set; } public LineItem[] LineItems { get; set; } public double TotalPrice { get; set; } } public class Referral { public string CustomerId { get; set; } public double CommissionPercentage { get; set; } }
var order = session.Include<Order>(x => x.Refferal.CustomerId) .Load("orders/1234"); // از کش سشن خوانده میشود var referrer = session.Load<Customer>(order.Refferal.CustomerId);
public class LineItem { public string ProductId { get; set; } public string Name { get; set; } public int Quantity { get; set; } public double Price { get; set; } }
var order = session.Include<Order>(x => x.LineItems.Select(li => li.ProductId)) .Load("orders/1234"); foreach (var lineItem in order.LineItems) { // از کش سمت کلاینت خوانده میشود var product = session.Load<Product>(lineItem.ProductId); }
و به صورت خلاصه برای باگذاری اسناد مرتبط، دیگر از دو کوئری پشت سر هم ذیل استفاده نکنید:
var order = session.Load<Order>("orders/1"); var customer = session.Load<Customer>(order.CustomerId);
ج) تفاوت بین Reference و Relationship
برای درک اینکه آیا اطلاعات یک شیء مرتبط را بهتر است داخل شیء اصلی (Aggregate rooe) ذخیره کرد یا خیر، باید مفاهیم ارجاع و ارتباط را بررسی کنیم.
اگر به مثال سفارش و مشتری دقت کنیم، یک سفارش را بدون مشتری نیز میتوان تکمیل کرد. برای مثال بسیاری از فروشگاهها به همین نحو عمل میکنند و اگر شماره Id مشتری را به سندی اضافه میکنیم، صرفا جهت این است که بدانیم این سند متعلق به شخص دیگری نیست. بنابراین «ارجاعی» به کاربر در جدول سفارش میتواند وجود داشته باشد.
اکنون اقلام سفارش را درنظر بگیرید. هر آیتم سفارش تنها با بودن آن سفارش خاص است که معنا پیدا میکنند و نه بدون آن. این آیتم میتواند ارجاعی به محصول مرتبط داشته باشد. اینجا است که میگوییم اقلام سند با سفارش «در ارتباط» هستند؛ اما یک سند ارجاعی دارد به مشتری.
از این دو مفهوم برای تشخیص تشکیل Root Aggregate استفاده میشود. به این ترتیب تشخیص دادهایم اقلام سند، Root Aggregate را تشکیل میدهند؛ بنابراین ذخیره سازی تمام آنها داخل یک سند RavenDB معنا پیدا میکند.
چند مثال برای درک بهتر نحوه طراحی اسناد در RavenDB
الف) Stackoverflow
صفحه نمایش یک سؤال و پاسخهای آن و همچنین رایهای هر آیتم را درنظر بگیرید. در اینجا کاربران همزمانی ممکن است به یک سؤال رای بدهند، پاسخهایی را ارائه دهند و یا کاربر اصلی، سؤال خویش را ویرایش کند. به این ترتیب با قرار دادن کلیه آیتمهای این سند داخل آن، به مشکلات همزمانی برخواهیم خورد. برای مثال واقعا نمیخواهیم که به علت افزوده شدن یک پاسخ، کل سند قفل شود.
بنابراین ذخیره سازی سؤال در یک سند و ذخیره سازی لیست پاسخها در سندی دیگر، طراحی بهتری خواهد بود.
ب) سبد خرید و آیتمهای آن
زمانیکه کاربری مشغول به خرید آنلاین از سایتی میشود، لیست اقلام انتخابی او یک سفارش را تشکیل داده و به تنهایی معنا پیدا نمیکنند. به همین جهت ذخیره سازی اقلام سفارش به صورت یک Root aggregate در اینجا مفهوم داشته و متداول است.
ج) یک بلاگ و کامنتهای آن
در اینجا نیز کاربران، مجزای از مطلب اصلی ارسال شده ممکن است نظرات خود را ویرایش کنند یا اینکه بخواهیم نظرات را جداگانه لیست کنیم. بنابراین این دو (مطالب و نظرات) موضوعاتی جداگانه بوده و نیازی نیست به صورت یک Root aggregate تعریف شوند.
بنابراین در حین طراحی اسناد NoSQL باید به اعمال و «محدودههای تراکنشی» انجام شده دقت داشت تا اینکه صرفا عنوان شود این یک رابطه یک به چند یا چند به چند است.
داستانی از Unicode
<html> <body> <style type="text/css">p {float: left; padding: 0 15px; margin: 0;}</style> <script type="text/javascript"> for (var i=0; i<128; i++) document.writeln ((i%32?'':'<p>') + i + ': ' + String.fromCharCode (i) + '<br>'); </script> </body> </html>
private String ISO = "ISO-8859-"; protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { for (int i = 1; i < 16; i++) { ListItem item = new ListItem(); item.Text = ISO + i.ToString(); item.Value = i.ToString(); DropDownList1.Items.Add(item); } ShowCodes(1); } } protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e) { if (DropDownList1.SelectedItem != null) { int value = int.Parse(DropDownList1.SelectedValue); ShowCodes(value); } } private void ShowCodes(int value) { Response.Charset = ISO + value; string s = ""; for (int i = 0; i < 256; i++) { char ch = (char)i; s += i + "-" + ch; s += "<br/>";//br tag } Label1.Text = s; }
کد زیر در جاوااسکریپت کاراکترهای یونیکد را در مرز معینی که برایش مشخص کردهایم نشان میدهد:
<html> <body> <style type="text/css">p {float: left; padding: 0 15px; margin: 0;}</style> <script type="text/javascript"> for (var i=0; i<2096; i++) document.writeln ((i%256?'':'<p>') + i + ': ' + String.fromCharCode (i) + '<br>'); </script> </body> </html>
CSS & Unicode
/* cyrillic */ @font-face { font-style: normal; src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/mErvLBYg_cXG3rLvUsKT_fesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { font-style: normal; src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/-2n2p-_Y08sg57CNWQfKNvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+1F00-1FFF; } /* greek */ @font-face { font-style: normal; src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/u0TOpm082MNkS5K0Q4rhqvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+0370-03FF; } /* vietnamese */ @font-face { font-style: normal; src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/NdF9MtnOpLzo-noMoG0miPesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+0102-0103, U+1EA0-1EF1, U+20AB; } /* latin-ext */ @font-face { font-style: normal; src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/Fcx7Wwv8OzT71A3E1XOAjvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; }
UTF-8 نجات بخش میشود
- بسیاری از نرم افزارها و پروتکلها هنوز 8 بیتی کار میکنند.
- اگر یک متن انگلیسی ارسال کنید، 8 بیت هم کافی است ولی در این حالت 32 بیت جابجا میشود؛ یعنی 4 برابر و در ارسال و دریافت و پهنای باند برایمان مشکل ایجاد میکند.
مقایسهای بین نسخههای مختلف :
همانطور که میبینید UTF-8 برای کاراکترهای اسکی، از یک بایت و برای دیگر حروف از دوبایت و برای بقیه BMPها از سه بایت استفاده میکند و در صورتی که کاراکتری در ناحیه مکمل supplementary باشد، از چهار بایت استفاده خواهد کرد. UTF-16 از دو بایت برای نمایش کاراکترهای BMP و از 4 بایت برای نمایش کاراکترهای مکمل استفاده میکند و در UTF-32 از 4 بایت برای همه کاراکترها یا کد پوینتها استفاده میشود.
من هم با جناب نصیری موافقم که ادامه چنین کاری نیاز به فضای متقابل و طبیعتا دلگرمی و انگیزه داره؛ اما صحبت اینجاست که «من اگر بنشینم، تو اگر بنشینی، چه کسی برخیزد؟». فکر میکنم به گفته Hossein Raziee باید یه حرکتی بهرحال انجام داد؛ اینطور که من برداشت کردم، فکر میکنم دو تا موضوع وبلاگ نویسی IT در ایران رو تحت الشعاع قرار داده: یکی گرایش به تالارهاست. و دومی گرایش به بلاگ های خارجی. اکثر برنامه نویسای قوی که دست به قلم هستن، توی تالارها فعالیت میکنن؛ ظاهرا اهمیت وبلاگ ها هنوز برای ما جا نیفتاده. دوستانی هم که یه مقداری از مشق و تمرین بالاتر میرن، معمولا نه تنها خودشون شروع به انتقال نمیکنن، بلکه حتی برای ارتقای خودشون و پیگیری مطالب سراغ بلاگ های غیر فارسی میرن؛ فکر میکنم نیاز به یه حرکت اصولی و درست و حساب شده داریم تا موضوع تولید محتوی IT به زبان فارسی رو تو یه مسیر درست بندازیم. یکی از راههایی که به ذهن من میرسید، ایجاد یه شبکه بین نویسنده های ایرانی بود؛ حدود یه سال پیش این موضوع رو با چند تا از دوستان اینترنتیم مطرح کردم که استقبال نشد. حالا دوباره از دوستان میپرسم که نظرشون چیه؟
نمونه بسیار مفید (ولی تنها یه شمّه) خلاصه اشتراک هایی هست که جناب نصیری منتشر میکنن؛ وقتی یه نفر مثل وحید نصیری لینکی رو منتشر میکنه من مطمئنم که مطلب مفیدی هست؛ پس حتما میرم و میخونم؛ و چه بسا جزو خوانندگان دائمی اون سایت هم بشم؛ حالا عرض بنده اینه که این موضوع باید منسجم و شبکه ای باشه؛
با جناب نصیری موافقم؛ با شاهین هم موافقم؛ اما موافق این نیستم که آقای نصیری بگن میخوام بکشم کنار و من و شاهین کیاست هم بگیم نه اینکارو نکنید؛ چون وحید نصیری شاید امروز احساساتی بشه و بخاطر من و امثال من ادامه بده، اما فردا دوباره بی انگیزگی یا خستگی میاد سراغش و باز همین داستان؛
من فکر میکنم باید راه چاره رو پیدا کرد؛ راهی که به نظر من میرسید رو عرض کردم؛ از دوستان که تا جاییکه من میشناسم و مطالب رو دنبال میکنم، میدونم همشون از افراد تاثیر گذار در IT ایران و با دانش و تجربه زیاد هستن هم خواهش میکنم که هر راهی به ذهنشون میرسه عنوان کنن که با همفکری هم بتونیم به یه راه حل درست برسیم؛
به سهم خودم هم از جناب نصیری عمیقا تشکر میکنم و از پرحرفی و زیاده گوییم پوزش میخوام. پاینده باشید؛
شرکت در وبینار شرکت تلریک
شرکت تلریک برای دوم سپتامبر در ساعت 7:30 دقیقه بعد از ظهر به وقت ایران وبیناری را ارائه میکند که از همین الان میتوانید در آن ثبت نام کنید. موضوع وبینار در مورد محصول test studio جهت بررسی و برطرف کردن مشکلات Kendo UI در حین کار است. مدرس این وبینار آقای جاش بریستو یکی از مدیران تلریک در استرالیا و یکی از مدیران سابق شرکت مایکروسافت است.
- این وصله فقط برای غیر فعال کردن تغییر ساعت سال بعد (1401) میباشد؟
- بعد از اعمال کردن این وصله راهی وجود داره که نیاز به ریستارت کردن سرور نباشه؟