Saya akan menjelaskan secara singkat bagaimana kloning database diatur (pembuatan beberapa contoh database dari satu cadangan) pada proyek saat ini. Metode ini memungkinkan menghemat waktu dan ruang hard disk.
Situasi: ada database tebal (katakanlah, seratus GB). Saya ingin memiliki database ini dengan semua data secara terpisah untuk setiap pengembang dan tidak menghabiskan disk terabyte di atasnya. Berikut ini adalah solusi untuk MSSQL untuk windows menggunakan PowerShell.
Saya menemukan utilitas Redgate SQL Clone. Situs ini memiliki
deskripsi cara kerjanya . Intinya adalah menggunakan hal seperti itu: membedakan hard disk virtual. Ini diterjemahkan ke dalam bahasa Rusia sebagai "disk virtual diferensial" - disk yang hanya menyimpan perbedaannya dengan disk "induk".
Detail di bawah potongan
Skema kerjanya adalah sebagai berikut:
- Kami membuat dan mencolokkan disk virtual biasa (nantinya akan menjadi induknya).
- Kami membuat satu contoh basis data dari mana klon akan dibuat. Kami membersihkan pro-data, menyiapkan basis data sepenuhnya untuk bekerja di lingkungan pengujian. Kami menempatkan file database pada disk virtual.
- Putuskan koneksi database dari server. Kami memutuskan koneksi disk virtual.
- Buat disk pembeda. Kami terhubung ke sistem. Kami menghubungkan database dari disk ini ke server sql.
- Ulangi langkah 4 hingga jumlah database yang harmonis tercapai.
Membuat disk induk tidak akan dijelaskan, karena ini dapat dilakukan secara manual melalui antarmuka grafis manajemen disk. Nah, atau perintah google dan melengkapi skrip yang diberikan dalam artikel.
Catat waktu:
Di windows 10 dan windows server 2016 ada powershell commandlet
New-VHD . Bagi mereka yang menggunakan versi server sebelumnya ada utilitas diskpart. Mengotomatiskan bekerja dengannya sangat tidak nyaman, karena pada input, ia menerima file dengan perintah untuk dieksekusi.
Catatan dua:
Karena Karena file database ditempatkan pada disk yang berbeda, kinerja solusi semacam itu masih jauh dari sempurna. Ternyata beberapa tingkat tipuan: catatan masuk ke database, yang terletak pada disk virtual yang menyimpan perbedaan di rumah yang dibangun Jack. Saya tidak punya nomor kinerja tertentu (karena di sirkuit pengujian kami ini bukan pertanyaan pertama). Saya akan berterima kasih jika seseorang mengukur seberapa banyak kecepatan menulis / membaca terkuras.
Perhatikan tiga:
Karena skrip tidak dimaksudkan untuk digunakan secara luas dan disediakan hanya sebagai contoh, ada peningkatan kelengkungan dan pengikatan yang ketat pada MSSQL.
Kami akan menginisialisasi beberapa variabel:$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;
Karena skrip dijalankan pada mesin pengembang, dan semua tindakan dilakukan pada mesin dengan server sql, diasumsikan bahwa PowerShell Remoting dikonfigurasi. Semua perintah dieksekusi dalam sesi terbuka.
Get-StoredCredential adalah commandlet untuk menyimpan kredensial pada mesin lokal (diinstal secara terpisah). Pada prinsipnya, Anda dapat melakukannya tanpanya, itulah sebabnya ia dibungkus dengan try / catch.
Berikut ini adalah kode eksekusi skrip 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 } }
Perintah Sql yang saya jalankan melalui SQLCMD: function run_sql([string]$sql) { Write-Host $sql SQLCMD -S $server -d master -U $($db_cred.UserName) -P $($db_cred.Password) -Q $sql }
Membuat disk pembeda: run_script "create vdisk file=`"$root_path\$name.vhdx`" parent=`"$root_path\parent_disk.vhdx`""
Selanjutnya, hubungkan disk dan basis data: $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"
Sepotong "fungsi ls: [dz]: -n" hanyalah semacam sihir untuk mendapatkan daftar huruf drive. Cara kerjanya - tidak tahu, disalin dari stackoverflow.
Dalam kode di atas, kesulitan terbesar adalah untuk mendapatkan disk virtual yang dihasilkan dan menaruhnya di surat tertentu. Dia juga perlu melakukan online sebelumnya.
Melepaskan drive sedikit lebih mudah: 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
Menyatukan semuanya: 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
Kali Achtung:
Jika Anda me-reboot server, maka Anda tersiksa untuk menjelaskan kepada server sql bahwa database ini tidak ada, dan Anda harus menyambungkan kembali.
Achtung dua:
Penulis, tentu saja, memeriksa perintah di sirkuit pengujiannya, tetapi tidak bermaksud untuk menjamin apa pun (terutama kinerjanya). Dengan risiko Anda sendiri.
Total:
Meluncurkan basis data pengujian tambahan membutuhkan beberapa menit dan 40MB pada disk. Dengan demikian, jauh lebih nyaman bagi setiap pengembang untuk mengatur instance database mereka sendiri.
Opsional:
Script yang sama dapat digunakan untuk meningkatkan database untuk tes integrasi.
Semoga bermanfaat bagi seseorang.