PDA

View Full Version : سوال: اجرای کدها سمت سرور یا کلاینت؟



soft-c
شنبه 09 دی 1391, 14:40 عصر
سلام
از لحاظ علمی و عملی کدها ی برنامه سمت کلاینت اجرا بشوند بهتر است یا سمت سرور؟
مثلا دستورات sql را در یک ado در دلفی بنویسیم بهتر است یا در sql server بنوییسیم و با استفاده از stored procedure از آن استفاده کنیم ؟
از لحاظ سرعت و ...

hamid-nic
شنبه 09 دی 1391, 20:52 عصر
استفاده از stored procedure سمت سرور و فراخوانی آن از کلاینت .

soft-c
شنبه 09 دی 1391, 21:17 عصر
استفاده از stored procedure سمت سرور و فراخوانی آن از کلاینت .
میشه بگین علتش چیه ؟ یعنی چه تاثیراتی روی برنامه داره ؟
آیا از لحاظ سرعت می توان با برنامه یا نرم افزاری اونا تست کرد ؟
تشکر

mohsen24000
شنبه 09 دی 1391, 21:56 عصر
یکی از تاثیراتش اینه که شما دیتای کمتری رو روی شبکه به سمت سرور منتقل می کنی، پس این یعنی ترافیک کمتر!
دوم این که شما هر وقت نیاز به تغییر در کدهای SQL داشته باشی براحتی سمت سرور این کار رو انجام می دی و دیگه نیاز نیست هر دفعه برنامه رو تغییر داده و کامپایل کنی!

یوسف زالی
یک شنبه 10 دی 1391, 10:35 صبح
سلام.
این مساله به چیزهای زیادی بستگی داره. مثلا اگر پردازش داری و روی LAN هستی بهتره که با انتقال حداقلی داده ها پردازش رو در "کلاینت" انجام بدی. اما اگر روی نت هستی باید کمی محتاط تر باشی و تا حد امکان نسبت داده انتقالی به محاسبات رو پایین بیاری. یعنی ممکنه روی نت به صرفه تر باشه که پردازش هاتون در همون سرور انجام بشه.
در حالتی که در شبکه ی محلی هستید معمولا محاسبات به کلاینت ها واگذار می شه. به طور مثال محاسبات تخفیفات جوایز شرکت ما سمت اس کیو ال برای هر کلاینت در حدود 55 ثانیه طول می کشید که این بار اضافی در حالتی که چند نفر "حمله" می کردن عملا سرور رو می خوابوند اما به شبکه کاری نداشت.
بعد از اینکه محاسبات رو به سمت کلاینت انتقال دادیم زمان به 2 ثانیه رسید. این در حالی بود که به دلیل درگیر نشدن سرور در محاسبات مهم نبود چند نفر الان دارن محاسبات انجام می دن.
یک اصل کلی:
اس کیو ال برای محاسبات ریاضی سنگین نیست. سرعت Native Code ها برای این کار اصلا قابل قیاس با اس کیو ال نیست.

در هر حال،
طبق تجربیات من بهتره محاسبات سمت کلاینت باشه.
بهتره درگیری با جداول سمت سرور باشه.
بهتره با SP ها کار کنیم تا با ADOQuery ها.
بهتره فقط یک کانکشن داشته باشیم که در طول اجرا باز می مونه.
بهتره اگر حجم انتقال داده ها خیلی زیاد نیست Pivot کردن و گروه بندی های چندگانه هم سمت کلاینت باشه.
بهتره به جای ذخیره ی هر ردیف سند یک بار آخر کار به صورت XML سند ارسال بشه به سرور ...

البته این فقط نظر منه!
موفق باشید.

hossein_h62
یک شنبه 10 دی 1391, 11:23 صبح
سلام
به نظر من و همونطور که جناب یوسف خان فرمودن، تمام این موارد بستگی به ابعاد پروژه ،امکانات سخت افزاری، طراحی اصولی بانک اطلاعاتی و مواردی از ایندست داره.
مسلما در یک پروژه کم حجم و کوچک شاید اصلا تفاوت اجرا در سمت کلاینت یا سرور محسوس نباشه، ولی وقتی بحث یک سیستم یکپارچه شرکت تولیدی با N کاربر و محاسبات سنگین انبار و قیمت تمام شده و ... به میون میاد تمام جوانب باید در نظر گرفته بشه.
مثلا مشکلی که یوسف خان گفتند در شرکتشون بوده احتمالا با ارتقا سرور یا بهینه سازی کدهایی که قراره سمت سرور اجرا بشن، امکان بهبودشون بوده. البته بازم به نوع محاسبات بستگی داره.
در کل شخصا برای پروژه های متوسط رو به بزرگ ترجیح میدم از sp ها و سایر قابلیت هایی که DBMS در اختیارمون میزاره استفاده کنم.

soft-c
یک شنبه 10 دی 1391, 12:55 عصر
بهتره به جای ذخیره ی هر ردیف سند یک بار آخر کار به صورت XML سند ارسال بشه به سرور ...
میشه یک نمونه برنامه در این زمینه نشان بدید؟

یوسف زالی
یک شنبه 10 دی 1391, 13:41 عصر
حسین جان نمی شه به مشتری بگی سرور رو تعویض کن. معمولا شرکت ها راغب نیستند به خاطر برنامه ای سرور 5 - 6 میلیونی بخرند.
ضمنا محاسبات در سرور اصلا منطقی نیست. شما یک برنامه سرویس دهنده بیشتر نداری که داره با round robin (اگه اشتباه نکنم) سعی می کنه به تمام درخواست ها رسیدگی کنه.
محاسبات منظورم این نیست که مانده بگیری. این اصلا محاسبه به حساب نمیاد. فقط داده هاش زیاده.
موردی که داشتیم این بوده که می باید برای 150 قانون پارامتری روی سندی با 10 قلم کالا تمام محاسبات رو انجام بدیم.
با یک حساب سرانگشتی چیزی در حدود 12000 محاسبه بود فقط برای یک سند.
در این محاسبات ضرب و تقسیم های زیادی وجود داشت. تبدیل واحد های گوناگون، ثبت های موقت گوناگون..
اگر برای مانده گیری یا موجودی یا همچین چیزی نیاز باشه معمولا دیوانگی هست که در سرور انجام "نشه".
اما اگر برای ریالی کردن اسناد (برای روش FIFO) از روش سرور ساید استفاده کنید متوجه منظورم می شید.
بگذریم.

سافت سی عزیز:
مثالی می زنم امیدوارم متوجه بشید. چون اجازه ندارم از کدهای خودمون برات بگذارم.
شما جداول زیر رو در نظر بگیر برای سند:



InvDocs
-----------
IDC int Unchecked
CompanyIDC int Unchecked
FiscalYearIDC int Unchecked
CodeIDC int Unchecked
DateIDC char(10) Unchecked
IOC_IDC int Unchecked
INV_IDC int Unchecked
DocTypeIDC int Unchecked
StatusIDC int Unchecked
DescIDC varchar(500) Unchecked
UserIDC int Unchecked

iInvDocs
------------
IID int Unchecked
IDC_IID int Unchecked
GUD_IID int Unchecked
AmntIID decimal(18, 0) Unchecked
PriceIID decimal(18, 0) Unchecked
DescIID varchar(500) Unchecked


SP مربوطه رو من به این شکل می نویسم:



ALTER procedure [dbo].[InvDocsUpdate]
@Mode char(2), -- X = none, I = insert, U = update, D = delete, master and detail, { IX, UX, UU, DX }
@XML xml
as
declare @IXML int
declare @MSN int
declare @SN int
declare @Code int
declare @DocType int
declare @Ret int

exec sp_xml_preparedocument @IXML OUTPUT, @XML

declare @InvDocs as table
(
xml_IDC int,
xml_CompanyIDC int,
xml_FiscalYearIDC int,
xml_CodeIDC int,
xml_DateIDC char(10),
xml_IOC_IDC int,
xml_INV_IDC int,
xml_DocTypeIDC int,
xml_StatusIDC int,
xml_DescIDC varchar(500),
xml_UserIDC int
)

declare @iInvDocs as table
(
xml_IID int,
xml_IDC_IID int,
xml_GUD_IID int,
xml_AmntIID decimal(18, 0),
xml_PriceIID decimal(18, 0),
xml_DescIID varchar(500)
)

begin transaction trans1

insert into @InvDocs
select *
from OpenXML(@IXML, '/Root/InvDocs', 1)
with (
IDC int,
CompanyIDC int,
FiscalYearIDC int,
CodeIDC int,
DateIDC char(10),
IOC_IDC int,
INV_IDC int,
DocTypeIDC int,
StatusIDC int,
DescIDC varchar(500),
UserIDC int
)

insert into @iInvDocs
select *
from OpenXML(@IXML, '/Root/iInvDocs', 1)
with (
IID int,
IDC_IID int,
GUD_IID int,
AmntIID decimal(18, 0),
PriceIID decimal(18, 0),
DescIID varchar(500)
)

select @SN = xml_IDC,
@Code = xml_CodeIDC,
@DocType = xml_DocTypeIDC
from @InvDocs

-----------------------------------------------------

if @Mode = 'IX'
begin
if exists(select top 1 1 from InvDocs where CodeIDC = @Code and DocTypeIDC = @DocType)
begin
set @Ret = -1
return @Ret
end

if @Code = 0
set @Code = isnull((
select MAX(CodeIDC)
from InvDocs
where DocTypeIDC = @DocType
), 0) +1

insert into InvDocs
select
xml_CompanyIDC,
xml_FiscalYearIDC,
@Code,
xml_DateIDC,
xml_IOC_IDC,
xml_INV_IDC,
xml_DocTypeIDC,
xml_StatusIDC,
xml_DescIDC,
xml_UserIDC
from @InvDocs

set @MSN = @@IDENTITY

insert into iInvDocs
select
@MSN,
xml_GUD_IID,
xml_AmntIID,
xml_PriceIID,
xml_DescIID
from @iInvDocs

set @Ret = @MSN
end -- IX

-----------------------------------------------------

else if @Mode = 'UX'
begin
if exists(select top 1 1 from InvDocs where IDC <> @SN and CodeIDC = @Code and DocTypeIDC = @DocType)
begin
set @Ret = -1
return @Ret
end

update InvDocs
set
CompanyIDC = xml_CompanyIDC,
FiscalYearIDC = xml_FiscalYearIDC,
CodeIDC = xml_CodeIDC,
DateIDC = xml_DateIDC,
IOC_IDC = xml_IOC_IDC,
INV_IDC = xml_INV_IDC,
DocTypeIDC = xml_DocTypeIDC,
StatusIDC = xml_StatusIDC,
DescIDC = xml_DescIDC,
UserIDC = xml_UserIDC
from @InvDocs
where IDC = xml_IDC

set @Ret = @MSN
end -- UX

-----------------------------------------------------

else if @Mode = 'UU'
begin
if exists(select top 1 1 from InvDocs where IDC <> @SN and CodeIDC = @Code and DocTypeIDC = @DocType)
begin
set @Ret = -1
return @Ret
end

update InvDocs
set
@MSN = xml_IDC,
CompanyIDC = xml_CompanyIDC,
FiscalYearIDC = xml_FiscalYearIDC,
CodeIDC = xml_CodeIDC,
DateIDC = xml_DateIDC,
IOC_IDC = xml_IOC_IDC,
INV_IDC = xml_INV_IDC,
DocTypeIDC = xml_DocTypeIDC,
StatusIDC = xml_StatusIDC,
DescIDC = xml_DescIDC,
UserIDC = xml_UserIDC
from @InvDocs
where IDC = xml_IDC

update iInvDocs
set
AmntIID = xml_AmntIID,
PriceIID = xml_PriceIID,
DescIID = xml_DescIID
from @iInvDocs
where IID = xml_IID

insert into iInvDocs
select
@MSN,
xml_GUD_IID,
xml_AmntIID,
xml_PriceIID,
xml_DescIID
from @iInvDocs
where xml_IID not in (select IID from iInvDocs where IDC_IID = @MSN)

delete iInvDocs
where IDC_IID = @MSN and IID not in (select xml_IID from @iInvDocs)

set @Ret = @MSN
end -- UU

-----------------------------------------------------

else if @Mode = 'DX'
begin
select @MSN = xml_IDC
from @InvDocs

delete iInvDocs
where IDC_IID = @MSN

delete InvDocs
where IDC = @MSN

set @Ret = @MSN
end -- DX

-----------------------------------------------------

commit transaction trans1

return @MSN



و در UI با ltBatchOptimistic کردن LockType مانع این می شم که سند هام درجا وارد جدول بشه.
بعد از همه کارهای ثبت در UI و اطمینان از تمام شدن عملیات :



smInsertMasterDetail = 'IX';
smUpdateMasterOnly = 'UX';
smUpdateMasterDetail = 'UU';
smDeleteMasterDetail = 'DX';
.
.
function XMLValidate(XML: string): string;
begin
XML := StringReplace(XML, '&', '&#x26;', [rfReplaceAll]);
XML := StringReplace(XML, '<', '&#x3C;', [rfReplaceAll]);
XML := StringReplace(XML, '>', '&#x3E;', [rfReplaceAll]);
XML := StringReplace(XML, ' ', ' ', [rfReplaceAll]);
XML := StringReplace(XML, #10, '&#xA;', [rfReplaceAll]);
XML := StringReplace(XML, #13, '&#xD;', [rfReplaceAll]);
XML := StringReplace(XML, #9, '&#x9;', [rfReplaceAll]);
XML := StringReplace(XML, '''', '&#x2C;', [rfReplaceAll]);
XML := StringReplace(XML, '"', '&#x22;', [rfReplaceAll]);
Result := XML;
end;
.
.


function GetXMLFromDataSetRow(MasterTag: string; MasterDS: TDataSet; DetailTag: string = ''; DetailDS: TDataSet = nil): string;
var
i: integer;
XML: string;
begin
XML := '<Root>'^M^J;

with MasterDS do
begin
XML := XML + '<' + MasterTag + ^M^J;
for i := 0 to FieldCount -1 do
XML := XML + ' ' + Fields[i].FieldName + ' = "' + XMLValidate(trim(Fields[i].AsString)) + '"' + ^M^J;
XML := XML + '></' + MasterTag + '>' + ^M^J;
end;

if Assigned(DetailDS) then
with DetailDS do
begin
First;
while not Eof do
begin
XML := XML + '<' + DetailTag + ^M^J;
for i := 0 to FieldCount -1 do
XML := XML + ' ' + Fields[i].FieldName + ' = "' + XMLValidate(trim(Fields[i].AsString)) + '"' + ^M^J;
XML := XML + '></' + DetailTag + '>' + ^M^J;

Next;
end;
end;

XML := XML + '</Root>';

Result := XML;
end;


SpInvDocsUpdate.Parameters.ParamByName('@Mode').Va lue := smInsertMasterDetail;
SpInvDocsUpdate.Parameters.ParamByName('@XML').Val ue := GetXMLFromDataSetRow('InvDocs', SpInvDocsSelect, 'iInvDocs', SpiInvDocsSelect));
SpInvDocsUpdate.ExecProc;


برای اصلاح خود سند (مثلا برای اصلاح State سند از موقت به تایید شده) و اصلاح سند (اعم از تیتر سند یا افزودن-اصلاح-حذف ردیف های سند) و حذف تمام سند هم همین طور.
به نظر کمی پیچیده میاد اما با درکش متوجه می شی که چندان هم سخت نیست.

بنده ادعایی در این خصوص ندارم. این روش رو هم بعد از کلی آزمون و خطا بهش رسیدم. حالا اگر کسی روش بهتری داشته باشه خوشحال هم می شم با ذکر علت به ما هم آموزش بده.
ممکنه واقعا نیازی به این کارها نباشه اما پر از نکته هست. مخصوصا دلیل وجود اون Validator.

hossein_h62
یک شنبه 10 دی 1391, 14:18 عصر
یوسف عزیز منم عرض کردم که تو شرایط مختلف متفاوت هستش و نمیشه نسخه ثابتی براش پیچید، توی پروژه بزرگی درگیر بودم و هستم که کلیه محاسبات سنگین روی داده ها، سمت سرور انجام میشه و مشکل خاصی هم نداشته.
موردی که در مورد تعویض سرور گفتی رو قبول دارم ولی جایی هم سراغ دارم که برای بهبود سرعتشون براحتی 16-17 میلیون بابت سرور پول میدن.
این فقط تجربه و نظر من بود!

یوسف زالی
یک شنبه 10 دی 1391, 14:45 عصر
فکر می کنم هر دو داریم یک چیز میگیم.
محاسبات روی داده مثل join یا avg ..منظور من نیست. منظور اینه که معمولا روش های کم هزینه تر برای بهبود سرعت وجود داره.
به هر حال همون طور که گفتید هر مساله ای ممکنه با یک روش مجزا بهینه بشه.
باید موردی Performance ها چک بشه.
موفق باشید دوست عزیز.

hamid-nic
سه شنبه 12 دی 1391, 13:25 عصر
در تکمیل گفته های دوستان این موارد را هم در نظر بگیرید .
اولاً اینکه استفاده از sp ها در کل مزیت داره شک نکنید . (سوای از موارد خاص)
در زیر چندین مزیت مهم در مورد استفاده از SP به طور مختصر بحث شده (برای کسب اطلاعات بیشتر عنوان لاتین هر کدام را در نت search کنید) :

Transactions (تراکنش - معاملات)
دستورات وارد شده در SP فقط یک بار کامپایل میشه و در دفعات بعدی نیاز به کامپایل مجدد نیست .
یکی از تفاوت های کلیدی در مورد sp ها این است که دستورات sql موجود در یک sp در حوزه تراکنش ها هستند این یعنی اینکه یا همه ی دستورات اجرا میشه و یا اینکه هیچ کدام اجرا نخواهد شد .

Speed (سرعت)
بر خلاف سایر دستورات استاندارد sql ، رویه های ذخیره شده یا همان sp ها توسط سرور پایگاه داده هم کامپایل میشه و هم عملیات بهینه سازی بر روی آنها انجام میشه . البته این بهینه سازی با استفاده از اطلاعات مربوط به ساختار یک پایگاه داده خاص است که در زمان اجرای روال ذخیره شده مورد نیاز است .
به این فرایند ذخیره سازی اطلاعات طرح اجرا یا (execution plan) گفته میشود که در بهینه سازی زمان نقش فوق العادی دارد .

Process Control (کنترل فرایندها)
یک رویه ذخیره شده (sp) می تواند شامل دستوراتی همچون IF ....ELSE و حلقه های FOR و WHILE باشد که به طور معمول در عبارات ساده یک SELECT در دسترس نخواهد بود. که این ویژگی ما را قادر می سازد به بعضی از عملیات های بسیار پیچیده منطقی از طریق کد نویسی رسیدگی کنیم .
بدون sp ها ما باید یک شی در لایه ی داده ایجاد کنیم که مسئولیت رسیدگی به حلقه ها را بر عهده بگیرد که این کار به دلیل نیاز به پردازش رکورد ها ترافیک زیادی از شبکه را شامل میشود همچنین ما را با مشکلاتی بعدی رو به رو می کند .

Security (امنیت)
sp ها نیز می توانند به عنوان یک لایه امنیتی اضافی عمل کنند. به طور مثال ما می توانیم یک sp ای که حقوق و دستمزد یک شرکت را محاسبه می کند تولید کنیم در حالی که به کاربران اجازه ی دسترسی مستقیم به اطلاعات را به هیچ وجه ندهیم . در واقع sp ها به کاربران اجازه ی دسترسی مستقیم به داده ها را نمی دهد .
ارائه یک محیط پایگاه داده امن در یک برنامه تحت وب یک نکته ی بسیار مهم به حساب می آید . از آن جایی که وب سرور ها یک رابطه ی مناسبی برای هکر ها و افرادی که دوست دارند به صورت مستقیم به داده ها دسترسی داشته باشند فراهم می کند لذا استفاده از SP ها کاهش چشمگیری در جلوگیری از سرقت مستقیم اطلاعات خواهد داشت .

Reduced Network Traffic (کاهش ترافیک شبکه)
استفاده از sp ها ، برنامه های کلاینت را قادر می سازد که با پاس دادن اطلاعات خیلی محدود به سرور اطلاعات مورد نیاز را از سرور بگیرند در واقع از رد و بدل کردن اطلاعات غیر ضروری در سراسر شبکه جلوگیری میشه . که این مورد در شبکه های بزرگ و دارای کلاینت ها متعدد نقش به سزایی در کنترل ترافیک شبکه خواهند داشت .

Modularization (مدولار کردن ، پیمانه)
پیمانه یا مدولار کردن کد یک جنبه ی کلیدی است که توسط SP ها قابل پیاده سازی است . در واقع فرآیند نوشتن کد ها که قابلیت استفاده مجدد دارند .


موفق باشید ...

یوسف زالی
سه شنبه 12 دی 1391, 14:49 عصر
این یعنی اینکه یا همه ی دستورات اجرا میشه و یا اینکه هیچ کدام اجرا نخواهد شد .

دستورات در SP به خودی خود تراکنش نیستند. می شه در SP تراکنش هم نوشت.

hamid-nic
سه شنبه 12 دی 1391, 21:57 عصر
دستورات در SP به خودی خود تراکنش نیستند. می شه در SP تراکنش هم نوشت.
منظور اون دستور TRANSACTION در Tsql نیست !!!!!!!!
این نوع درک مطلب از شما دوست عزیز بعیده !!!
یعنی اگر به هر نحوی خطایی در اجرای یک sp که مثلا شامل 100 خط است و این خطا در خط های اولیه اتفاق بیفتد سایر دستورات بعد از این خط هم اجرا نخواهد شد . در کل sp هیچ خروجی نخواهد داشت .

hossein_h62
چهارشنبه 13 دی 1391, 07:47 صبح
یعنی اگر به هر نحوی خطایی در اجرای یک sp که مثلا شامل 100 خط است و این خطا در خط های اولیه اتفاق بیفتد سایر دستورات بعد از این خط هم اجرا نخواهد شد . در کل sp هیچ خروجی نخواهد داشت .
حرف شما درسته . ولی اگر از بلاک TRANSACTION در sp استفاده نشه، عملیاتی که قبل از خطا انجام شده ROLLBACK نمیشن! فکر میکنم منظور آقا یوسف هم همین بود.

یوسف زالی
چهارشنبه 13 دی 1391, 12:05 عصر
فکر می کنم مناقشه ای تو مثالمون درست شده.
من منظور شما رو درست فهمیدم. اون متن اصلاحیه رو برای دوستانی نوشتم که ممکن بود از گنگ بودن متن شما دچار اشتباه بشن.
ذات SP تراکنش نیست. همونطور که خودتون می دونید و حسین آقا هم اشاره کردند اجرای SP مثل اجرای یک کوئری در قسمت New Query SQL Management هست و نه بیشتر.
البته باز هم منظور من از این متن این نیست که مثل New Query هربار دوباره کامپایل می شه.
منظورم خواص اون در اجراست.
خوبی هایی که شما برای SP اشاره کردید درست هست.
یک مورد رو هم من علاوه می کنم:

در SP دست شما بازه که رفتار برنامه UI خودتون رو بدون تغییر در EXE عوض کنید.
مثلا می تونید گزارشاتتون رو اصلاح کنید یا در مواردی از اون تغییراتی بدید.
این برای پشتیبانی نرم افزار دست ما رو خیلی باز می گذاره. مخصوصا وقتی ریموت هستیم یا در سایت مشتری هستیم و دستمان به سورس نمی رسه.
البته ممکنه این مورد در برخی اوقات ضعف هم تفسیر بشه. بستگی داره به این که طراحی سیستمتون برچه نیاز هایی استوار باشه.



این نوع درک مطلب از شما دوست عزیز بعیده !!!


همه ما انسان هستیم و ممکنه من هم در درک مطلب اشتباه کرده باشم.

hamid-nic
چهارشنبه 13 دی 1391, 13:52 عصر
همه ما انسان هستیم و ممکنه من هم در درک مطلب اشتباه کرده باشم.
خواهش می کنم شما بزرگوارید دوست عزیز.