आपने सुना है कि रेडिस में एक एम्बेडेड स्क्रिप्टिंग भाषा है, लेकिन क्या आपने इसे अभी तक आज़माया नहीं है? अपने रेडिस सर्वर के साथ लुआ की शक्ति का उपयोग करने के लिए आपको क्या समझने की आवश्यकता है, इसका एक दौरा यहां दिया गया है।
नमस्कार, लुआ!
हमारी पहली रेडिस लुआ स्क्रिप्ट वास्तव में किसी भी सार्थक तरीके से रेडिस के साथ बातचीत किए बिना एक मूल्य लौटाती है:
local msg = "Hello, world!"
return msg
यह जितना आसान हो जाता है। पहली पंक्ति हमारे संदेश के साथ एक स्थानीय चर सेट करती है, और दूसरी पंक्ति उस मान को रेडिस सर्वर से क्लाइंट को लौटाती है। इस फ़ाइल को स्थानीय रूप से hello.lua
. के रूप में सहेजें और इसे इस तरह चलाएं:
redis-cli --eval hello.lua
कनेक्टिविटी समस्याएं?
यह redis-cli
उदाहरण मानता है कि आप स्थानीय रूप से एक पुनर्वितरणकर्ता चला रहे हैं। यदि आप RedisGreen जैसे दूरस्थ सर्वर के साथ काम कर रहे हैं, तो आपको होस्ट और पोर्ट जानकारी निर्दिष्ट करनी होगी। कनेक्ट करें . ढूंढें अपने सर्वर के लिए लॉगिन जानकारी को तुरंत कॉपी करने के लिए अपने RedisGreen डैशबोर्ड पर बटन।
यह भी देखें:127.0.0.1:6379 पर Redis से कनेक्ट नहीं हो सका:कनेक्शन अस्वीकृत।
इसे चलाने पर "Hello, World!" प्रिंट होगा। EVAL
. का पहला तर्क पूरी लुआ स्क्रिप्ट है - यहाँ हम cat
. का उपयोग कर रहे हैं फ़ाइल से स्क्रिप्ट पढ़ने के लिए आदेश। दूसरा तर्क रेडिसकी की संख्या है जिसे स्क्रिप्ट एक्सेस करेगी। हमारी सरल "हैलो वर्ल्ड" स्क्रिप्ट किसी भी कुंजी तक नहीं पहुंचती है, इसलिए हम 0
. का उपयोग करते हैं ।
कुंजी और तर्कों तक पहुंच बनाना
मान लीजिए कि हम एक URL-शॉर्टनर बना रहे हैं। हर बार जब कोई URL आता है तो हम उसे संग्रहीत करना चाहते हैं और एक अद्वितीय संख्या वापस करना चाहते हैं जिसका उपयोग बाद में URL तक पहुंचने के लिए किया जा सकता है।
हम INCR
. का उपयोग करके Redis से एक अद्वितीय आईडी प्राप्त करने के लिए Lua स्क्रिप्ट का उपयोग करेंगे और यूआरएल को तुरंत एक हैश में स्टोर करें जो कि यूनिक आईडी से कुंजीबद्ध हो:
local link_id = redis.call("INCR", KEYS[1])
redis.call("HSET", KEYS[2], link_id, ARGV[1])
return link_id
हम पहली बार यहां call()
. का उपयोग करके Redis को एक्सेस कर रहे हैं समारोह।call()
रेडिस को भेजने के लिए तर्क हैं:पहले हम INCR <key>
, तो हम HSET <key> <field> <value>
. ये दो आदेश क्रमिक रूप से चलेंगे- इस स्क्रिप्ट के निष्पादित होने के दौरान रेडिस कुछ और नहीं करेगा, और यह बहुत तेज़ी से चलेगा।
हम दो Lua टेबल एक्सेस कर रहे हैं, KEYS
और ARGV
. टेबल्स सहयोगी सरणियाँ हैं, और डेटा की संरचना के लिए लुआ का एकमात्र तंत्र है। हमारे उद्देश्यों के लिए आप उन्हें किसी भी भाषा में एक सरणी के बराबर के रूप में सोच सकते हैं, लेकिन इन दो लुआ-आइम्स पर ध्यान दें जो भाषा के लिए नए लोगों की यात्रा करते हैं:
-
टेबल्स वन-बेस्ड हैं, यानी इंडेक्सिंग 1 से शुरू होती है। तो
mytable
में पहला एलिमेंट है।mytable[1]
. है , दूसरा हैmytable[2]
, आदि. -
तालिकाएँ शून्य मान नहीं रख सकतीं। यदि कोई ऑपरेशन
[ 1, nil, 3, 4 ]
. की एक तालिका देता है , इसके बजाय परिणाम[ 1 ]
. होगा — तालिका कोछोटा गया पहले शून्य मान पर।
जब हम इस स्क्रिप्ट को लागू करते हैं, तो हमें KEYS
. के मानों को भी पास करना होगा और ARGV
टेबल। कच्चे रेडिस प्रोटोकॉल में, कमांड इस तरह दिखता है:
EVAL $incrset.lua 2 links:counter links:url https://malcolmgladwellbookgenerator.com/
कॉल करते समय EVAL
, स्क्रिप्ट के बाद हम 2
. प्रदान करते हैं KEYS
. की संख्या के रूप में जिसे एक्सेस किया जाएगा, फिर हम अपने KEYS
. को सूचीबद्ध करते हैं , और अंत में हम ARGV
. के लिए मान प्रदान करते हैं ।
आम तौर पर जब हम रेडिस लुआ स्क्रिप्ट के साथ ऐप्स बनाते हैं, तो रेडिसक्लाइंट लाइब्रेरी चाबियों की संख्या निर्दिष्ट करने का ख्याल रखेगी। उपरोक्त कोडब्लॉक पूर्णता के लिए दिखाया गया है, लेकिन कमांड लाइन पर इसे करने का आसान तरीका यहां दिया गया है:
redis-cli --eval incrset.lua links:counter links:urls , https://malcolmgladwellbookgenerator.com/
--eval
का उपयोग करते समय ऊपर के रूप में, अल्पविराम KEYS[]
. को अलग करता है ARGV[]
. से आइटम।
बस चीजों को स्पष्ट करने के लिए, यहां हमारी मूल स्क्रिप्ट फिर से है, इस बार KEYS
. के साथ और ARGV
विस्तारित:
local link_id = redis.call("INCR", "links:counter")
redis.call("HSET", "links:urls", link_id, "https://malcolmgladwellbookgenerator.com")
return link_id
Redis के लिए Lua स्क्रिप्ट लिखते समय, एक्सेस की जाने वाली प्रत्येक कुंजी को केवल KEYS
द्वारा ही एक्सेस किया जाना चाहिए मेज़। ARGV
तालिका का उपयोग पैरामीटर-पासिंग के लिए किया जाता है - यहाँ यह उस URL का मान है जिसे हम संग्रहीत करना चाहते हैं।
सशर्त तर्क:increx और hincrex
ऊपर दिया गया हमारा उदाहरण हमारे URL-शॉर्टनर के लिए लिंक को सहेजता है, लेकिन हमें यह भी ट्रैक करने की आवश्यकता है कि कोई URL कितनी बार एक्सेस किया गया है। ऐसा करने के लिए, हम Redis में हैश में एक काउंटर रखेंगे। जब कोई उपयोगकर्ता लिंक पहचानकर्ता के साथ आता है, तो हम यह देखने के लिए जांच करेंगे कि क्या यह मौजूद है, और यदि ऐसा होता है तो इसके लिए हमारे काउंटर को बढ़ा दें:
if redis.call("HEXISTS", KEYS[1], ARGV[1]) == 1 then
return redis.call("HINCRBY", KEYS[1], ARGV[1], 1)
else
return nil
end
हर बार जब कोई शॉर्टलिंक पर क्लिक करता है, तो हम इस स्क्रिप्ट को यह ट्रैक करने के लिए चलाते हैं कि लिंक फिर से साझा किया गया था। हम EVAL
. का उपयोग करके स्क्रिप्ट को लागू करते हैं और पास करेंlinks:visits
हमारी एकल कुंजी के लिए और लिंक पहचानकर्ता हमारी पिछली स्क्रिप्ट से एकल तर्क के रूप में लौटा है।
स्क्रिप्ट बिना हैश के लगभग समान दिखेगी। यहां एक स्क्रिप्ट है जो एक मानक रेडिस कुंजी को तभी बढ़ाती है जब वह मौजूद हो:
if redis.call("EXISTS",KEYS[1]) == 1 then
return redis.call("INCR",KEYS[1])
else
return nil
end
SCRIPT LOAD and EVALSHA
याद रखें कि जब रेडिस लुआ स्क्रिप्ट चला रहा होता है, तो यह कुछ और नहीं चलाएगा। सबसे अच्छी स्क्रिप्ट केवल छोटे परमाणु डेटा संचालन की मौजूदा रेडिस शब्दावली को आवश्यक तर्क के सबसे छोटे बिट के साथ विस्तारित करती है। लुआ स्क्रिप्ट में बग एक रेडिस सर्वर को पूरी तरह से लॉक कर सकते हैं - चीजों को छोटा और डीबग करने में आसान रखने के लिए सबसे अच्छा।
भले ही वे आम तौर पर काफी छोटे होते हैं, लेकिन जब भी हम एक को चलाना चाहते हैं, तो हमें पूरी लुआ स्क्रिप्ट को निर्दिष्ट करने की आवश्यकता नहीं होती है। एक वास्तविक एप्लिकेशन में आप इसके बजाय अपनी प्रत्येक Lua स्क्रिप्ट को Redis के साथ पंजीकृत करेंगे जब आपका एप्लिकेशन बूट होता है (या जब आप परिनियोजित करते हैं), तो बाद में स्क्रिप्ट को उनके विशिष्ट SHA-1 पहचानकर्ता द्वारा कॉल करें।
redis-cli SCRIPT LOAD "return 'hello world'"
# "5332031c6b470dc5a0dd9b4bf2030dea6d65de91"
redis-cli EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0
# "hello world"
SCRIPT LOAD
. के लिए एक स्पष्ट कॉल EVAL
. के बाद से लाइव एप्लिकेशन में आमतौर पर अनावश्यक होता है निहित रूप से उस स्क्रिप्ट को लोड करता है जो इसे पास की जाती है। एक एप्लिकेशन EVALSHA
का प्रयास कर सकता है आशावादी रूप से और EVAL
. पर वापस आएं केवल अगर स्क्रिप्ट नहीं मिली है।
यदि आप रूबी प्रोग्रामर हैं, तो Shopify के वूल्वरिन पर एक नज़र डालें, जो रूबी ऐप्स के लिए Lua स्क्रिप्ट को लोड करने और संग्रहीत करने को सरल बनाता है। PHP प्रोग्रामर्स के लिए, Predis Luascripts को जोड़ने का समर्थन करता है, जैसे कि वे सामान्य Redis कमांड थे। यदि आप लुआ के साथ अपनी बातचीत को मानकीकृत करने के लिए इन या अन्य उपकरणों का उपयोग करते हैं, तो मुझे बताएं- मुझे यह जानने में दिलचस्पी होगी कि वहां और क्या है।
लुआ का उपयोग कब करें?
Lua के लिए Redis समर्थन कुछ हद तक WATCH
. के साथ ओवरलैप करता है /MULTI
/EXEC
ब्लॉक, जो समूह संचालन करते हैं, इसलिए उन्हें एक साथ निष्पादित किया जाता है। तो आप एक के ऊपर दूसरे का उपयोग कैसे करते हैं? प्रत्येक ऑपरेशन एक MULTI
. में होता है ब्लॉक को स्वतंत्र होने की जरूरत है, लेकिन लुआ के साथ, बाद के संचालन पहले के संचालन के परिणामों पर निर्भर कर सकते हैं। Lua स्क्रिप्ट का उपयोग करने से उन रेस स्थितियों से भी बचा जा सकता है जो WATCH
. के दौरान धीमे क्लाइंट को भूखा रख सकती हैं उपयोग किया जाता है।
हमने रेडिसग्रीन में जो देखा है, उसमें से अधिकांश ऐप जो लुआ का उपयोग करते हैं, वे भी बहु / EXEC का उपयोग करेंगे, लेकिन इसके विपरीत नहीं। सबसे सफल Lua स्क्रिप्ट छोटी होती हैं, और केवल एक ऐसी सुविधा को लागू करती हैं, जिसकी आपके ऐप को आवश्यकता होती है, लेकिन वह Redisvocabulary का हिस्सा नहीं है।
लाइब्रेरी में जाना
रेडिस लुआ दुभाषिया सात पुस्तकालयों को लोड करता है:आधार, तालिका, स्ट्रिंग, गणित, डिबग, cjson, और cmsgpack। पहले कई मानक पुस्तकालय हैं जो आपको किसी भी भाषा से अपेक्षित बुनियादी संचालन करने की अनुमति देते हैं। अंतिम दो ने रेडिस को JSON और MessagePack को समझने दिया- यह एक अत्यंत उपयोगी विशेषता है, और मैं सोचता रहता हूं कि मैं इसे अधिक बार उपयोग क्यों नहीं करता।
सार्वजनिक एपीआई वाले वेब ऐप्स में JSON के चारों ओर झूठ बोलने की प्रवृत्ति होती है। तो हो सकता है कि आपके पास सामान्य रेडिस कुंजियों में संग्रहीत JSON ब्लॉब्स का एक गुच्छा हो और आप उनके अंदर कुछ विशेष मानों तक पहुंच बनाना चाहते हों, जैसे कि आपने उन्हें हैश के रूप में संग्रहीत किया था। Redis JSON समर्थन के साथ, यह आसान है:
if redis.call("EXISTS", KEYS[1]) == 1 then
local payload = redis.call("GET", KEYS[1])
return cjson.decode(payload)[ARGV[1]]
else
return nil
end
यहां हम यह देखने के लिए जांचते हैं कि कुंजी मौजूद है या नहीं और यदि नहीं तो जल्दी से शून्य लौटाएं। फिर रेडिस से JSON मान को वेगेट करें, इसे cjson.decode()
with के साथ पार्स करें , और अनुरोधित मान लौटाएं।
redis-cli set apple '{ "color": "red", "type": "fruit" }'
# OK
redis-cli --eval json-get.lua apple , type
# "fruit"
इस स्क्रिप्ट को अपने Redis सर्वर में लोड करने से आप Redis में संग्रहीत JSON मानों को मान सकते हैं जैसे कि वे हैश थे। यदि आपकी वस्तुएं काफी छोटी हैं, तो यह वास्तव में काफी तेज है, भले ही हमें प्रत्येक एक्सेस पर मूल्य को पार्स करना पड़े।
यदि आप एक ऐसे सिस्टम के लिए आंतरिक एपीआई पर काम कर रहे हैं जो प्रदर्शन की मांग करता है, तो आप जेएसओएन पर मैसेजपैक चुनने की संभावना रखते हैं, क्योंकि यह छोटा और तेज़ है। सौभाग्य से रेडिस (ज्यादातर जगहों पर) के साथ, मैसेजपैक काफी ड्रॉप-इन-रिप्लेसमेंट है JSON के लिए:
if redis.call("EXISTS", KEYS[1]) == 1 then
local payload = redis.call("GET", KEYS[1])
return cmsgpack.unpack(payload)[ARGV[1]]
else
return nil
end
क्रंचिंग नंबर
लुआ और रेडिस में अलग-अलग प्रकार की प्रणालियाँ हैं, इसलिए यह समझना महत्वपूर्ण है कि रेडिस-लुआ सीमा पार करते समय मूल्य कैसे बदल सकते हैं। जब कोई संख्या लुआ से रेडिस क्लाइंट के पास वापस आती है, तो यह एक पूर्णांक बन जाती है - दशमलव बिंदु के बाद का कोई भी अंक गिरा दिया जाता है:
local indiana_pi = 3.2
return indiana_pi
जब आप इस स्क्रिप्ट को चलाते हैं, तो रेडिस 3 का पूर्णांक लौटाएगा - आप पाई के दिलचस्प टुकड़े खो देंगे। काफी सरल लगता है, लेकिन जब आप स्क्रिप्ट के बीच में रेडिस के साथ बातचीत करना शुरू करते हैं तो चीजें थोड़ी अधिक मुश्किल हो जाती हैं। एक उदाहरण:
local indiana_pi = 3.2
redis.call("SET", "pi", indiana_pi)
return redis.call("GET", "pi")
यहां परिणामी मान एस्ट्रिंग है:"3.2"
क्यों? रेडिस के पास एक समर्पित संख्यात्मक प्रकार नहीं है। जब हम पहली बार SET
मूल्य, रेडिस इसे एक स्ट्रिंग के रूप में सहेजता है, इस तथ्य के सभी रिकॉर्ड खो देता है कि लुआ ने शुरू में मूल्य को एक फ्लोट के रूप में सोचा था। जब हम बाद में मान निकालते हैं, तब भी यह एक स्ट्रिंग होता है।
Redis में वे मान जो GET
. के साथ एक्सेस किए जाते हैं /SET
INCR
जैसे सांख्यिक संक्रियाओं को छोड़कर स्ट्रिंग्स के रूप में सोचा जाना चाहिए और DECR
उनके खिलाफ चलाए जा रहे हैं। ये विशेष संख्यात्मक संचालन वास्तव में पूर्णांक उत्तर लौटाएंगे (और गणितीय नियमों के अनुसार संग्रहीत मूल्य में हेरफेर करें), लेकिन रेडिस में संग्रहीत मूल्य का "प्रकार" अभी भी एक स्ट्रिंग मान है।
गोचस:एक सारांश
रेडिस में लुआ के साथ काम करते समय ये सबसे आम त्रुटियां हैं जो हम देखते हैं:
-
सबसे लोकप्रिय भाषाओं के विपरीत, लुआ में टेबल्स एक-आधारित हैं। KEYS तालिका में पहला तत्व है
KEYS[1]
, दूसरा हैKEYS[2]
,आदि. -
लुआ में एक शून्य मान एक तालिका को समाप्त करता है। तो
[ 1, 2, nil, 3 ]
स्वचालित रूप से[1, 2]
बन जाएगा . तालिकाओं में शून्य मानों का प्रयोग न करें। -
redis.call
अपवाद-शैली लुआ त्रुटियों को बढ़ाएगा, जबकिredis.pcall
स्वचालित रूप से किसी भी त्रुटि को ट्रैप कर देगा और उन्हें उन अस्टेबल्स को वापस कर देगा जिनका निरीक्षण किया जा सकता है। -
रेडिस को भेजे जाने पर लुआ संख्याओं को पूर्णांक में बदल दिया जाता है - दशमलव बिंदु के बाद का सब कुछ खो जाता है। किसी भी फ़्लोटिंग पॉइंट नंबरों को वापस करने से पहले उन्हें स्ट्रिंग में बदलें।
-
अपनी Lua स्क्रिप्ट में उपयोग की जाने वाली सभी कुंजियों को
KEYS
. में निर्दिष्ट करना सुनिश्चित करें तालिका, अन्यथा आपकी स्क्रिप्ट शायद रेडिस के भविष्य के संस्करणों में टूट जाएगी। -
लुआ स्क्रिप्ट रेडिस में किसी भी अन्य ऑपरेशन की तरह हैं:निष्पादित होने के दौरान और कुछ भी नहीं चलता है। लिपियों को रेडिस सर्वर की शब्दावली का विस्तार करने के तरीके के रूप में सोचें - उन्हें संक्षिप्त और सटीक रखें।
आगे पढ़ना
ऑनलाइन लुआ और रेडिस के लिए बहुत सारे महान संसाधन हैं - यहां कुछ Iuse हैं:
- EVAL डॉक्स
- रेडिसग्रीन की लुआ स्क्रिप्ट लाइब्रेरी
- लुआ संदर्भ मैनुअल
- लुआ ट्यूटोरियल निर्देशिका