Senario:
Anda memiliki file dengan string (nilai kalimat rata-rata) di setiap baris. Demi argumen, katakanlah file ini berukuran 1 MB (ribuan baris).
Anda memiliki skrip yang membaca file, mengubah beberapa string dalam dokumen (tidak hanya menambahkan tetapi juga menghapus dan memodifikasi beberapa baris) dan kemudian menimpa semua data dengan data baru.
Pertanyaan-pertanyaan:
Apakah 'server' PHP, OS atau httpd dll. Sudah memiliki sistem untuk menghentikan masalah seperti ini (membaca / menulis setengah jalan melalui penulisan)?
Jika ya, tolong jelaskan cara kerjanya dan berikan contoh atau tautan ke dokumentasi yang relevan.
Jika tidak, apakah ada hal-hal yang dapat saya aktifkan atau set-up, seperti mengunci file sampai penulisan selesai dan membuat semua bacaan lainnya dan / atau penulisan gagal sampai skrip sebelumnya selesai menulis?
Asumsi Saya dan Informasi Lainnya:
Server yang dimaksud menjalankan PHP dan Apache atau Lighttpd.
Jika skrip dipanggil oleh satu pengguna dan setengah jalan menulis ke file dan pengguna lain membaca file pada saat yang tepat. Pengguna yang membacanya tidak akan mendapatkan dokumen lengkap, karena belum ditulis. (Jika asumsi ini salah, mohon koreksi saya)
Saya hanya peduli dengan penulisan dan pembacaan PHP ke file teks, dan khususnya, fungsi "fopen" / "fwrite" dan terutama "file_put_contents". Saya telah melihat dokumentasi "file_put_contents" tetapi belum menemukan tingkat detail atau penjelasan yang baik tentang apa bendera atau "LOCK_EX" itu.
Skenario adalah contoh dari skenario terburuk di mana saya akan menganggap masalah ini lebih mungkin terjadi, karena ukuran file yang besar dan cara data diedit. Saya ingin mempelajari lebih lanjut tentang masalah ini dan tidak ingin atau memerlukan jawaban atau komentar seperti "gunakan mysql" atau "mengapa Anda melakukan itu" karena saya tidak melakukan itu, saya hanya ingin belajar tentang membaca / menulis file dengan PHP dan sepertinya tidak mencari di tempat yang tepat / dokumentasi dan ya saya mengerti PHP bukan bahasa yang sempurna untuk bekerja dengan file dengan cara ini.
sumber
file_put_contents()
hanyalah bungkus untukfopen()/fwrite()
tarian,LOCKEX
melakukan hal yang sama seperti jika Anda akan meneleponflock($handle, LOCKEX)
.Jawaban:
1) Tidak 3) Tidak
Ada beberapa masalah dengan pendekatan yang disarankan asli:
Pertama, beberapa sistem mirip UNIX seperti Linux mungkin tidak memiliki dukungan penguncian yang diterapkan. OS tidak mengunci file secara default. Saya telah melihat syscalls menjadi NOP (tidak ada operasi), tapi itu beberapa tahun yang lalu, jadi Anda perlu memverifikasi apakah kunci yang ditetapkan oleh instance aplikasi Anda dihormati oleh instance lain. (yaitu 2 pengunjung bersamaan). Jika penguncian masih diimplementasikan [sangat mungkin itu], OS memungkinkan Anda menimpa file itu.
Membaca file besar baris demi baris tidak layak karena alasan kinerja. Saya sarankan menggunakan file_get_contents () untuk memuat seluruh file ke dalam memori dan kemudian meledak () untuk mendapatkan baris. Atau, gunakan fread () untuk membaca file dalam blok. Tujuannya adalah untuk meminimalkan jumlah panggilan yang dibaca.
Dalam hal penguncian file:
LOCK_EX berarti kunci eksklusif (biasanya untuk menulis). Hanya satu proses yang dapat menahan kunci eksklusif untuk file yang diberikan pada waktu tertentu. LOCK_SH adalah kunci bersama (biasanya untuk membaca), Lebih dari satu proses dapat menahan kunci bersama untuk file yang diberikan pada waktu tertentu. LOCK_UN membuka kunci file. Membuka kunci dilakukan secara otomatis jika Anda menggunakan file_get_contents () http://en.wikipedia.org/wiki/File_locking#In_Unix-like_systems
Solusi elegan
PHP mendukung filter aliran data yang dimaksudkan untuk memproses data dalam file atau dari input lain. Anda mungkin ingin membuat satu filter dengan benar menggunakan API standar. http://php.net/manual/en/function.stream-filter-register.php http://php.net/manual/en/filters.php
Solusi alternatif (dalam 3 langkah):
Buat antrian. Alih-alih memproses satu nama file, gunakan database atau mekanisme lain untuk menyimpan nama file unik di suatu tempat dalam proses / sedang diproses dalam / diproses. Dengan cara ini tidak ada yang ditimpa. Basis data juga akan berguna untuk menyimpan informasi tambahan, seperti metadata, cap waktu yang andal, hasil pemrosesan, dan lainnya.
Untuk file hingga beberapa MB, baca seluruh file ke dalam memori dan kemudian proseskan (file_get_contents () + explode () + foreach ())
Untuk file yang lebih besar baca file dalam blok (yaitu 1024 Bytes) dan proses + tulis secara real-time setiap blok sebagai bacaan (hati-hati tentang baris terakhir yang tidak berakhir dengan \ n. Ini perlu diproses dalam batch berikutnya)
sumber
Saya tahu ini sudah tua, tetapi kalau-kalau ada yang mengalami ini. IMHO cara untuk melakukannya adalah seperti ini:
1) Buka file asli (mis. Original.txt) menggunakan file_get_contents ('original.txt').
2) Buat perubahan / pengeditan Anda.
3) Gunakan file_put_contents ('original.txt.tmp') dan tuliskan ke file temp original.txt.tmp.
4) Kemudian pindahkan file tmp ke file asli, ganti file asli. Untuk ini, Anda menggunakan rename ('original.txt.tmp', 'original.txt').
Keuntungan: Saat file sedang diproses dan ditulis ke file tidak terkunci dan yang lain masih bisa membaca konten lama. Setidaknya di Linux / kotak Unix rename adalah operasi atom. Gangguan apa pun selama penulisan file tidak menyentuh file asli. Hanya setelah file telah sepenuhnya ditulis ke disk, file dipindahkan. Lebih menarik baca ini di komentar ke http://php.net/manual/en/function.rename.php
Edit ke alamat komitmen (juga untuk komentar):
/programming/7054844/is-rename-atomic memiliki referensi lebih lanjut tentang apa yang mungkin perlu Anda lakukan jika Anda beroperasi di seluruh sistem file.
Pada kunci bersama untuk membaca saya tidak yakin mengapa itu akan diperlukan karena dalam implementasi ini tidak ada tulisan ke file secara langsung. Kawanan PHP (yang digunakan untuk mendapatkan kunci) sedikit tetapi tidak dapat diandalkan dan dapat diabaikan oleh proses lain. Itulah mengapa saya menyarankan untuk menggunakan rename.
File rename idealnya dinamai secara unik untuk proses melakukan penggantian nama sehingga untuk memastikan tidak 2 proses melakukan hal yang sama. Tetapi ini tentu saja tidak mencegah pengeditan file yang sama oleh lebih dari satu orang pada saat yang bersamaan. Tapi setidaknya file akan dibiarkan utuh (edit terakhir menang).
Langkah 3) & 4) akan menjadi ini:
sumber
tempnam
fungsi, yang atom menciptakan sebuah file dan kembali nama file.Dalam dokumentasi PHP untuk file_put_contents () Anda dapat menemukan dalam contoh # 2 penggunaan untuk LOCK_EX , dengan kata lain:
The LOCK_EX adalah konstan dengan bilangan bulat nilai selain dapat digunakan pada beberapa fungsi dalam bitwise .
Ada juga fungsi khusus untuk mengontrol penguncian file: cara flock () .
sumber
file_get/put_contents
.Masalah yang tidak Anda sebutkan adalah kondisi lomba di mana dua contoh skrip Anda berjalan pada waktu yang hampir bersamaan, misalnya urutan kejadian ini:
Jadi ketika memperbarui file besar, Anda perlu LOCK_EX file itu sebelum Anda membacanya dan tidak melepaskan kunci sampai menulis telah dibuat. Dalam contoh ini saya percaya bahwa akan menyebabkan contoh skrip kedua hang sebentar sementara menunggu gilirannya untuk mengakses file, tetapi ini lebih baik daripada kehilangan data.
sumber