RE:NODE

ბაზები10 წუთის საკითხავი

Valkey-ის მეხსიერება: maxmemory, eviction პოლიტიკები და დიდი გასაღებები

Valkey-ის მეხსიერების ზომა და მორგება: რას ითვლის maxmemory, eviction პოლიტიკის არჩევა, დიდი გასაღებების პოვნა, კომპაქტური კოდირებები და ფრაგმენტაციის კოეფიციენტი.

0 მკითხველი

ყველაფერი, რასაც Valkey ინახავს, მეხსიერებაში ცხოვრობს, ამიტომ მეხსიერება ის ზღვარია, რომელსაც რეალურად შეხვდები. ორი პარამეტრი წყვეტს, რა მოხდება, როცა შეხვდები: maxmemory, მონაცემთა ნაკრების ჭერი, და maxmemory-policy, რა უნდა გაკეთდეს ჭერთან. წმინდა ქეშისთვის დააყენე ჭერი და გამოიყენე allkeys-lru ან allkeys-lfu, და Valkey ჩუმად მოიცილებს ყველაზე ნაკლებად სასარგებლო გასაღებებს. სესიებისთვის, რიგებისთვის ან ყველაფრისთვის, რისი დაკარგვაც არ შეგიძლია, გამოიყენე noeviction, რომელიც მონაცემების წაშლის ნაცვლად ახალ ჩანაწერებს OOM შეცდომით უარყოფს - და დარწმუნდი, რომ ამას მოხდენამდე შეამჩნევ. volatile-* პოლიტიკები შუაში დგას და ერთ მახეს შეიცავს: ისინი მხოლოდ იმ გასაღებებს აძევებს, რომლებსაც TTL აქვს, ამიტომ თუ არცერთს არ აქვს, ზუსტად noeviction-ივით იქცევა. პოლიტიკის შემდეგ საქმე გაზომვაა: იმ გასაღებების პოვნა, რომლებიც მეხსიერებას იყენებს, პატარა სტრუქტურების კომპაქტურ კოდირებებში შენახვა და საკმარისი მარაგის დატოვება მეხსიერების იმ ნაწილებისთვის, რომლებიც შენი მონაცემები არ არის.

maxmemory: რას ზღუდავს და რას არა#

valkey.conf
maxmemory 400mbmaxmemory-policy allkeys-lrumaxmemory-samples 5

maxmemory ზღუდავს მონაცემთა ნაკრების მიერ გამოყენებულ მეხსიერებას, როგორც ის used_memory-ში ჩანს. როცა ჩაწერა მას გადააჭარბებდა, eviction პოლიტიკა ეშვება. მისი ნაგულისხმევი მნიშვნელობაა 0, რაც 64-ბიტიან სისტემაზე ლიმიტის არარსებობას ნიშნავს - Valkey გაიზრდება, სანამ ოპერაციული სისტემა ან კონტეინერი არ გააჩერებს, რაც ნებისმიერ eviction პოლიტიკაზე გაცილებით უარესი ჩავარდნაა. ყოველთვის დააყენე.

რას არ მოიცავს, ისეთივე მნიშვნელოვანია, როგორც ის, რასაც მოიცავს:

  • რეპლიკაციისა და AOF-ის ბუფერები ლიმიტში არ ითვლება, როცა წყდება, გაძევება საჭიროა თუ არა.
  • კლიენტის ბუფერები - მოთხოვნების ბუფერები და გამოსავლის ბუფერები ნელი მკითხველებისა და pub/sub გამომწერებისთვის - ნამდვილი მეხსიერებაა, რომელიც შენი გასაღებების გარეთ ცხოვრობს.
  • ფრაგმენტაცია - სხვაობა იმას შორის, რასაც allocator ფლობს, და იმას შორის, რასაც მონაცემები იყენებს - საერთოდ used_memory-ის გარეთაა.
  • Fork snapshot-ებისა და AOF rewrite-ებისთვის მუშაობისას copy-on-write-ის გამო შეიძლება ბევრ დამატებით მეხსიერებას საჭიროებდეს, როგორც აღწერილია პოსტში Valkey-ში შენახვა.

ასე რომ, პროცესის ნამდვილი ანაბეჭდი, used_memory_rss, ყოველთვის maxmemory-ზე მაღლაა, ზოგჯერ ბევრით. გავრცელებული საწყისი წერტილია maxmemory ხელმისაწვდომი მეხსიერების 60-დან 80 პროცენტამდე, უფრო დაბლა, თუ შენახვა ჩართულია და ჩაწერის სიხშირე მაღალია. CONFIG SET maxmemory 400mb მას მუშაობისას ცვლის, სადაც სერვერი კონფიგურაციის ცვლილებებს უშვებს.

Eviction პოლიტიკები#

პოლიტიკარას აძევებსვისთვის არის სწორი
noevictionარაფერს; ჩანაწერები OOM-ით ვარდებასესიები, რიგები, მონაცემები, რომლებიც არ უნდა დაკარგო (ნაგულისხმევი)
allkeys-lruყველაზე დიდი ხნის წინ გამოყენებულ გასაღებს, ნებისმიერსზოგადი ქეში
allkeys-lfuყველაზე იშვიათად გამოყენებულ გასაღებს, ნებისმიერსქეში სტაბილური ცხელი ნაკრებით
allkeys-randomშემთხვევით გასაღებსთანაბარი წვდომა, იშვიათად საუკეთესო არჩევანი
volatile-lruყველაზე დიდი ხნის წინ გამოყენებულს, TTL-იან გასაღებებს შორისქეში და მდგრადი მონაცემები ერთ სერვერზე
volatile-lfuყველაზე იშვიათად გამოყენებულს, TTL-იან გასაღებებს შორისიგივე, სიხშირეზე დაფუძნებული
volatile-randomშემთხვევით გასაღებს TTL-ითიშვიათად
volatile-ttlგასაღებს უახლოესი ვადითროცა TTL მნიშვნელოვნებას ასახავს

როგორ აირჩიო:

  1. ამ სერვერზე ყველაფერი ერთჯერადია? მაშინ allkeys-lru. გადადი allkeys-lfu-ზე, თუ გასაღებების მცირე ნაკრებს მუდმივად კითხულობენ და scan ან batch ამოცანა მათ გაძევებისკენ უბიძგებს - LFU ახსოვს, რომ გასაღები პოპულარულია, მაშინ როცა LRU-ს მხოლოდ ის ახსოვს, რომ მას ახლახან შეეხნენ.
  2. ამ სერვერზე რამე არ არის ერთჯერადი? მაშინ noeviction, და აკონტროლე მეხსიერება, რადგან სავსე სერვერი ჩანაწერებს უარყოფს.
  3. ნარევია? გულწრფელი პასუხი ორი სერვერია. volatile-* პოლიტიკები შერეულ სერვერს საშუალებას აძლევს, მხოლოდ ქეშის გასაღებები გააძევოს (რომლებსაც TTL აქვს) და მდგრადი შეინარჩუნოს (რომლებსაც არა), მაგრამ ისინი დამოკიდებულია იმაზე, რომ ქეშის ყოველი ჩაწერა TTL-ს აყენებდეს, სამუდამოდ, კოდის ყოველ გზაზე.

როგორ მუშაობს გაძევება სინამდვილეში#

Valkey არ ინახავს გასაღებების იდეალურ სიას, ბოლო გამოყენების მიხედვით დალაგებულს; ეს ყოველ გასაღებზე მეხსიერება დაჯდებოდა. ამის ნაცვლად, როცა გაძევება სჭირდება, ის რამდენიმე გასაღებს ნიმუშად იღებს და მათ შორის საუკეთესო კანდიდატს აძევებს, რაუნდებს შორის კარგი კანდიდატების მცირე pool-ს ინახავს. maxmemory-samples 5 ნაგულისხმევია; მისი 10-მდე აწევა ნამდვილ LRU-ს უფრო უახლოვდება, თითო გაძევებაზე ცოტა მეტი CPU-ს ფასად. პრაქტიკაში მიახლოება საკმარისად კარგია, რომ ვერ შეამჩნევ.

LFU იყენებს მცირე ლოგარითმულ მთვლელს თითო გასაღებზე, რომელიც წვდომით იზრდება და დროთა განმავლობაში ქრება. lfu-log-factor (ნაგულისხმევი 10) აკონტროლებს, რამდენად სწრაფად ჯერდება მთვლელი, ხოლო lfu-decay-time (ნაგულისხმევი 1, წუთებში) აკონტროლებს, რამდენად სწრაფად მცირდება, როცა გასაღები უმოქმედოა. OBJECT FREQ key აჩვენებს გასაღების მთვლელს, როცა LFU პოლიტიკა აქტიურია; OBJECT IDLETIME key აჩვენებს ბოლო წვდომიდან გასულ წამებს LRU პოლიტიკების დროს.

გაძევება ხდება მაშინ, როცა ჩაწერას მეხსიერება სჭირდება, თვითონ ამ ჩაწერის შიგნით. ამიტომ სავსე სერვერში ჩანაწერების დიდი ტალღა გაძევების სამუშაოს ჩაწერის გზაზე აკეთებს, რაც latency-ში ჩანს. lazyfree-lazy-eviction yes გაძევებული მნიშვნელობების რეალურ გათავისუფლებას ფონურ thread-ს გადასცემს, რაც გეხმარება, როცა გაძევებული მნიშვნელობები დიდია.

მთვლელები, რომლებსაც INFO stats-ში უნდა უყურო, არის evicted_keys, რომელიც იზრდება ყოველთვის, როცა პოლიტიკა რამეს აშორებს, და expired_keys TTL-ით წაშლილი გასაღებებისთვის. ქეშზე გარკვეული გაძევება ნორმალურია. სტაბილურად მზარდი სიხშირე ნიშნავს, რომ სამუშაო ნაკრები აღარ ეტევა, და hit ratio მასთან ერთად ეცემა.

რა იყენებს მეხსიერებას სინამდვილეში#

ყოველი გასაღები თავის სახელსა და მნიშვნელობაზე მეტი ჯდება. არის ჩანაწერი გასაღებების სივრცის hash ცხრილში, გასაღების სტრიქონი, ობიექტის სათაური მნიშვნელობისთვის, ვადის ჩანაწერი, თუ TTL აქვს, და allocator-ის დამრგვალება ყოველ გამოყოფაზე. პაწაწინა მნიშვნელობებისთვის ზედნადებმა შეიძლება მონაცემებს გადააჭარბოს - მილიონი გასაღები, რომელიც 4-ბაიტიან მთვლელს ინახავს, 4 MB-ზე გაცილებით მეტს იყენებს. Valkey 8.1-მა მთავარი hash ცხრილი უფრო კომპაქტური დიზაინით შეცვალა, რამაც თითო გასაღების ზედნადები შესამჩნევად შეამცირა, მაგრამ პატარა გასაღებები მაინც შედარებით ძვირია.

დიდი დაზოგვა კომპაქტური კოდირებებიდან მოდის. პატარა აგრეგატები ინახება ერთ შეკუმშულ ბლოკად და არა სრულ hash ცხრილად ან skip list-ად:

ტიპიკომპაქტური კოდირებაკომპაქტური რჩება, სანამ
Hashlistpackმაქსიმუმ hash-max-listpack-entries (128) ველი, მნიშვნელობები hash-max-listpack-value (64) ბაიტამდე
Sorted setlistpackმაქსიმუმ zset-max-listpack-entries (128) წევრი, თითო 64 ბაიტამდე
მთელი რიცხვების setintsetმაქსიმუმ set-max-intset-entries (512) წევრი
პატარა setlistpackმაქსიმუმ set-max-listpack-entries (128) წევრი
Listlistpack-ების quicklistკვანძის ზომას list-max-listpack-size ადგენს

OBJECT ENCODING key გეუბნება, რომელ კოდირებაშია გასაღები. Hash, რომელიც ზღვრებს გადააჭარბებს, სრულ hash ცხრილად გარდაიქმნება და უკან აღარ ბრუნდება. ორი პრაქტიკული შედეგი:

  • დააჯგუფე პატარა მნიშვნელობები hash-ებში. ერთი hash ასი პატარა ველით გაცილებით იაფია, ვიდრე ასი ცალკე გასაღები, რადგან ის ერთი გასაღების ზედნადებს იზიარებს და listpack-შია. მომხმარებლის პროფილის შენახვა HSET user:42 name ... plan ...-ით ჯობია SET user:42:name-სა და SET user:42:plan-ს.
  • შეინახე ველები პატარად, თუ შეგიძლია. 64 ბაიტზე მეტი ერთი ველიც კი მთელ hash-ს listpack კოდირებიდან აგდებს. ზღვრების აწევა ცოტა CPU-ს (listpack-ები წრფივად სკანირდება) მეხსიერებაზე ცვლის; რამდენიმე ასეულამდე მნიშვნელობები გონივრულია, თუ მეხსიერებაა შეზღუდვა.

დიდი გასაღებების პოვნა#

ერთი ზედმეტად დიდი გასაღები - სია, რომელსაც არავინ ჭრის, set ყველა მომხმარებლისა, ვინც ოდესმე შესულა, მთელი კატალოგის ქეშირებული blob - ხშირად თავისთავად ხსნის მეხსიერების პრობლემას. CLI-ს შეუძლია მათი პოვნა სერვერის დაბლოკვის გარეშე, რადგან გასაღებების სივრცეს SCAN-ით გადის:

bash
$ valkey-cli -h db.example.net -p 6380 --askpass --bigkeys$ valkey-cli -h db.example.net -p 6380 --askpass --memkeys$ valkey-cli -h db.example.net -p 6380 --askpass MEMORY USAGE session:9f2c41 SAMPLES 0

--bigkeys აჩვენებს თითოეული ტიპის უდიდეს გასაღებს ელემენტების რაოდენობით; --memkeys - მეხსიერებით. MEMORY USAGE გაძლევს ერთი გასაღების ბაიტებს, ზედნადების ჩათვლით; SAMPLES 0 აიძულებს, აგრეგატის ყველა ელემენტი გაზომოს და არა ხუთიდან შეაფასოს.

დიდი გასაღებები ზიანს თავიანთ ზომაზე მეტად აყენებს. ერთის წაკითხვა სერვერს ბლოკავს, სანამ პასუხი შენდება და იგზავნება. ერთის წაშლა DEL-ით ბლოკავს, სანამ მისი მეხსიერება თავისუფლდება - გამოიყენე UNLINK, რომელიც ფონურად ათავისუფლებს. და დიდი გასაღების ნაწილობრივ გაძევება შეუძლებელია: მეხსიერების წნეხის ქვეშ ის ან მთლიანად რჩება, ან მთლიანად მიდის. შეზღუდე სიები LTRIM-ით, იდენტიფიკატორების set-ებს მიეცი TTL ან როტაციის სქემა, და კატალოგის ზომის მნიშვნელობები თითო ელემენტის გასაღებებად დაყავი.

ფრაგმენტაცია#

INFO memory აჩვენებს როგორც იმას, რასაც მონაცემები იყენებს, ასევე იმას, რასაც პროცესი ფლობს:

bash
$ valkey-cli -h db.example.net -p 6380 --askpass INFO memory \    | grep -E '^(used_memory_human|used_memory_rss_human|mem_fragmentation_ratio|maxmemory_human|maxmemory_policy):'

mem_fragmentation_ratio არის RSS გაყოფილი used_memory-ზე. დაახლოებით 1.0-დან 1.5-მდე ჯანსაღია. ამაზე გაცილებით მაღალი ნიშნავს, რომ allocator მეხსიერებას ფლობს გვერდებში, რომლებიც მხოლოდ ნაწილობრივაა გამოყენებული, ჩვეულებრივ მას შემდეგ, რაც სხვადასხვა ზომის ბევრი გასაღები წაიშალა ან ვადა გაუვიდა. 1.0-ზე დაბალი ნიშნავს, რომ პროცესის ნაწილი swap-შია გატანილი, რაც ბაზაზე, რომელიც მეხსიერების სისწრაფის პასუხებს გპირდება, უარესი პრობლემაა. თითქმის ცარიელ სერვერზე კოეფიციენტი უაზროა - რამდენიმე მეგაბაიტი საბაზისო RSS პაწაწინა მონაცემთა ნაკრებზე დიდ რიცხვს იძლევა, რომელიც არაფერს ნიშნავს.

საშუალებები, თანმიმდევრობით: MEMORY DOCTOR გაძლევს დიაგნოზს უბრალო ენაზე; activedefrag yes სერვერს საშუალებას აძლევს, მნიშვნელობები ფონურად უფრო სავსე გვერდებში გადაიტანოს, თუ jemalloc-ით არის აწყობილი (Linux-ზე ნაგულისხმევი), active-defrag-threshold-lower-ითა და active-defrag-ignore-bytes-ით დაწესებულ ფარგლებში; MEMORY PURGE allocator-ს სთხოვს, თავისუფალი გვერდები გაათავისუფლოს. Restart ყველაფერს აბრუნებს, და ჩართული შენახვით ის იაფია, მაგრამ ეს უხეში ინსტრუმენტია.

მეხსიერება, რომელიც შენი მონაცემები არ არის#

დატოვე მარაგი maxmemory-ის ზემოთ იმისთვის, რასაც ის არ ითვლის:

  • კლიენტის გამოსავლის ბუფერები. ნელი მომხმარებელი, ან pub/sub გამომწერი, რომელიც კითხვას წყვეტს, პასუხებს სერვერის მეხსიერებაში აგროვებს. client-output-buffer-limit მათ ზღუდავს - ნაგულისხმევად pubsub 32mb 8mb 60, რაც ნიშნავს, რომ გამომწერი 32 MB-ზე ითიშება, ან 8 MB-ზე მეტით 60 წამის შემდეგ.
  • Copy-on-write შენახვისას. snapshot-ის ან AOF rewrite-ის დროს ჩაწერილი ყოველი გვერდი შვილობილის გულისთვის დუბლირდება.
  • კავშირები. ყოველი კლიენტი თავისი ბუფერებისთვის მეხსიერება ჯდება. გამჟონი pool-იდან ათასობით უმოქმედო კავშირი იკრიბება; CLIENT LIST და INFO clients მათ აჩვენებს.
  • Lua სკრიპტები და სკრიპტების ძრავა, მცირე, მაგრამ არა ნულოვანი.

Linux swap და OOM killer ფარავს, რას აკეთებს ოპერაციული სისტემა, როცა ჯამი ხელმისაწვდომს აჭარბებს, და ეს არასოდეს არის ის, რაც გინდა.

ზომის შერჩევა: გარჩეული მაგალითი#

არითმეტიკით შეფასება არასანდოა; ნიმუშებით შეფასება მარტივია. დავუშვათ, აპლიკაცია 200,000 სესიას შეინახავს. ჩატვირთე რეალისტური რამდენიმე ათასი სატესტო სერვერში და შემდეგ გაზომე:

sample_size.py
import statistics, redisr = redis.Redis.from_url("redis://:pass@db.example.net:6380/0")sizes = []for key in r.scan_iter(match="session:*", count=1000):    sizes.append(r.memory_usage(key, samples=0))    if len(sizes) >= 2000:        breakprint(f"keys sampled: {len(sizes)}, mean bytes: {statistics.mean(sizes):.0f}")

თუ საშუალო დაახლოებით 600 ბაიტი გამოვა, 200,000 სესიას დაახლოებით 120 MB მონაცემთა ნაკრები სჭირდება. დაამატე მარაგი ზრდისთვის და იმ მეხსიერებისთვის, რომელიც შენი მონაცემები არ არის - კიდევ ნახევარი გონივრული წესია ჩართული შენახვით - და დაახლოებით 180 MB-ს უყურებ, რაც ეტევა სერვერზე დაახლოებით 200 MB გამოსაყენებელი მოცულობით, მცირე მარაგით. გაშვების შემდეგ ისევ გაზომე INFO memory-ით, რადგან ნამდვილი სესიები ყოველთვის სატესტოზე დიდია.

სესიები Valkey-ში ფარავს სესიის მნიშვნელობების პატარად შენახვას, რაც ყველაზე იაფი მეხსიერებაა, რასაც ოდესმე დაზოგავ.

მეხსიერება RE:NODE-ის Valkey სერვერზე#

Valkey გეგმები მეხსიერებით იყიდება - 256 MB, 512 MB, 1 GB, 2 GB და 4 GB - და თითოეული მონაცემებისთვის გამოსაყენებელ მოცულობას ამის დაახლოებით 80 პროცენტად აღნიშნავს: დაახლოებით 200 MB, 400 MB, 800 MB, 1.6 GB და 3.2 GB. ზომა გამოსაყენებელი რიცხვის მიხედვით შეარჩიე. თუ თვითონ პროცესი გეგმის მეხსიერების ლიმიტს მიაღწევს, პლატფორმა კონტეინერს აჩერებს და სუფთად თავიდან უშვებს, swap-ის დაშვების ნაცვლად; ჩართული AOF-ითა და snapshot-ებით მონაცემები დისკიდან ბრუნდება, მაგრამ დაკავშირებული კლიენტები მოკლე გათიშვას ხედავენ, და crash watcher მას ითვლის. ახალ სერვერზე შეამოწმე maxmemory და maxmemory-policy INFO memory-ით, რომ იცოდე, რა ქცევას მიიღებ, როცა ის შეივსება, და გადადი უფრო მაღალ გეგმაზე, როცა evicted_keys ან მეხსიერების გამოყენება გეუბნება, რომ სამუშაო ნაკრები გაიზარდა.

FAQ#

რა არის ნაგულისხმევი eviction პოლიტიკა?

noeviction. როცა maxmemory მიიღწევა, ჩამწერი ბრძანებები OOM შეცდომით ვარდება და არაფერი იშლება. ეს უსაფრთხოა მონაცემებისთვის, რომლებიც უნდა შეინახო, და გამოუსადეგარია ქეშისთვის, ამიტომ ქეშის სერვერზე allkeys-lru ან allkeys-lfu მკაფიოდ დააყენე.

LRU თუ LFU ქეშისთვის?

LRU უსაფრთხო ნაგულისხმევია. LFU უკეთესია, როცა გასაღებების მცირე ნაკრები მუდმივად გამოიყენება და შემთხვევითი მასობრივი წაკითხვები - ანგარიში, crawler - სხვა შემთხვევაში ცხელ გასაღებებს გააძევებდა. თუ ვერ ხვდები, გამოიყენე LRU და გადართვის შემდეგ hit ratio-ები შეადარე.

რატომ იყენებს პროცესი maxmemory-ზე მეტ მეხსიერებას?

იმიტომ, რომ maxmemory მონაცემთა ნაკრებს ზღუდავს და არა პროცესს. კლიენტის ბუფერები, ფრაგმენტაცია და copy-on-write snapshot-ებისა და AOF rewrite-ების დროს ყველა მის გარეთაა. დატოვე მარაგი ლიმიტის მეხუთედიდან ნახევრამდე, იმის მიხედვით, რამდენად ინტენსიურად წერს სერვერი.

როგორ ვიპოვო, რომელი გასაღებები იყენებს ყველაზე მეტ მეხსიერებას?

გაუშვი valkey-cli --memkeys ან --bigkeys, რომლებიც გასაღებების სივრცეს დაბლოკვის გარეშე სკანირებს, და MEMORY USAGE key SAMPLES 0 საეჭვოებზე. ჯერ შეუზღუდავი სიები და set-ები მოძებნე; ჩვეულებრივ დამნაშავე ისინი არიან.

დამიბრუნებს Valkey მეხსიერებას გასაღებების წაშლის შემდეგ?

ნაწილობრივ. გათავისუფლებული მეხსიერება ახალი მონაცემებისთვის მაშინვე ხელახლა გამოიყენება, მაგრამ allocator მას ოპერაციულ სისტემას ყოველთვის არ უბრუნებს, ამიტომ RSS შეიძლება მაღალი დარჩეს და ფრაგმენტაციის კოეფიციენტი გაიზარდოს. activedefrag და MEMORY PURGE გეხმარება; restart ჩართული შენახვით მას სრულად აბრუნებს საწყის მდგომარეობაში.


კომენტარები

სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.

0/2000