Halaman ini berjalan melalui contoh umum untuk menunjukkan betapa mudahnya migrasi data biasa saat menggunakan Redis dan penyimpanan data tanpa skema NoSQL lainnya.
Semua halaman aplikasi Blog Redis #
- Mendesain Basis Data NoSQL menggunakan Redis
- Migrasi data tanpa rasa sakit menggunakan Redis dan penyimpanan data NoSQL tanpa skema lainnya
Migrasi data tanpa rasa sakit dengan penyimpanan data NoSQL dan Redis tanpa skema #
Mengembangkan baru sistem database greenfield yang menggunakan back-end RDBMS sebagian besar merupakan pengalaman yang bebas masalah. Sebelum sistem aktif, Anda dapat dengan mudah memodifikasi skema dengan nuking seluruh database aplikasi, dan membuatnya kembali dengan skrip DDL otomatis yang akan membuat dan mengisinya dengan data uji yang sesuai dengan skema baru Anda.
Masalah nyata dalam kehidupan TI Anda terjadi setelah penerapan pertama Anda dan sistem Anda ditayangkan. Pada saat itu Anda tidak lagi memiliki opsi untuk nuke database dan membuatnya kembali dari awal. Jika Anda beruntung, Anda memiliki skrip yang dapat secara otomatis menyimpulkan pernyataan DDL yang diperlukan untuk bermigrasi dari skema lama Anda ke skema baru Anda. Namun setiap perubahan signifikan pada skema Anda kemungkinan akan melibatkan larut malam, waktu henti, dan upaya yang tidak sepele untuk memastikan migrasi yang berhasil ke skema db baru.
Proses ini jauh lebih tidak menyakitkan dengan penyimpanan data tanpa skema. Bahkan dalam banyak kasus ketika Anda hanya menambahkan dan menghapus bidang itu tidak ada sama sekali. Dengan tidak membuat datastore Anda memahami detail intrinsik skema Anda, ini berarti ini bukan lagi masalah tingkat infrastruktur dan dapat dengan mudah ditangani oleh logika aplikasi jika diperlukan.
Bebas perawatan, tanpa skema, dan tidak mengganggu adalah kualitas desain mendasar yang dimasukkan ke dalam Redis dan operasinya. Misalnya menanyakan daftar Postingan Blog terbaru mengembalikan hasil yang sama untuk daftar kosong seperti di database Redis kosong - 0 hasil. Karena nilai dalam Redis adalah string biner-aman, Anda dapat menyimpan apa pun yang Anda inginkan di dalamnya dan yang paling penting dengan ekstensi ini berarti bahwa semua operasi Redis dapat mendukung semua jenis aplikasi Anda tanpa memerlukan 'bahasa perantara' seperti DDL untuk menyediakan skema kaku dari apa yang diharapkan. Tanpa inisialisasi sebelumnya, kode Anda dapat berbicara langsung ke penyimpanan data Redis secara alami seolah-olah itu adalah koleksi dalam memori.
Untuk mengilustrasikan apa yang dapat dicapai dalam praktik, saya akan menjalankan dua strategi berbeda dalam menangani perubahan skema.
- Pendekatan tidak melakukan apa-apa - di mana penambahan, penghapusan kolom, dan perubahan non-destruktif dari jenis kolom ditangani secara otomatis.
- Menggunakan terjemahan khusus - menggunakan logika tingkat aplikasi untuk menyesuaikan terjemahan antara jenis lama dan baru.
Kode sumber lengkap yang dapat dijalankan untuk contoh ini tersedia di sini.
Contoh Kode #
Untuk mendemonstrasikan skenario migrasi biasa, saya menggunakan BlogPost
ketik yang ditentukan pada halaman sebelumnya untuk memproyeksikannya ke New.BlogPost
yang berbeda secara mendasar Tipe. Definisi lengkap dari tipe lama dan baru ditunjukkan di bawah ini:
Skema lama #
public class BlogPost
{
public BlogPost()
{
this.Categories = new List<string>();
this.Tags = new List<string>();
this.Comments = new List<BlogPostComment>();
}
public int Id { get; set; }
public int BlogId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public List<string> Categories { get; set; }
public List<string> Tags { get; set; }
public List<BlogPostComment> Comments { get; set; }
}
public class BlogPostComment
{
public string Content { get; set; }
public DateTime CreatedDate { get; set; }
}
Skema baru #
'Versi baru' berisi sebagian besar perubahan yang mungkin Anda temui dalam pengembangan aplikasi normal:
- Menambahkan, menghapus, dan mengganti nama bidang
- Perubahan tidak merusak
int
menjadilong
dandouble
bidang - Mengubah jenis kumpulan Tag dari
List
keHashSet
- Mengubah
BlogPostComment
yang diketik dengan kuat ketik stringDictionary
yang diketik dengan longgar - Memperkenalkan
enum
baru ketik - Menambahkan kolom kalkulasi nullable
Jenis skema baru #
public class BlogPost
{
public BlogPost()
{
this.Labels = new List<string>();
this.Tags = new HashSet<string>();
this.Comments = new List<Dictionary<string, string>>();
}
//Changed int types to both a long and a double type
public long Id { get; set; }
public double BlogId { get; set; }
//Added new field
public BlogPostType PostType { get; set; }
public string Title { get; set; }
public string Content { get; set; }
//Renamed from 'Categories' to 'Labels'
public List<string> Labels { get; set; }
//Changed from List to a HashSet
public HashSet<string> Tags { get; set; }
//Changed from List of strongly-typed 'BlogPostComment' to loosely-typed string map
public List<Dictionary<string, string>> Comments { get; set; }
//Added pointless calculated field
public int? NoOfComments { get; set; }
}
public enum BlogPostType
{
None,
Article,
Summary,
}
1. Pendekatan tidak melakukan apa pun - menggunakan data lama dengan tipe baru #
Meskipun sulit dipercaya, tanpa usaha ekstra Anda bisa berpura-pura tidak ada perubahan yang benar-benar dilakukan dan bebas mengakses tipe baru dengan melihat data lama. Ini dimungkinkan ketika ada perubahan non-destruktif (yaitu tidak ada kehilangan informasi) dengan jenis bidang baru. Contoh di bawah ini menggunakan repositori dari contoh sebelumnya untuk mengisi Redis dengan data uji dari tipe lama. Seolah-olah tidak ada yang terjadi, Anda dapat membaca data lama menggunakan tipe baru:
var repository = new BlogRepository(redisClient);
//Populate the datastore with the old schema from the 'BlogPostBestPractice'
BlogPostBestPractice.InsertTestData(repository);
//Create a typed-client based on the new schema
using (var redisBlogPosts = redisClient.GetTypedClient<New.BlogPost>())
{
//Automatically retrieve blog posts
IList<New.BlogPost> allBlogPosts = redisBlogPosts.GetAll();
//Print out the data in the list of 'New.BlogPost' populated from old 'BlogPost' type
Console.WriteLine(allBlogPosts.Dump());
/*Output:
[
{
Id: 3,
BlogId: 2,
PostType: None,
Title: Redis,
Labels: [],
Tags:
[
Redis,
NoSQL,
Scalability,
Performance
],
Comments:
[
{
Content: First Comment!,
CreatedDate: 2010-04-28T21:42:03.9484725Z
}
]
},
{
Id: 4,
BlogId: 2,
PostType: None,
Title: Couch Db,
Labels: [],
Tags:
[
CouchDb,
NoSQL,
JSON
],
Comments:
[
{
Content: First Comment!,
CreatedDate: 2010-04-28T21:42:03.9484725Z
}
]
},
{
Id: 1,
BlogId: 1,
PostType: None,
Title: RavenDB,
Labels: [],
Tags:
[
Raven,
NoSQL,
JSON,
.NET
],
Comments:
[
{
Content: First Comment!,
CreatedDate: 2010-04-28T21:42:03.9004697Z
},
{
Content: Second Comment!,
CreatedDate: 2010-04-28T21:42:03.9004697Z
}
]
},
{
Id: 2,
BlogId: 1,
PostType: None,
Title: Cassandra,
Labels: [],
Tags:
[
Cassandra,
NoSQL,
Scalability,
Hashing
],
Comments:
[
{
Content: First Comment!,
CreatedDate: 2010-04-28T21:42:03.9004697Z
}
]
}
]
*/
}
2. Menggunakan terjemahan khusus untuk memigrasikan data menggunakan logika aplikasi #
Beberapa kelemahan dengan pendekatan 'tidak melakukan apa-apa' di atas adalah Anda akan kehilangan data 'bidang yang diubah namanya'. Ada juga saat-saat ketika Anda ingin data yang baru dimigrasikan memiliki nilai spesifik yang berbeda dari default bawaan .NET. Bila Anda ingin kontrol lebih besar atas migrasi data lama Anda, menambahkan terjemahan khusus adalah latihan yang sepele ketika Anda dapat melakukannya secara asli dalam kode:
var repository = new BlogRepository(redisClient);
//Populate the datastore with the old schema from the 'BlogPostBestPractice'
BlogPostBestPractice.InsertTestData(repository);
//Create a typed-client based on the new schema
using (var redisBlogPosts = redisClient.GetTypedClient<BlogPost>())
using (var redisNewBlogPosts = redisClient.GetTypedClient<New.BlogPost>())
{
//Automatically retrieve blog posts
IList<BlogPost> oldBlogPosts = redisBlogPosts.GetAll();
//Write a custom translation layer to migrate to the new schema
var migratedBlogPosts = oldBlogPosts.ConvertAll(old => new New.BlogPost
{
Id = old.Id,
BlogId = old.BlogId,
Title = old.Title,
Content = old.Content,
Labels = old.Categories, //populate with data from renamed field
PostType = New.BlogPostType.Article, //select non-default enum value
Tags = old.Tags,
Comments = old.Comments.ConvertAll(x => new Dictionary<string, string>
{ { "Content", x.Content }, { "CreatedDate", x.CreatedDate.ToString() }, }),
NoOfComments = old.Comments.Count, //populate using logic from old data
});
//Persist the new migrated blogposts
redisNewBlogPosts.StoreAll(migratedBlogPosts);
//Read out the newly stored blogposts
var refreshedNewBlogPosts = redisNewBlogPosts.GetAll();
//Note: data renamed fields are successfully migrated to the new schema
Console.WriteLine(refreshedNewBlogPosts.Dump());
/*
[
{
Id: 3,
BlogId: 2,
PostType: Article,
Title: Redis,
Labels:
[
NoSQL,
Cache
],
Tags:
[
Redis,
NoSQL,
Scalability,
Performance
],
Comments:
[
{
Content: First Comment!,
CreatedDate: 28/04/2010 22:58:35
}
],
NoOfComments: 1
},
{
Id: 4,
BlogId: 2,
PostType: Article,
Title: Couch Db,
Labels:
[
NoSQL,
DocumentDB
],
Tags:
[
CouchDb,
NoSQL,
JSON
],
Comments:
[
{
Content: First Comment!,
CreatedDate: 28/04/2010 22:58:35
}
],
NoOfComments: 1
},
{
Id: 1,
BlogId: 1,
PostType: Article,
Title: RavenDB,
Labels:
[
NoSQL,
DocumentDB
],
Tags:
[
Raven,
NoSQL,
JSON,
.NET
],
Comments:
[
{
Content: First Comment!,
CreatedDate: 28/04/2010 22:58:35
},
{
Content: Second Comment!,
CreatedDate: 28/04/2010 22:58:35
}
],
NoOfComments: 2
},
{
Id: 2,
BlogId: 1,
PostType: Article,
Title: Cassandra,
Labels:
[
NoSQL,
Cluster
],
Tags:
[
Cassandra,
NoSQL,
Scalability,
Hashing
],
Comments:
[
{
Content: First Comment!,
CreatedDate: 28/04/2010 22:58:35
}
],
NoOfComments: 1
}
]
*/
}
Hasil akhirnya adalah penyimpanan data yang diisi dengan data baru yang diisi persis seperti yang Anda inginkan - siap menyajikan fitur aplikasi baru Anda. Sebaliknya, mencoba hal di atas dalam solusi RDBMS tipikal tanpa downtime secara efektif merupakan prestasi ajaib yang dihargai dengan 999 poin Stack Overflow dan belasungkawa pribadi dari rektor besar @JonSkeet
Saya harap ini dengan jelas menggambarkan perbedaan antara kedua teknologi. Dalam praktiknya, Anda akan kagum dengan peningkatan produktivitas yang dimungkinkan saat Anda tidak perlu memodelkan aplikasi Anda agar sesuai dengan ORM dan RDBMS dan dapat menyimpan objek seperti memori.
Itu selalu ide yang baik untuk mengekspos diri Anda ke teknologi baru jadi jika Anda belum melakukannya, saya mengundang Anda untuk mulai mengembangkan dengan Redis hari ini untuk melihat manfaatnya bagi diri Anda sendiri. Untuk memulai semua yang Anda butuhkan hanyalah sebuah instance dari redis-server (tidak diperlukan konfigurasi, cukup unzip dan jalankan) dan C# Redis Client dari ServiceStack yang bebas ketergantungan dan Anda siap untuk pergi!