سوف أصف بإيجاز كيف تم تنظيم استنساخ قاعدة البيانات (إنشاء العديد من مثيلات قاعدة البيانات من نسخة احتياطية واحدة) على المشروع الحالي. تسمح الطريقة بتوفير الوقت ومساحة القرص الثابت.
الوضع: هناك قاعدة بيانات سميكة (على سبيل المثال ، مائة جيجابايت). أرغب في الحصول على قاعدة البيانات هذه مع جميع البيانات بشكل منفصل لكل مطور وعدم إنفاق قرص تيرابايت عليها. التالي حل MSSQL للنوافذ باستخدام بوويرشيل.
جئت عبر الأداة المساعدة Redgate SQL Clone. يحتوي الموقع على
وصف لكيفية عمله . خلاصة القول هي استخدام شيء من هذا القبيل: التفريق بين القرص الصلب الظاهري. يُترجم هذا إلى اللغة الروسية كـ "قرص افتراضي تفاضلي" - قرص يتم تخزين الفارق عليه فيما يتعلق بالقرص "الأصل" فقط.
التفاصيل تحت خفض
مخطط العمل كما يلي:
- نقوم بإنشاء وتوصيل القرص الظاهري المعتاد (سيصبح لاحقًا الأصل).
- نقوم بإنشاء مثيل قاعدة بيانات واحد سيتم من خلاله إجراء النسخ. نقوم بتنظيف البيانات المؤيدة ، وإعداد قاعدة البيانات بالكامل للعمل في بيئة اختبار. نضع ملفات قاعدة البيانات على القرص الظاهري.
- افصل قاعدة البيانات من الخادم. نحن قطع القرص الظاهري.
- إنشاء قرص التفاضلية. نحن نتصل بالنظام نقوم بتوصيل قاعدة البيانات من هذا القرص إلى خادم sql.
- كرر الخطوة 4 حتى يتم تحقيق عدد متناغم من قواعد البيانات.
إنشاء قرص أصل لن يتم وصفه ، لأنه يمكن القيام بذلك يدويًا من خلال الواجهة الرسومية لإدارة الأقراص. حسنا ، أو أوامر جوجل واستكمال البرامج النصية الواردة في المقال.
ملاحظة مرات:
في نظامي Windows 10 و windows server 2016 ، يوجد بوويرشيل commandlet
New-VHD . بالنسبة لأولئك الذين يستخدمون الإصدارات السابقة من الخادم ، هناك أداة مساعدة من نوع diskpart. أتمتة العمل معها ليست مريحة للغاية ، لأنه عند الإدخال ، يتلقى ملفًا به أوامر للتنفيذ.
الملاحظة الثانية:
بسبب نظرًا لوضع ملفات قاعدة البيانات على القرص differencing ، فإن أداء مثل هذا الحل بعيد عن أن يكون ممتازًا. اتضح عدة مستويات من عدم الاتصال المباشر: ينتقل السجل إلى قاعدة البيانات ، التي تقع على قرص افتراضي يخزن الفرق في المنزل الذي بناه جاك. ليس لدي أرقام أداء محددة (لأن هذا ليس السؤال الأول على حلبة الاختبار لدينا). سأكون ممتناً لو قام أحدهم بقياس مقدار سرعة الكتابة / القراءة.
الملاحظة الثالثة:
بسبب البرامج النصية لم تكن مخصصة للاستخدام على نطاق واسع ويتم توفيرها فقط على سبيل المثال ، هناك انحناء متزايد وملزم مشدود لـ MSSQL.
سنقوم بتهيئة بعض المتغيرات:$server = "server"; $db_file_name = "db_file_name"; $root_path = "path to folder with disks"; $cred = try { Get-StoredCredential -Target "$server\Administrator"; } catch { Get-Credential -Message "server windows user" -UserName "$server\Administrator" } $db_cred = $(try { Get-StoredCredential -Target "$server\sa"; } catch { Get-Credential -Message "sql server user" -UserName "sa" }).GetNetworkCredential(); $session = New-PSSession -ComputerName $server -Credential $cred;
بسبب يتم تشغيل البرنامج النصي على جهاز المطور ، ويتم تنفيذ جميع الإجراءات على الجهاز مع خادم sql ، من المفترض أن يتم تكوين الاتصال عن بعد powerhell. يتم تنفيذ جميع الأوامر في جلسة مفتوحة.
Get-StoredCredential هو عبارة عن سطر أوامر لحفظ بيانات الاعتماد على الجهاز المحلي (مثبت بشكل منفصل). من حيث المبدأ ، يمكنك الاستغناء عنه ، ولهذا السبب يتم لفه في try / catch.
التالي هو رمز تنفيذ البرنامج النصي diskpart: function run_script([string]$script, [bool]$suppress_output = $false) { $result = Invoke-Command -Session $session -ArgumentList $script -ScriptBlock { param($script) $script.Split("`r`n") | % { Write-Host $_.Trim() }; Out-File -FilePath "tmp" -InputObject $script -Encoding ascii return diskpart /s "tmp" } if($suppress_output) { return $result; } else { $result | ? { !$_.Contains("Microsoft") -and $_ -ne "" } | Write-Host } }
أوامر Sql التي أقوم بتنفيذها عبر SQLCMD: function run_sql([string]$sql) { Write-Host $sql SQLCMD -S $server -d master -U $($db_cred.UserName) -P $($db_cred.Password) -Q $sql }
إنشاء قرص مختلف: run_script "create vdisk file=`"$root_path\$name.vhdx`" parent=`"$root_path\parent_disk.vhdx`""
بعد ذلك ، قم بتوصيل القرص وقاعدة البيانات: $disk_letter = Invoke-Command -Session $session -ScriptBlock { ls function:[dz]: -n | ?{ !(test-path $_) } | select -Last 1; } $volumes = run_script "list volume" $true $disks = run_script "list disk" $true $script = " sel vdisk file=`"$current_path\$db_name.vhdx`" attach vdisk"; run_script $script; $disks_after = run_script "list disk" $true $new_disk = $($disks_after | ? { $_ -notin $disks } ) Write-Host $new_disk $new_disk -match "\d+" $diskId = $Matches[0] $script = " select disk $diskId online disk"; run_script $script $volumes_after = run_script "list volume" $true # get added disk $new_volume = $($volumes_after | ? { $_ -notin $volumes } ) Write-Host $new_volume $new_volume -match "\d+" $volumeId = $Matches[0] $script = " select volume $volumeId assign letter=$disk_letter"; run_script $script run_script "list volume"; run_script "list vdisk"; $atach_script = "CREATE DATABASE $db_name ON (FILENAME = '$disk_letter\$db_file_name.mdf'),(FILENAME = '$disk_letter\$db_file_name.ldf') FOR ATTACH"; run_sql "$atach_script"
هذه القطعة من "ls function: [dz]: -n" هي مجرد نوع من السحر للحصول على قائمة من أحرف محركات الأقراص. كيف يعمل - أي فكرة ، نسخ من stackoverflow.
في التعليمة البرمجية أعلاه ، فإن أكبر صعوبة هي الحصول على القرص الظاهري الناتج ووضعه على حرف معين. انه يحتاج أيضا إلى القيام به على الانترنت مسبقا.
فصل محرك الأقراص أسهل قليلاً: run_sql " ALTER DATABASE $name SET OFFLINE WITH ROLLBACK IMMEDIATE GO sp_detach_db $name"; $script = "select vdisk file=`"$root_path\$name.vhdx`" detach vdisk "; run_script $script
وضع كل ذلك معا: param( [ValidateSet("detach_all", "attach_all_available", "create_new", "attach_db", "detach_db", "remove_file")][Parameter(mandatory=$true)][string] $mode, [string] $name ) function run_sql([string]$sql) { Write-Host $sql SQLCMD -S $server -d master -U $($db_cred.UserName) -P $($db_cred.Password) -Q $sql } function run_script([string]$script, [bool]$suppress_output = $false) { $result = Invoke-Command -Session $session -ArgumentList $script -ScriptBlock { param($script) $script.Split("`r`n") | % { Write-Host $_.Trim() }; Out-File -FilePath "tmp" -InputObject $script -Encoding ascii return diskpart /s "tmp" } if($suppress_output) { return $result; } else { $result | ? { !$_.Contains("Microsoft") -and $_ -ne "" } | Write-Host } } function attach_disk([string]$db_name, [string]$current_path) { $disk_letter = Invoke-Command -Session $session -ScriptBlock { ls function:[dz]: -n | ?{ !(test-path $_) } | select -Last 1; } $volumes = run_script "list volume" $true $disks = run_script "list disk" $true $script = " sel vdisk file=`"$current_path\$db_name.vhdx`" attach vdisk"; run_script $script; $disks_after = run_script "list disk" $true $new_disk = $($disks_after | ? { $_ -notin $disks } ) Write-Host $new_disk $new_disk -match "\d+" $diskId = $Matches[0] $script = " select disk $diskId online disk"; run_script $script $volumes_after = run_script "list volume" $true # get added disk $new_volume = $($volumes_after | ? { $_ -notin $volumes } ) Write-Host $new_volume $new_volume -match "\d+" $volumeId = $Matches[0] $script = " select volume $volumeId assign letter=$disk_letter"; run_script $script run_script "list volume"; run_script "list vdisk"; $atach_script = "CREATE DATABASE $db_name ON (FILENAME = '$disk_letter\$db_file_name.mdf'),(FILENAME = '$disk_letter\$db_file_name.ldf') FOR ATTACH"; run_sql "$atach_script" } $server = "server"; $db_file_name = "db_file_name"; $cred = try { Get-StoredCredential -Target "$server\Administrator"; } catch { Get-Credential -Message "server windows user" -UserName "$server\Administrator" } $db_cred = $(try { Get-StoredCredential -Target "$server\sa"; } catch { Get-Credential -Message "sql server user" -UserName "sa" }).GetNetworkCredential(); $session = New-PSSession -ComputerName $server -Credential $cred; $root_path = "path to folder with disks"; $files = Invoke-Command -Session $session -ArgumentList $root_path -ScriptBlock { param($root_path) Get-ChildItem -Filter "*.vhdx" -Path $root_path } switch ($mode) { "detach_all" { $files ` | % { Write-Host $("*"*40) `r`n $_.FullName `r`n; $_ } ` | % { " ALTER DATABASE $($_.Name.Replace('.vhdx', '')) SET OFFLINE WITH ROLLBACK IMMEDIATE GO sp_detach_db $($_.Name.Replace('.vhdx', ''))" } ` | % { run_sql "$_" } $files ` | % { Write-Host $("*"*40) `r`n $_.FullName `r`n; $_ } ` | % { run_script "select vdisk file=`"$($_.FullName)`" detach vdisk " } break; } "attach_all_available" { $files | % { $_.Name.Replace('.vhdx', '') } | ? { $_ -ne "parent_disk" } | % { attach_disk $_ $root_path } break; } "attach_db" { attach_disk $name $root_path break; } "detach_db" { run_sql " ALTER DATABASE $name SET OFFLINE WITH ROLLBACK IMMEDIATE GO sp_detach_db $name"; $script = "select vdisk file=`"$root_path\$name.vhdx`" detach vdisk "; run_script $script break; } "create_new" { $script = "create vdisk file=`"$root_path\$name.vhdx`" parent=`"$root_path\parent_disk.vhdx`"" run_script $script attach_disk $name $root_path; break; } "remove_file" { Invoke-Command -Session $session -ArgumentList $name,$root_path -ScriptBlock { param($name, $root_path) Remove-Item -Path "$root_path\$name.vhdx" } } } Remove-PSSession $session
مرات أتشتونغ:
إذا قمت بإعادة تشغيل الخادم ، فعندئذ تعذّب أن تشرح لخادم sql أن قواعد البيانات هذه غير موجودة ، وأنك بحاجة إلى إعادة الاتصال بها.
أتشتونج اثنين:
قام المؤلف ، بالطبع ، بفحص الأوامر الموجودة على دارة الاختبار الخاصة به ، لكنه لا ينوي ضمان أي شيء (خاصةً أدائها). في خطر yout الخاصة.
المجموع:
يستغرق بدء تشغيل قاعدة بيانات اختبار إضافية بضع دقائق و 40 ميغابايت على القرص. وبالتالي ، فمن المناسب لكل مطور تنظيم مثيل قاعدة البيانات الخاصة به.
اختياري:
يمكن استخدام البرنامج النصي نفسه لرفع قاعدة البيانات لاختبارات التكامل.
آمل أن تكون مفيدة لشخص ما.