यह पृष्ठ यह दिखाने के लिए एक विशिष्ट उदाहरण के माध्यम से चलता है कि रेडिस और अन्य नोएसक्यूएल स्कीमा-रहित डेटा स्टोर का उपयोग करते समय दर्द रहित विशिष्ट डेटा माइग्रेशन कैसे हो सकता है।
सभी Redis ब्लॉग एप्लिकेशन पेज #
- Redis का उपयोग करके NoSQL डेटाबेस डिज़ाइन करना
- Redis और अन्य स्कीमा-रहित NoSQL डेटास्टोर का उपयोग करके दर्द रहित डेटा माइग्रेशन
स्कीमा-रहित NoSQL डेटास्टोर और Redis के साथ दर्द रहित डेटा माइग्रेशन #
विकासशील नया RDBMS बैक-एंड का उपयोग करने वाला ग्रीनफील्ड डेटाबेस सिस्टम ज्यादातर एक परेशानी मुक्त अनुभव है। सिस्टम के लाइव होने से पहले, आप पूरे एप्लिकेशन डेटाबेस को न्यूक करके एक स्कीमा को आसानी से संशोधित कर सकते हैं, और इसे स्वचालित डीडीएल स्क्रिप्ट के साथ फिर से बना सकते हैं जो इसे आपके नए स्कीमा में फिट होने वाले परीक्षण डेटा के साथ बनाएगा और पॉप्युलेट करेगा।
आपके आईटी जीवन में वास्तविक परेशानी आपकी पहली तैनाती के बाद होती है और आपका सिस्टम लाइव हो जाता है। उस समय आपके पास डेटाबेस को न्यूक करने और इसे स्क्रैच से फिर से बनाने का विकल्प नहीं होगा। यदि आप भाग्यशाली हैं, तो आपके पास एक स्क्रिप्ट है जो स्वचालित रूप से आपके पुराने स्कीमा से आपके नए में माइग्रेट करने के लिए आवश्यक डीडीएल स्टेटमेंट का अनुमान लगा सकती है। हालांकि आपके स्कीमा में किसी भी महत्वपूर्ण परिवर्तन में देर रात, डाउनटाइम, और नए डीबी स्कीमा में सफल माइग्रेशन सुनिश्चित करने के लिए एक गैर-तुच्छ प्रयास शामिल होने की संभावना है।
स्कीमा-रहित डेटा स्टोर के साथ यह प्रक्रिया बहुत कम दर्दनाक है। वास्तव में ज्यादातर मामलों में जब आप केवल फ़ील्ड जोड़ और हटा रहे होते हैं तो यह बिल्कुल भी मौजूद नहीं होता है। आपके डेटास्टोर को आपके स्कीमा के आंतरिक विवरण को नहीं समझने से, इसका मतलब है कि यह अब एक बुनियादी ढांचा-स्तर का मुद्दा नहीं है और जरूरत पड़ने पर इसे आसानी से एप्लिकेशन लॉजिक द्वारा नियंत्रित किया जा सकता है।
रखरखाव-मुक्त, स्कीमा-रहित और गैर-घुसपैठ होने के कारण रेडिस और इसके संचालन में बेक किए गए मौलिक डिजाइन गुण हैं। उदाहरण के लिए हाल के BlogPosts की सूची को क्वेरी करने से खाली सूची . के लिए वही परिणाम मिलता है जैसा कि यह एक खाली Redis डेटाबेस . में होगा - 0 परिणाम। चूंकि रेडिस में मान बाइनरी-सुरक्षित स्ट्रिंग हैं, आप उनमें जो कुछ भी चाहते हैं उसे स्टोर करने में सक्षम हैं और सबसे महत्वपूर्ण रूप से विस्तार से इसका मतलब है कि सभी रेडिस ऑपरेशंस डीडीएल जैसी 'मध्यवर्ती भाषा' की आवश्यकता के बिना आपके सभी एप्लिकेशन प्रकारों का समर्थन कर सकते हैं। क्या उम्मीद की जाए की कठोर स्कीमा। बिना किसी पूर्व आरंभीकरण के आपका कोड स्वाभाविक रूप से एक रेडिस डेटास्टोर से सीधे बात कर सकता है जैसे कि यह एक इन-मेमोरी संग्रह था।
यह समझाने के लिए कि व्यवहार में क्या हासिल किया जा सकता है, मैं स्कीमा परिवर्तनों को संभालने की दो अलग-अलग रणनीतियों के माध्यम से चलाऊंगा।
- कुछ न करें दृष्टिकोण - जहां फ़ील्ड जोड़ना, हटाना और फ़ील्ड प्रकारों के विनाशकारी परिवर्तन को स्वचालित रूप से नियंत्रित किया जाता है।
- कस्टम अनुवाद का उपयोग करना - पुराने और नए प्रकारों के बीच अनुवाद को अनुकूलित करने के लिए एप्लिकेशन स्तर तर्क का उपयोग करना।
इस उदाहरण के लिए पूर्ण चलाने योग्य स्रोत कोड यहां उपलब्ध है।
उदाहरण कोड #
एक सामान्य माइग्रेशन परिदृश्य प्रदर्शित करने के लिए, मैं BlogPost
. का उपयोग कर रहा हूं इसे मौलिक रूप से भिन्न New.BlogPost
. पर प्रोजेक्ट करने के लिए पिछले पृष्ठ पर परिभाषित प्रकार प्रकार। पुराने और नए प्रकारों की पूरी परिभाषा नीचे दी गई है:
पुरानी स्कीमा #
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; }
}
नई स्कीमा #
'नए संस्करण' में सामान्य ऐप विकास में आपके सामने आने वाले अधिकांश परिवर्तन शामिल हैं:
- जोड़े, निकाले और बदले गए फ़ील्ड
int
. का गैर-विनाशकारी परिवर्तनlong
. में औरdouble
फ़ील्ड- टैग संग्रह प्रकार को
List
से बदला गया है एकHashSet
. पर - एक जोरदार टाइप किए गए
BlogPostComment
. को बदल दिया ढीले-ढाले स्ट्रिंग में टाइप करेंDictionary
- एक नया
enum
पेश किया टाइप करें - एक अशक्त परिकलित फ़ील्ड जोड़ा गया
नए स्कीमा प्रकार #
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. कुछ न करें दृष्टिकोण - पुराने डेटा का नए प्रकार के साथ उपयोग करना #
हालांकि विश्वास करना मुश्किल है, बिना किसी अतिरिक्त प्रयास के आप केवल दिखावा कर सकते हैं वास्तव में कोई परिवर्तन नहीं किया गया था और पुराने डेटा को देखते हुए नए प्रकारों को स्वतंत्र रूप से एक्सेस करें। यह तब संभव है जब नए क्षेत्र प्रकारों के साथ गैर-विनाशकारी परिवर्तन (अर्थात जानकारी का कोई नुकसान नहीं) हो। नीचे दिया गया उदाहरण पुराने प्रकार के परीक्षण डेटा के साथ रेडिस को पॉप्युलेट करने के लिए पिछले उदाहरण से रिपॉजिटरी का उपयोग करता है। जैसे कुछ हुआ ही नहीं, आप नए प्रकार का उपयोग करके पुराने डेटा को पढ़ सकते हैं:
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. एप्लिकेशन लॉजिक का उपयोग करके डेटा माइग्रेट करने के लिए कस्टम अनुवाद का उपयोग करना #
उपरोक्त 'कुछ न करें' दृष्टिकोण के साथ कुछ कमियां यह है कि आप 'नामांकित फ़ील्ड' का डेटा खो देंगे। ऐसे समय भी आएंगे जब आप चाहते हैं कि नए माइग्रेट किए गए डेटा में विशिष्ट मान हों जो .NET अंतर्निहित डिफ़ॉल्ट से भिन्न हों। जब आप अपने पुराने डेटा के माइग्रेशन पर अधिक नियंत्रण चाहते हैं, तो कस्टम अनुवाद जोड़ना एक छोटी सी कवायद है जब आप इसे मूल रूप से कोड में कर सकते हैं:
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
}
]
*/
}
अंतिम परिणाम एक डेटास्टोर है जो नए डेटा से भरा हुआ है जिस तरह से आप इसे चाहते हैं - आपके नए एप्लिकेशन की सुविधाओं की सेवा के लिए तैयार है। इसके विपरीत, बिना किसी डाउनटाइम के एक विशिष्ट आरडीबीएमएस समाधान में उपरोक्त का प्रयास करना प्रभावी रूप से जादू का एक करतब है जो 999 स्टैक ओवरफ्लो पॉइंट्स द्वारा पुरस्कृत किया जा सकता है और इसके ग्रैंड चांसलर @JonSkeet 😃
से व्यक्तिगत संवेदना है।मुझे आशा है कि यह स्पष्ट रूप से दो प्रौद्योगिकियों के बीच अंतर को दर्शाता है। व्यावहारिक रूप से आप उत्पादकता लाभ से चकित होंगे जब आपको ओआरएम और आरडीबीएमएस के आसपास फिट होने के लिए अपने आवेदन को मॉडल करने की ज़रूरत नहीं है और वस्तुओं को स्मृति की तरह सहेज सकते हैं।
नई तकनीकों के बारे में खुद को उजागर करना हमेशा एक अच्छा विचार है, इसलिए यदि आपने पहले से ऐसा नहीं किया है, तो मैं आपको अपने लिए लाभ देखने के लिए आज ही रेडिस के साथ विकास शुरू करने के लिए आमंत्रित करता हूं। आरंभ करने के लिए आपको केवल रेडिस-सर्वर (कोई कॉन्फ़िगरेशन की आवश्यकता नहीं है, बस अनज़िप और रन) और निर्भरता-मुक्त सर्विसस्टैक के सी # रेडिस क्लाइंट की आवश्यकता है और आप जाने के लिए तैयार हैं!