cari
Rumahhujung hadapan webtutorial jsMengendalikan Data Berkongsi dalam Sistem Teragih: Penyelaman Mendalam ke dalam Gabungan, Penyiaran dan Pengoptimuman Pertanyaan

Handling Sharded Data in Distributed Systems: A Deep Dive into Joins, Broadcasts, and Query Optimization

Dalam pangkalan data teragih moden, keperluan untuk menskalakan data secara mendatar telah membawa kepada penggunaan meluas sharding. Walaupun sharding membantu mengurus set data yang besar merentas berbilang nod, ia memperkenalkan cabaran, terutamanya apabila melakukan menyertai dan memastikan pengambilan data yang cekap. Dalam artikel ini, kami meneroka pelbagai konsep dan teknik yang menangani cabaran ini, terutamanya memfokuskan pada sambungan siaran, penjajaran kunci pecahan dan enjin pertanyaan teragih seperti Presto dan BigQuery. Selain itu, kami menunjukkan cara menangani masalah ini dalam aplikasi dunia nyata menggunakan Node.js dan Express.


Contoh Perkongsian dalam Node.js dengan Express.js

Begini cara anda boleh melaksanakan sharding dalam PostgreSQL menggunakan Node.js dan Express.js.

Contoh Perkongsian PostgreSQL

Menggunakan Citus atau serpihan logik manual dengan Node.js:

Contoh dengan Perkongsian Logik

  1. Sediakan Jadual untuk Serpihan:
    Gunakan jadual untuk serpihan (data_pengguna pada shard1 dan data_pengguna pada shard2).

  2. Buat Express.js API:
    Edarkan pertanyaan berdasarkan kunci serpihan (cth., user_id).

   const express = require('express');
   const { Pool } = require('pg');

   const poolShard1 = new Pool({ connectionString: 'postgresql://localhost/shard1' });
   const poolShard2 = new Pool({ connectionString: 'postgresql://localhost/shard2' });

   const app = express();
   app.use(express.json());

   const getShardPool = (userId) => (userId % 2 === 0 ? poolShard1 : poolShard2);

   app.post('/user', async (req, res) => {
       const { userId, data } = req.body;
       const pool = getShardPool(userId);
       try {
           await pool.query('INSERT INTO user_data (user_id, data) VALUES (, )', [userId, data]);
           res.status(200).send('User added successfully');
       } catch (err) {
           console.error(err);
           res.status(500).send('Error inserting user');
       }
   });

   app.get('/user/:userId', async (req, res) => {
       const userId = parseInt(req.params.userId, 10);
       const pool = getShardPool(userId);
       try {
           const result = await pool.query('SELECT * FROM user_data WHERE user_id = ', [userId]);
           res.status(200).json(result.rows);
       } catch (err) {
           console.error(err);
           res.status(500).send('Error retrieving user');
       }
   });

   app.listen(3000, () => console.log('Server running on port 3000'));

1. Perkongsian dalam Pangkalan Data Teragih

Sharding ialah proses membahagikan data secara mendatar merentas berbilang contoh pangkalan data, atau serpihan, untuk meningkatkan prestasi, kebolehskalaan dan ketersediaan. Perkongsian selalunya diperlukan apabila satu tika pangkalan data tidak dapat mengendalikan jumlah data atau trafik.

Strategi Perkongsian:

  • Perkongsian berasaskan Julat: Data diedarkan merentas serpihan berdasarkan julat kunci, cth., membahagikan pesanan mengikut tarikh_pesanan.
  • Perkongsian berasaskan cincang: Data dicincang oleh kunci serpihan (cth., id_pengguna) untuk mengagihkan data secara sama rata merentas serpihan.
  • Perkongsian berasaskan direktori: Direktori pusat menjejaki tempat data berada dalam sistem.

Walau bagaimanapun, apabila jadual berkaitan dipecahkan pada kekunci yang berbeza, atau apabila jadual memerlukan penyambungan dengan jadual lain merentas berbilang serpihan, prestasi boleh merosot kerana keperluan untuk operasi pengumpul-serakan. Di sinilah pemahaman siaran bergabung dan penjajaran kunci serpihan menjadi penting.


2. Cabaran dengan Gabungan dalam Sistem Berkongsi

Apabila data berada dalam serpihan yang berbeza, melakukan cantuman antara serpihan tersebut boleh menjadi rumit. Berikut ialah pecahan cabaran biasa:

1. Shard Key Misalignment:

Dalam banyak sistem, jadual dipecahkan pada kekunci yang berbeza. Contohnya:

  • Jadual pengguna mungkin dipecahkan oleh user_id.
  • Jadual pesanan mungkin dipecahkan mengikut wilayah.

Apabila melakukan gabungan (cth., orders.user_id = users.user_id), sistem perlu mengambil data daripada berbilang serpihan kerana rekod yang berkaitan mungkin tidak berada dalam serpihan yang sama.

2. Scatter-Gather Joins:

Dalam gabungan scatter-gather, sistem mesti:

  • Hantar permintaan kepada semua serpihan yang menyimpan data yang berkaitan.
  • Agregat hasil merentas serpihan. Ini boleh merendahkan prestasi dengan ketara, terutamanya apabila data tersebar pada banyak serpihan.

3. Gabungan Siaran:

Satu sambungan siaran berlaku apabila salah satu jadual yang dicantumkan cukup kecil untuk disiarkan ke semua serpihan. Dalam kes ini:

  • Jadual kecil (mis., pengguna) direplikasi merentasi semua nod di mana jadual yang lebih besar dan berpecah (mis., pesanan) berada.
  • Setiap nod kemudiannya boleh menyertai data tempatannya dengan data yang disiarkan, mengelakkan keperluan untuk komunikasi silang serpihan.

3. Menggunakan Enjin Pertanyaan Teragih untuk Data Berkongsi

Enjin pertanyaan teragih seperti Presto dan BigQuery direka bentuk untuk mengendalikan data berpecah dan menyertai pertanyaan dengan cekap merentas sistem teragih.

Presto/Trino:

Presto ialah enjin pertanyaan SQL teragih yang direka untuk menanyakan set data yang besar merentas sumber data heterogen (cth., pangkalan data hubungan, pangkalan data NoSQL, tasik data). Presto melakukan gabungan merentas sumber data yang diedarkan dan boleh mengoptimumkan pertanyaan dengan meminimumkan pergerakan data antara nod.

Contoh Kes Penggunaan: Menyertai Data Berkongsi dengan Presto

Dalam senario di mana pesanan dipecahkan mengikut wilayah dan pengguna dipecahkan oleh user_id, Presto boleh melakukan gabungan merentas serpihan yang berbeza menggunakan model pelaksanaan yang diedarkan.

Pertanyaan:

   const express = require('express');
   const { Pool } = require('pg');

   const poolShard1 = new Pool({ connectionString: 'postgresql://localhost/shard1' });
   const poolShard2 = new Pool({ connectionString: 'postgresql://localhost/shard2' });

   const app = express();
   app.use(express.json());

   const getShardPool = (userId) => (userId % 2 === 0 ? poolShard1 : poolShard2);

   app.post('/user', async (req, res) => {
       const { userId, data } = req.body;
       const pool = getShardPool(userId);
       try {
           await pool.query('INSERT INTO user_data (user_id, data) VALUES (, )', [userId, data]);
           res.status(200).send('User added successfully');
       } catch (err) {
           console.error(err);
           res.status(500).send('Error inserting user');
       }
   });

   app.get('/user/:userId', async (req, res) => {
       const userId = parseInt(req.params.userId, 10);
       const pool = getShardPool(userId);
       try {
           const result = await pool.query('SELECT * FROM user_data WHERE user_id = ', [userId]);
           res.status(200).json(result.rows);
       } catch (err) {
           console.error(err);
           res.status(500).send('Error retrieving user');
       }
   });

   app.listen(3000, () => console.log('Server running on port 3000'));

Presto akan:

  1. Gunakan scatter-gather untuk mengambil rekod pengguna yang berkaitan.
  2. Sertai data merentas nod.

Google BigQuery:

BigQuery ialah gudang data tanpa pelayan yang diurus sepenuhnya, yang cemerlang dalam menjalankan pertanyaan analisis berskala besar. Walaupun BigQuery mengasingkan butiran sharding, ia secara automatik membahagikan dan mengedarkan data merentas banyak nod untuk pertanyaan yang dioptimumkan. Ia boleh mengendalikan set data yang besar dengan mudah dan amat berkesan untuk pertanyaan analitikal yang mana data dibahagikan mengikut masa atau dimensi lain.

Contoh Kes Penggunaan: Menyertai Jadual Sharded dalam BigQuery
   const express = require('express');
   const { Pool } = require('pg');

   const poolShard1 = new Pool({ connectionString: 'postgresql://localhost/shard1' });
   const poolShard2 = new Pool({ connectionString: 'postgresql://localhost/shard2' });

   const app = express();
   app.use(express.json());

   const getShardPool = (userId) => (userId % 2 === 0 ? poolShard1 : poolShard2);

   app.post('/user', async (req, res) => {
       const { userId, data } = req.body;
       const pool = getShardPool(userId);
       try {
           await pool.query('INSERT INTO user_data (user_id, data) VALUES (, )', [userId, data]);
           res.status(200).send('User added successfully');
       } catch (err) {
           console.error(err);
           res.status(500).send('Error inserting user');
       }
   });

   app.get('/user/:userId', async (req, res) => {
       const userId = parseInt(req.params.userId, 10);
       const pool = getShardPool(userId);
       try {
           const result = await pool.query('SELECT * FROM user_data WHERE user_id = ', [userId]);
           res.status(200).json(result.rows);
       } catch (err) {
           console.error(err);
           res.status(500).send('Error retrieving user');
       }
   });

   app.listen(3000, () => console.log('Server running on port 3000'));

BigQuery secara automatik mengendalikan pembahagian dan pengedaran, meminimumkan keperluan untuk pembahagian manual.


4. Mengendalikan Penyelewengan Kunci Shard dalam Aplikasi Node.js

Apabila berurusan dengan data yang dipecahkan dalam aplikasi Node.js, isu seperti kekunci serpihan yang tidak sejajar dan keperluan untuk penyatuan-pengumpulan sering timbul. Begini cara anda boleh mengharungi cabaran ini menggunakan Node.js dan Express.

Mengendalikan Penyertaan Siaran dalam Node.js

Jika gabungan memerlukan penyiaran jadual kecil (mis., pengguna) merentas semua serpihan, anda boleh melaksanakan gabungan dalam lapisan aplikasi dengan mengambil jadual kecil sekali dan menggunakannya untuk bergabung dengan data daripada jadual berpecah.

SELECT o.order_id, u.user_name
FROM orders o
JOIN users u
ON o.user_id = u.user_id;

Mengendalikan Pertanyaan Scatter-Gather dalam Node.js

Untuk pertanyaan yang melibatkan cantuman scatter-gather (cth., apabila kekunci shard tidak sejajar), anda perlu menanyakan semua serpihan dan mengagregatkan hasil dalam lapisan aplikasi anda.

SELECT o.order_id, u.user_name
FROM `project.dataset.orders` o
JOIN `project.dataset.users` u
ON o.user_id = u.user_id
WHERE o.order_date BETWEEN '2024-01-01' AND '2024-12-31';

5. Amalan Terbaik untuk Pengoptimuman Pertanyaan dengan Data Berkongsi

Apabila berurusan dengan data yang dipecahkan dan melakukan gabungan, pertimbangkan amalan terbaik berikut:

  1. Jajarkan Kekunci Shard: Apabila boleh, pastikan jadual berkaitan menggunakan kekunci shard yang sama. Ini meminimumkan keperluan untuk cantuman rentas serpihan dan meningkatkan prestasi.

  2. Nyahnormalisasi: Dalam senario yang kerap disertai, pertimbangkan untuk menyahnormalkan data anda. Sebagai contoh, anda boleh menyimpan maklumat pengguna terus dalam jadual siaran, mengurangkan keperluan untuk menyertai.

  3. Gunakan Broadcast Joins untuk Meja Kecil: Jika salah satu jadual cukup kecil, siarkan ke semua nod untuk mengelakkan pertanyaan serakan-kumpul.

  4. Data Pra-Sertai: Untuk data yang kerap diakses, pertimbangkan untuk pra-menyertai dan menyimpan hasil dalam paparan nyata atau cache.

  5. Memanfaatkan Enjin Pertanyaan Teragih: Untuk pertanyaan analitikal yang kompleks, gunakan sistem seperti Presto atau BigQuery yang mengendalikan gabungan dan pengoptimuman yang diedarkan secara automatik.


6. Amalan Terbaik untuk Penomboran Berasaskan Kursor dengan Data Berkongsi

Dalam sistem teragih dengan sharding sedemikian, penomboran berasaskan kursor perlu dikendalikan dengan berhati-hati, terutamanya kerana data tersebar merentasi berbilang serpihan. Kuncinya ialah:

  1. Pisah pertanyaan: Tanya setiap serpihan secara berasingan untuk data yang berkaitan.
  2. Kendalikan penomboran dalam ketulan: Tentukan cara membuat penomboran merentas data serpihan (sama ada pada siaran atau pengguna), dan kumpulkan hasil yang berkaitan.
  3. Sertai di peringkat aplikasi: Ambil hasil daripada setiap serpihan, sertai data dalam ingatan, dan kemudian gunakan logik kursor untuk halaman seterusnya.

Mari kita lihat bagaimana kita boleh melaksanakan perkara ini dengan Node.js dan Express, dengan mengambil kira bahawa data berada pada serpihan yang berbeza dan memerlukan gabungan pasca ambil pada peringkat aplikasi.

Cara Mengendalikan Penomboran dan Cantuman dengan Jadual Sharded

Anggap kita ada:

  • siaran jadual dipecahkan oleh user_id.
  • pengguna jadual dipecahkan oleh user_id.

Kami ingin mendapatkan semula siaran penomboran untuk pengguna tertentu, tetapi memandangkan pengguna dan siaran berada pada serpihan yang berbeza, kami perlu membahagikan pertanyaan, mengendalikan penomboran dan kemudian melakukan gabungan pada peringkat aplikasi.

Pendekatan:

  1. Pertanyakan Serpihan Berkaitan:

    • Mula-mula, anda perlu menanyakan jadual siaran merentas serpihan untuk mengambil siaran.
    • Selepas mengambil siaran yang berkaitan, gunakan user_id daripada siaran untuk menanyakan jadual pengguna (sekali lagi, merentas serpihan).
  2. Strategi Penomboran:

    • Penomboran pada siaran: Anda boleh menggunakan created_at, post_id atau medan unik lain untuk menomborkan jadual siaran.
    • Penomboran pada pengguna: Anda mungkin perlu mengambil data pengguna secara berasingan atau menggunakan user_id sebagai kursor untuk membuat penomboran melalui pengguna.
  3. Sertai Peringkat Aplikasi:

    • Selepas mendapatkan semula data daripada serpihan yang berkaitan (untuk kedua-dua siaran dan pengguna), sertai mereka di peringkat aplikasi.
  4. Mengendalikan Kursor:

    • Selepas mengambil halaman pertama, gunakan create_at terakhir atau post_id (daripada siaran) sebagai kursor untuk pertanyaan seterusnya.

Contoh Pelaksanaan

1. Siaran Pertanyaan Merentasi Serpihan

Di sini kami akan melaksanakan pertanyaan merentas serpihan siaran yang berbeza, menapis menggunakan kursor (cth., created_at atau post_id).

2. Pertanyaan Pengguna Merentasi Serpihan Menggunakan Data Pos

Sebaik sahaja kami mempunyai post_id dan user_id yang berkaitan daripada pertanyaan pertama, kami akan mengambil data pengguna daripada serpihan yang berkaitan.

   const express = require('express');
   const { Pool } = require('pg');

   const poolShard1 = new Pool({ connectionString: 'postgresql://localhost/shard1' });
   const poolShard2 = new Pool({ connectionString: 'postgresql://localhost/shard2' });

   const app = express();
   app.use(express.json());

   const getShardPool = (userId) => (userId % 2 === 0 ? poolShard1 : poolShard2);

   app.post('/user', async (req, res) => {
       const { userId, data } = req.body;
       const pool = getShardPool(userId);
       try {
           await pool.query('INSERT INTO user_data (user_id, data) VALUES (, )', [userId, data]);
           res.status(200).send('User added successfully');
       } catch (err) {
           console.error(err);
           res.status(500).send('Error inserting user');
       }
   });

   app.get('/user/:userId', async (req, res) => {
       const userId = parseInt(req.params.userId, 10);
       const pool = getShardPool(userId);
       try {
           const result = await pool.query('SELECT * FROM user_data WHERE user_id = ', [userId]);
           res.status(200).json(result.rows);
       } catch (err) {
           console.error(err);
           res.status(500).send('Error retrieving user');
       }
   });

   app.listen(3000, () => console.log('Server running on port 3000'));

Butiran Utama:

  1. Penomboran pada siaran: Kursor adalah berdasarkan medan create_at atau medan unik lain dalam siaran, yang digunakan untuk membuat penomboran melalui hasil.
  2. Query Shards Independent: Memandangkan siaran dan pengguna dipecahkan pada kunci yang berbeza, kami menanyakan setiap shard secara bebas, mengumpulkan data daripada semua shards sebelum melakukan gabungan pada peringkat aplikasi.
  3. Pengendalian Kursor: Selepas mendapatkan semula keputusan, kami menggunakan terakhir dicipta_pada (atau post_id) daripada siaran untuk menjana kursor untuk halaman seterusnya.
  4. Sertai di Peringkat Aplikasi: Selepas mengambil data daripada serpihan yang berkaitan, kami menyertai siaran dengan data pengguna berdasarkan user_id dalam ingatan.

Kesimpulan

Menguruskan data berpecah dalam sistem yang diedarkan memberikan cabaran yang unik, terutamanya dalam hal melaksanakan gabungan yang cekap. Memahami teknik seperti sambungan siaran, gabungan scatter-gather dan memanfaatkan enjin pertanyaan teragih boleh meningkatkan prestasi pertanyaan dengan ketara. Selain itu, dalam pertanyaan peringkat aplikasi, adalah penting untuk mempertimbangkan penjajaran kunci beling, nyahnormalisasi dan strategi pertanyaan yang dioptimumkan. Dengan mengikuti amalan terbaik ini dan menggunakan alatan yang betul, pembangun boleh memastikan bahawa aplikasi mereka mengendalikan data berpecah dengan berkesan dan mengekalkan prestasi pada skala.

Atas ialah kandungan terperinci Mengendalikan Data Berkongsi dalam Sistem Teragih: Penyelaman Mendalam ke dalam Gabungan, Penyiaran dan Pengoptimuman Pertanyaan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Python vs JavaScript: Bahasa mana yang harus anda pelajari?Python vs JavaScript: Bahasa mana yang harus anda pelajari?May 03, 2025 am 12:10 AM

Memilih Python atau JavaScript harus berdasarkan perkembangan kerjaya, keluk pembelajaran dan ekosistem: 1) Pembangunan Kerjaya: Python sesuai untuk sains data dan pembangunan back-end, sementara JavaScript sesuai untuk pembangunan depan dan penuh. 2) Kurva Pembelajaran: Sintaks Python adalah ringkas dan sesuai untuk pemula; Sintaks JavaScript adalah fleksibel. 3) Ekosistem: Python mempunyai perpustakaan pengkomputeran saintifik yang kaya, dan JavaScript mempunyai rangka kerja front-end yang kuat.

Rangka Kerja JavaScript: Menguasai Pembangunan Web ModenRangka Kerja JavaScript: Menguasai Pembangunan Web ModenMay 02, 2025 am 12:04 AM

Kuasa rangka kerja JavaScript terletak pada pembangunan yang memudahkan, meningkatkan pengalaman pengguna dan prestasi aplikasi. Apabila memilih rangka kerja, pertimbangkan: 1.

Hubungan antara JavaScript, C, dan penyemak imbasHubungan antara JavaScript, C, dan penyemak imbasMay 01, 2025 am 12:06 AM

Pengenalan Saya tahu anda mungkin merasa pelik, apa sebenarnya yang perlu dilakukan oleh JavaScript, C dan penyemak imbas? Mereka seolah -olah tidak berkaitan, tetapi sebenarnya, mereka memainkan peranan yang sangat penting dalam pembangunan web moden. Hari ini kita akan membincangkan hubungan rapat antara ketiga -tiga ini. Melalui artikel ini, anda akan mempelajari bagaimana JavaScript berjalan dalam penyemak imbas, peranan C dalam enjin pelayar, dan bagaimana mereka bekerjasama untuk memacu rendering dan interaksi laman web. Kita semua tahu hubungan antara JavaScript dan penyemak imbas. JavaScript adalah bahasa utama pembangunan front-end. Ia berjalan secara langsung di penyemak imbas, menjadikan laman web jelas dan menarik. Adakah anda pernah tertanya -tanya mengapa Javascr

Aliran node.js dengan typescriptAliran node.js dengan typescriptApr 30, 2025 am 08:22 AM

Node.js cemerlang pada I/O yang cekap, sebahagian besarnya terima kasih kepada aliran. Aliran memproses data secara berperingkat, mengelakkan beban memori-ideal untuk fail besar, tugas rangkaian, dan aplikasi masa nyata. Menggabungkan sungai dengan keselamatan jenis typescript mencipta powe

Python vs JavaScript: Pertimbangan Prestasi dan KecekapanPython vs JavaScript: Pertimbangan Prestasi dan KecekapanApr 30, 2025 am 12:08 AM

Perbezaan prestasi dan kecekapan antara Python dan JavaScript terutamanya dicerminkan dalam: 1) sebagai bahasa yang ditafsirkan, Python berjalan perlahan tetapi mempunyai kecekapan pembangunan yang tinggi dan sesuai untuk pembangunan prototaip pesat; 2) JavaScript adalah terhad kepada benang tunggal dalam penyemak imbas, tetapi I/O multi-threading dan asynchronous boleh digunakan untuk meningkatkan prestasi dalam node.js, dan kedua-duanya mempunyai kelebihan dalam projek sebenar.

Asal JavaScript: Meneroka Bahasa PelaksanaannyaAsal JavaScript: Meneroka Bahasa PelaksanaannyaApr 29, 2025 am 12:51 AM

JavaScript berasal pada tahun 1995 dan dicipta oleh Brandon Ike, dan menyedari bahasa itu menjadi C. 1.C Language menyediakan keupayaan pengaturcaraan prestasi tinggi dan sistem untuk JavaScript. 2. Pengurusan memori JavaScript dan pengoptimuman prestasi bergantung pada bahasa C. 3. Ciri lintas platform bahasa C membantu JavaScript berjalan dengan cekap pada sistem operasi yang berbeza.

Di sebalik tabir: Apa bahasa JavaScript?Di sebalik tabir: Apa bahasa JavaScript?Apr 28, 2025 am 12:01 AM

JavaScript berjalan dalam penyemak imbas dan persekitaran Node.js dan bergantung pada enjin JavaScript untuk menghuraikan dan melaksanakan kod. 1) menjana pokok sintaks abstrak (AST) di peringkat parsing; 2) menukar AST ke bytecode atau kod mesin dalam peringkat penyusunan; 3) Laksanakan kod yang disusun dalam peringkat pelaksanaan.

Masa Depan Python dan JavaScript: Trend dan RamalanMasa Depan Python dan JavaScript: Trend dan RamalanApr 27, 2025 am 12:21 AM

Trend masa depan Python dan JavaScript termasuk: 1. Kedua -duanya akan terus mengembangkan senario aplikasi dalam bidang masing -masing dan membuat lebih banyak penemuan dalam prestasi.

See all articles

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

Video Face Swap

Video Face Swap

Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Alat panas

SublimeText3 versi Inggeris

SublimeText3 versi Inggeris

Disyorkan: Versi Win, menyokong gesaan kod!

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

SublimeText3 Linux versi baharu

SublimeText3 Linux versi baharu

SublimeText3 Linux versi terkini

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

Persekitaran pembangunan bersepadu PHP yang berkuasa