 So I'm going to talk about memory optimization and specifically, you know, the idea is that it registers everything in memory, right? So if you're, you know, unless you've got a lot of money to burn on RAM, you need to start thinking about how do you optimize your data structures so that, you know, you can optimize. I mean, you don't have to spend a lot of money on RAM. So that's the goal of my topic. The way I'm trying to structure this is, you know, talk about three things, internal data structures for Redis. So Redis has got five data types and, you know, but internally, how are they stored? What is the internal representation when Redis is using in memory? So I'll spend a little time on that and that will, that will help me to go on to the next topic, which is once you understand how Redis is storing data internally, what can you do as a developer to make the best use of memory? So that's, that's how, so I'll have a certain set of tips and then every once in a while you end up with problems there in production, you're using a lot of memory and you want to find out which key is causing the problem or what set of data is causing the problem. So how do you diagnose that? So there's some tools which can help you in that. So I'm going to start with the internal representation, right? So this is a, this is a very general statement, but Redis is sort of storing everything in a big hash map or a big dictionary, right? So you've got a top level namespace where you've got all the keys and the keys can only be strings, right? And the values, they can be some of these five data types, right? So these are the five data types that Redis supports. And so at a very general level, Redis only has one big dictionary and these five data types, right? So the reason I bring this up is even if you're storing a two byte key and a two byte value, you know, what you have to understand is because it is being stored in a dictionary, that dictionary has its overheads. And so even if you're storing small strings, there is an overhead because you're storing them in a larger dictionary, right? And so if you're storing a set, the set would have a key, that key is what is there in the dictionary and the set is stored separately outside the dictionary as a pointer sort of. So from moving on from here, the internal data types that Redis has, you know, these are the six or seven, there's another one, but sort of deprecated, but these are the data types that Redis uses internally to store all data in memory, right? What I'm trying to go through this real quickly, the objective is just understand a little bit of how this is stored and, you know, laid out in memory so that you know, when I'm storing a piece of data, how does it store and what can you do to optimize it? So real quick, strings, you know, plain old byte array sort of a thing, dictionaries, w link list again, I'll have a slide later on. So actually, let's just go ahead. So the first data type is the most common, which is string. And string is actually a misnomer. When you talk about strings, we're talking usually about characters, but in Redis string consider string as a byte array. It is, it is, you know, just an array of bytes and you can store any sort of data in that. So it's not that you can only store characters and text. You can store any sort of binary data. You can store long integers, big arrays and internally string data structure is used as a building block for other data structures, right? So if you're storing, say it will come up later, but then if you're storing a small list or a small set, then Redis will do optimization to store it internally in a string, which, which is actually just a byte array, right? So this is, this is sort of the basic building block. Two important things about a string is that it's a byte array. And the second thing is that it is a prefix. The, the length of the string is prefixed. So, I mean in C, I mean Redis is written in C, right? A string, the size of a string, if you want to find out, you have to go until you find the trailing slash zero. But in Redis, the way it is implemented is that the length of the string is before, is known beforehand. So, yeah, so this is, this is one of the fundamental data structures. Sorry, it depends on the length of the string, right? I mean, it's just, yes. No, I'm sorry. The, there's a limit to the size of the string, and so it just takes that. It's a prefix, it's a fixed size. It's not variable. It's a dictionary. So dictionary is basically a hash table. And this is again, I mean, after strings, this is the second most common. So hash tables or dictionary, the top level thing in C, I mean, in Redis is a dictionary. So all your top level keys are stored in a dictionary and the values could be any of the five data structures, right? That, yeah. But hash tables are also used internally to implement the set. So you've got these logical data types, right? So Redis has the logical data type called set. Now internally, a set is implemented using a hash table, right? So similarly, so you've got your top level keys stored in a hash. The set itself, the elements of a set are stored in this. And then you've got the hash data structure, right? Where you can store key value pairs. So that itself is a hash data structure. So these are the three main places where hash is used. One thing that influences memory is that there are actually two hash tables when you're talking in Redis, right? So if you're familiar with the hash data structure, as you keep on adding more elements to the hash, you sort of end up into a chain-like situation. So hash is implemented as buckets, right? And so when a bucket, when you add another element to the bucket, it sort of maintains a link list and you keep on adding. So if the hash table is not implemented correctly, at some point of time it will degenerate into a link list, where even if you have a key, it will keep on trying to find out in that bucket, right? So in order to avoid that, Redis maintains two hash tables. And it does something called as incremental hashing. So what it does is every time you do a hash operation, it will try to migrate some of the keys from one table to another. So the purpose of this is because you've got two hash tables, it is sort of, you know, it is good in one sense that you're not degenerating into an order n retrieval time, but it is also bad in the sense that you now have, you know, you've got two data structures and so you have to maintain that and therefore there is more memory to it, right? So anyways, the short summary is that dictionary is used for your top-level key, it's used for your hash set, and it's used for the hash data structure. So three things where hash is or dictionary is used. W linked list, there's nothing really great about it. This is the textbook implementation of W linked list. And if you look at the source code, it's really very simple to implement implementation. W linked lists are used for the list data type, right? The only important thing that I want to remember is that to store one element, so let's say your data is in your node, right? To store one integer, you're also maintaining two pointers, a previous and a next pointer. If you're storing a list of just say integers, the overhead for a W linked list is too high, because you're storing an integer of 8 bytes and you also have two pointers. So essentially you've used three times the memory that you would otherwise have, right? So the reason I'm bringing this up is that you just keep keep track of how many pointers that data structure has because that pointers are what is costing memory. This is a slightly more advanced data structure that Redis uses. It's called skip list. And the way I like to think about it is that you think of it as a railway tracks, right? Or railway lines. So you've got these major stations and you've got these intermediate stations. If you want to go from one station to another, what you would do is you would typically take a fast train and go to your closest major station. And then from there go to the slower track. Right? So sort of the same metaphor. So, you know, you've got these direct lines to these intermediate nodes. So for example, four is like a major station in that sense. And six is like a major station. So if you want to go from one to five, right? I mean, you want to search the element five, right? In your in your list, right? So the way you would do is you know that five is this is going to be sorted always. So because you want to search the element five, what you do is you go to the station, which is sort of or you go to the node, which is closest to five, right? And so you would go to four and then from there. So the way you go is from one, you can directly jump to four. So you don't have to. If this was a link list, you have to go through each node one at a time, right? But because you've got direct node connections to, you know, a more intermediate level, right? You can sort of jump. So probabilistically speaking, you still have like a log n retrieval time for any node. Yes, the nodes, the elements. So this is this is the basis for your sorted set, right? So for your sorted set, your elements are sorted in memory, right? So these are your nodes, the ones in yellow. These are the nodes, your data nodes. They're sorted. And now that you want to find out a node in the middle, right? The only way to go through this, I mean, assume that you don't have these things, right? All of these ones on the top, you don't have them, right? So then it just becomes a single list, right? And if you want to find out the data corresponding to the node number 7, you have to start from 1, 2, 3, 4, 5, 6, 7. Correct? There's no other way. But then what you do is if you maintain shortcuts, these are shortcut pointers to some intermediate nodes, right? And because you're having these shortcut pointers, now you can find out a value of a node in a much more efficient manner. Make sense? So you've got these, you've got these nodes, which when you start from here, right? You sort of, sorry, I missed your question again. So, right? So the way you do is basically you start at, when you're trying to find out ringer, you try to find out at the highest one, and then you go and then you see if it is, if that value is, for example, is lesser, the value that you're searching is lesser than that. So you go one level lower, and then you will search from there. So you could either start from the lowest level, I mean basically you start from the highest level, right? And then you would, it's sort of like a binary search in that there will be some hit or miss, and then you accordingly go one level lower or you go to one level higher. Correct? Yes? Yes. So what it does is it uses, the way this tree is maintained is that it sort of uses probabilistically not every time it will be there. So yes, there are some overheads to just maintaining this structure. Yes? Correct. Yes. Yes. This is sort of in contrast to a hash map. I mean another way to build, if you want to find out by key, you want to find out the value, the other structure would be hash map. But the advantage of a skip list over hash map is that your elements are now sorted. And so, I mean we took the use case of finding one element. But let's say that I want to find out a range of elements, which is what your sorted set also lets you do. Let's say that I want to find out all elements from 2 to 7. Right? So this is much more efficient over here because it's, you start at this point and you just get all the nodes sequentially. Right? So this structure is sort of useful as compared to a hash map in that sense. Right? This is the underlying structure used for sorted set. And if you look at it, there's a whole bunch of pointers going around over here. So as you can imagine, this is going to be an expensive data structure to maintain just because you've got a lot of overheads in maintaining the number of pointers. Right? So from a memory perspective, this is, this is all of, you know, too expensive. Can I just move on? Any questions on this? Yes. That is, that is intentional. So every, it sort of uses a probabilistic way of finding out how many levels a particular thing can have. Like you don't, I mean, if you talk about, I mean, the, the railway metaphor sort of breaks because you cannot decide beforehand which is a major station versus not a major station. So you sort of use probability to decide which nodes should have more levels versus shouldn't have. Moving on. So now there are, so at some point of time, Salvatore and United as sort of decided that he wanted to have more memory efficient data structures. Right? And so he came up with a few other data structures. Zip list is one of such data structure. So zip list is, think of it as like a doubly linked list, but except it does not use pointers. It's just compressed all of those linked list elements into one flat array. Right? So you've got these, everything is sort of stored in one array. And as a result, you don't have, you don't have a lot of wastage in terms of the pointers that you would otherwise have in a doubly linked list. Zip lists internally are used for smaller lists, smaller hashes and smaller sorted sets. So if you've got, if you say you've got only 50 elements in a set or sorry, in a hash map or something, right? Then internally Redis would use this data structure and because it is using this data structure, the memory utilization will be lower. Right? There's a drawback as well because it is, everything is in an array. It's not as efficient as compared to the previous data structures. So there is a cost benefit and trade off over here. If you use this data structure, you are consuming less memory, but the CPU, you're wanting more CPU cycles. So at some point of time, at some number of elements, it becomes more practical to use the traditional conventional data structures, but if it is a lower number of elements, then you would rather use zip lists. There is, there is something under your control. There are some parameters which are under your control, which I will just talk about in a moment. But the idea is that, you know, if you want to, I mean, Redis makes some default settings, right? As a developer, you may decide that my application, I'm okay to, I mean, I want to use lesser memory and I can afford to burn CPU cycles. You have that sort of situation you could tweak it accordingly. So Redis goes with some default. If you know your application and you know your data, you can tweak those parameters according to your needs, right? Okay, the other data set is integer sets. And you know, it's just a fancy word for a sorted integer array, right? So if you, I mean, if you're storing set, set of numbers, right? A set of numbers just basically means that there are no duplicates. The traditional way of implementing a set is to build a, is user hash table, right? And then basically a hash table will automatically guarantee that there are no duplicates, but this is an alternate mechanism. So if you are under a special scenario, which is if you are using a set and that set is only containing numbers, then Redis can sort of use a sorted array. And so the way a sorted array would work is basically, if you want to find out if a, if you want to check the existence of an element, you would do a binary search. So let's say that you want to find out if 115 exists, you will sort of start at the middle and then if it is, you know, you do a sort of binary search, right? And if it exists, then you will know. So that's how the set works, right? The integer set works. The big advantage is that this is really, really efficient memory-wise, right? And Redis does whole bunch of things. Like if your numbers are 16-bit numbers, then it is a 16-bit character array, 16-bit integer array. But if it is, you know, only 8-bit, then it will use a character array. You know, it will sort of byte array sort of. So it sort of optimizes on the size of your integers as well. And so from a memory efficiency perspective, this is super efficient. The drawbacks, it can only be used for integers. So if you want a set of say strings, you cannot use integer sets or Redis cannot use integer sets, right? The other thing is if you look at the documentation for a set, checking the existence of a member is order one, right? In a set, the checking the existence of a member is order one. But in this, if you look at it, to check the existence of a number, the time complexity is going to be log n because you are doing sort of a binary search, right? So the idea is that if it is a small enough set, it is not that many operations, it is still considered as order one. But if it goes beyond the protein limit, this no longer becomes efficient, right? So again, as I said, there is a trade-off here. If it is a smaller set, you would rather use integer sets or if you are concerned about memory, you would use this. But for larger sets, you would just use to stick to the hash table implementation, right? And Redis does this automatically. But, you know, we will see how we can use that to our advantage to reduce memory. Any questions on this? This has no relation to skip list now. For this, why is this sorted? Is your question? You know, it is sorted because you want to... I mean, if it is not sorted to find out the existence of a member would be order n. So if you maintain it sorted, it sort of... No, no, no. But this is not a sorted set. This implementation is used only for the set data set, yes. Because sorted set also has associated value of a score, right? Which is not over here, not present over here, okay? So a quick summary of what we just covered, right? So on the left are the logical data structures which you would see listed on Redis.io, right? So string hash set, sorted set and list. These are your data structures. Now internally, how are they stored, right? So string is stored as a string, just slash 0 terminated string. A hash can either be a hash table or it can be a zip list, right? Which one is used depends on the size of the hash, depends on your settings in your Redis file, configuration file. Similarly, with a set, it could use a hash table implementation or it could use an integer set. Now the constraints for... When does it use an integer set? If the set is only containing integers and numbers, then it could start using an integer set. Otherwise it would just take the hash table, right? And of course the ones on your right hand side are more memory efficient. The ones on your left are sort of, you know, they take more memory, but they are general purpose data structures, okay? A sorted set, that is like the, you know, that is the most memory hungry data structure that you would find in Redis. A sorted set needs two data structures, a hash table and a skip list, right? So it needs the hash table to maintain the scores and the skip list. So the combination of the hash table and the skip list together makes a sorted set, right? So as you can imagine, sorted set because it is using a hash table and a skip list and because the skip list has so many pointers in it, it consumes the most memory, right? If the sorted set is sufficiently small, it can be compacted into a zip list and stored accordingly, right? And finally list, list is again would be a doubly linked list in the general case, but under special circumstances it could use a zip list, right? So this is basically the, you know, quick summary of all the internal data structures Redis has. We come back to this in trying to figure out how can we use this to our advantage to use less memory? Yeah, there are some settings which I will just speak about. Yes, it will automatically convert it into a hash table. Yes, it will, yes. Yes, so that is a great question. Internally Redis will keep on toggling between one data structure and another. So, you know, if you sort of break the invariance, like you start with a small list, right? And you start adding elements. At some point of time it will go beyond your defaults, right? It will go, it will become big enough list that you cannot store it in a zip list. So then Redis will convert that into a hash table and it sort of happens all behind scenes without, you know, without you having to do anything. Sorry, there will be some cost to it. Yes, there is going to be some cost to it. Yes, there are some settings in which you can say that, so that is global setting. You cannot influence it at a key-based level. You cannot. So it is a global setting. Okay, so now we are just trying to look at what can we do to reduce our memory consumption, right? So the first thing is avoid small strings or basically avoid, there is a string data type, right? In which the key is a string and the value is also a string, right? And that consumes a lot of memory. The reason it consumes a lot of memory is that even though you have strings, the strings themselves need to be a part of your dictionary, your global dictionary. And so they're going to consume memory, right? And so one of the one of the basic tips is to split it up into smaller dictionaries, right? So let's I mean this example. I mean, I'm not sure if you could see it all, but what it's trying to do is it's trying to store user names and some current data responding to those user names, right? So what you could do is you could split up the key in a first portion of the key into your top level keys. And then these are your dictionaries. These are your hashes, right? So you've taken AR and then you said, okay, everything starting with AR, all users starting with AR will now, I will now store their data in a smaller dictionary, in a separate dictionary, right? I mean, this is just an example I came up with, but just going back to the earlier presentation where I think they were storing rate limiting, right? And then you had your keys split up by namespace and then some set of parameters. So you could very well have your namespace as your first key. And then your secondary hash table could then just be a smaller set of values inside that, right? It does not. You have to do this on your own. So the way you have to manipulate this data is you let's say that I mean as far as you are concerned, you're just trying to store Arpit with user data, Arjun with user data, right? As far as you are concerned, but now what you would have to do is in your application logic, you would have to split up the string into two portions and you would say, okay, I'll split it up into two portions that whatever that is, that is application specific. The first portion becomes the key for a hash table, right? And the second portion becomes in this hash table, it becomes another key. I mean, if you go back to your hash data structure, how do you manipulate a hash data structure, right? You've got a top-level key and then inside that hash, you could have another key, right? And this turns to be super efficient memory-wise. Another thing is because this hash table or this hash itself is small enough, Redis could very well compress this and store it in a ziplist. It doesn't have to make another hash table for this. It could just make it a ziplist, right? There are some drawbacks of this one is of course complicated logic on your, I mean, slightly more complicated logic on your application side. The other side is the example, I mean, if you have a top-level keys, you can have an expires like, you know, like they wanted. You know, their use case was they wanted to have expires on top-level keys. Over here, you cannot expire on this, on the key called up with right now in this way, right? But it still has advantages. Like, I mean, if I just go back to the example that was discussed earlier, where you wanted to have rate limiting on keys, what you could do is you could group your keys into buckets which expire together or into keys which expire together. So, for example, you know that a lot of keys are going to expire at the end of the hour, right? So, if you ensure that all of them fall in the same hash table or, you know, same internal data structure, hash table, right? You could then set an expire on that key. So, all your rate limiting that expires at the end of the hour will expire together, right? So, you could use that to your advantage. You don't necessarily have, you don't necessarily need expires at individual key. You can sort of use expires at a, you know, at a grouping, right? Any questions on this? Can I just move on to the next? Yeah, I mean, in this case, we just considering string, right? This is a hash table on the other side. So, this would be some string. Yeah, no. No, so Redis does not have a, I mean, I'm not sure Redis, I mean, Mcast has a limitation of 1 MB, but Redis does not have Redis. The string itself has a limitation of, I don't know, some, it's a big number. It's not, it's nowhere close to that 4 MB, 5 MB. It's pretty big. I think 2 raised to 32 is the size of a string and that is the maximum size of a string that you can store, which is, yeah, I mean, maybe, okay? Yeah, the other is maximize the usage of Zip List, right? So, if this goes back to this thing, right? To save memory, you need to get, you need to force Redis to start using Zip List. So, the two parameters that you can configure in your configuration, one is list max Zip List entries. And what that says is, what is the size after which, you know, Redis should start using, I mean, so let's say that this value is set to 500 or 512, right? So, which means that Redis will use the Zip List for any data structure which has got 512 elements. If it is used, if the number of elements in your list goes beyond 512, then it will convert it into a doubly linked list, right? And the other setting is what should, what is the maximum size of a value? Like for example, if you've got a value of that object itself is like storing a 2 MB string, right? Then it may not be efficient to use a Zip List. So, those are the two things. What is, what is the size or length of your list? And what is the maximum value of a particular element, right? So, you could use these two to your advantage, right? Now, Redis ships with some defaults, right? Which work for a large number of use cases. But then if, in your application, if you're ready to trade off, you want to really save memory, right? So, what you could do is you could just, depending on how many elements you expect a list would contain, you could tweak the setting accordingly. So, you know, if you know that you're only going to store 600 or 700 lists, elements in a list, you could just bump it up a little higher and then it will start using your, you know, Zip List data structure, right? So, these are the two things which, what you could do is, you know, from your production, you could just take a dump of your data and analyze it and find out on an average how many elements are you storing in a list, right? So, you should look at your data, find out how many elements you are storing in your list and then see if it is worthwhile to change the setting accordingly. You know, do you want to move it up or do you want to move it down in however that works? So, Redis ships, I think it's 512, I'm not wrong. It ships with certain defaults, right? But then what is the right value depends on your application and what trade-offs you are willing to make, right? I mean, of course, beyond a certain, if you just make it 1 million, you're losing all the advantages of Redis because then your operation is not going to be as fast, but there's some amount of leeway that you have depending on your application. A third thing is to use integer IDs, right? So, let's say that you've got users or any objects that you're storing, right? If you use integers as your identifiers, it has some advantages. Generally, Redis uses lesser memory for IDs, right? Especially if you're storing these IDs into a set or if you're storing these IDs in a list, then Redis would optimize the way it is stored. So, let's say, I mean, if you've got primary keys, right? You typically store your primary keys into some collection, right? Like a list or a set. So, if you're using integers, it will use lesser memory. This is sort of obvious. If you can try to avoid sorted sets, if there is a way in which you can use a dictionary and get around with it or just a set, you know, that would be helpful. You would save a lot of memory just by avoiding sorted sets, right? You could also, I mean, sorted sets also go and use a zip list if it is reasonably sized. So, you know, you could also find out what is the maximum size you expect for your sorted set and accordingly change your configuration so that you force a zip list. Yeah, this is sort of a hack. And, you know, if you're really pressed for memory, this is another thing that you could do. So, let's say that you're storing user objects in a dictionary, right? You've got one million user objects that you're storing, maybe as a cache, right? Now, your user has got certain keys like first name, last name, age, I mean, department, whatever. These keys are going to be repeated every time you're storing it in a dictionary, right? These keys, Redis is not smart enough saying, okay, you know what, all of these objects are sort of homogeneous and I can just optimize and not storing the keys. No, so Redis doesn't do that, right? But if you know that your objects are similar, you know, and what you could do is if you guys are familiar from the Python world, Python has got something called as named tuple, right? So, this is sort of that same idea where, you know, you're just taking an object and just storing it as a list, as a sequence of elements, right? And then you're using a list, right? So, when you do that, you end up saving a lot of memory, right? I mean, especially if you know that your objects are homogeneous and you're storing a lot of them, you could make sense to use this. Now, I mean, of course, this sort of increases the complexity on your application side. So, you need to abstract that piece of code and, you know, make sure that it is separate and, you know, it's well-contained, right? You sort of need an abstraction layer about this, right? But this ends up saving you a lot of memory. You know, sorry, okay, yes. No, I mean, it doesn't really matter. I mean, if you, so for example, you're storing user IDs and along with that, you want to store a user object. That's what this use case is. So, let's say that you've got 10 million users, right? And you've got a user ID and corresponding to that user ID, you've got a user object. The traditional way to do it is the key is your user ID and your value is a hash table. The hash table internally, hash map or dictionary, whatever you call it. And that internally has your keys as first name, last name, age, something, you know, like that. No, why not? So, if you know, if you do not, right? Because list also, it is now positional. So, if you want to update my last name, you know that I have to go and update the list at that particular index. Yes, I mean, yes, you're right. You cannot do, I mean, it's not, the right is not going to be as efficient as this, yes. I'm saying you're right, yes. It makes sense for a read intensive case, which I mean, if you're using, I mean, I don't, I mean, the sort of applications we tend to read is this, but if your application is different where, you know, you're trying to have a lot of rights. Actually, I mean, let me just make even a more general statement. If your application is right heavy, then you'd rather not do any of these, you know, these sort of things, right? Because by using a hash map, you're guaranteed to have a great performance, right? For example, if you're using a ziplist and you are very right heavy, even the ziplist, even without you maintaining it, the ziplist is going to be a little bit more expensive in terms of right, or, you know, because, so if you know you're right heavy, then you perhaps don't want to use this one, yes. I mean, I was talking mostly about the use case, where you're using this, I mean, this case you're using it as a cache, but yeah, so maybe then this one would not make sense, but you could still, you could still ensure that you are respecting the size of a ziplist so that you can save memory over there. So there are things you could do, but I would say not this one, where you're compressing and serializing everything in a list. Yeah, the other one is, you know, Redis has these actually just a month back on 1st of April actually. Redis introduced this new object called as Hyperlog log, which is a new data type or a new command. And so if you're counting objects, that is a good data set. So they fall under this category of probabilistic data structures, and I think the next talk is also going to, I don't know who's, but someone's going to talk about bloom filters in the next talk. And so both of them are probabilistic data structures in the sense that they are not accurate, but if you use Redis and use one of these data structures, it would help you to save a lot of memory where they are really memory efficient, but their trading off, the trade off is really is that they're not 100% accurate. So there's some percentage of accuracy, like some 90s accuracy. So if you're counting millions of objects, or you're doing some analytics, you don't really care about the exact count, but you're just looking at a, if it's 99% accurate, then I'm good enough. I'm good. So in those use cases, you could use these probabilistic data structures. So Hyperlog log is a new data structure and a new set of commands that were introduced very recently. Bloom filters, there's no data type specific to bloom filters, but bloom filters can be implemented in Redis over the string data structure. And so you could use bloom filters to save a lot of memory and to check the existence of an object. So beyond this, yeah, so beyond these six things that I have listed, there's also a few that Redis.io has a section where it says memory optimization. One of the other things that it recommends, which I have not put up here, is to use 32-bit version of Redis instead of the 64-bit version. The reason that saves a lot of memory is because a lot of these data structures are pointers, have a lot of pointers. And pointers, if you're using a 64-bit architecture, would be 8 bytes. Whereas if you use the same in 32, it's just 4 bytes. So you drastically save a lot of, in terms of memory, just by moving to a 32-bit architecture. So that is another thing. Like if you've got, if your system has, you know, you've got a lot of machines which you can afford to use a 32-bit, you could use that to save memory. Yes, you limit it. That's on the other side. Yes. I mean, it's a trade-off that you have to do. So I mean, the point I was trying to say is that if you've got a lot of machines which lesser RAM and your application can scale out, then using 32-bit is also an option. I mean, most of the times we do not, I mean, at least I have not cared about and I just use a 64-bit, but it could work. Yeah. Sorry. No. No, you're only restricted to, I mean, on the 32-bit architecture, you're only restricted to that much. Actually, I'm not sure. I shouldn't say. No, no, no. His question was more specific. Can I run multiple 32-bit instance machines in order to get around the fact of I can only use 4GP of RAM? So I do not think that is the answer, I'm not so sure. No, no, you definitely can run multiple instances on the same server. Will you be able to evade the 4GP limitation? I'm almost certain that you cannot, but I'll not say. Anyways, so this was the second section where I'm just listing down some of the tips that can save memory. Third thing is, you know, so let's say that you're already messed up where you have, you've got some data and it's using a lot of memory. You don't know which key or what piece of your application is using that memory, right? So there's this tool. What it does is it takes the DOM file, the RDB file that you have on your Redis server and it can analyze that and it can find out what is the size of every key and it will give you some statistics. So the way you run this is you, you know, this is the syntax. You pass it to your DOM file and then you redirect the output to a CSV file and this CSV file will contain one row for every key in your database and it will give you some statistics, like what is the memory, if it is a list, what is the number of elements in that list, you know, if it is dictionary, how many keys does that dictionary have, how many elements does it have and this output is super useful for you to A, find out which key is hogging most of my memory. So a lot of times what happens is you wanted some, you wanted to delete a key, but you did not or there's a bug in your application in which you did not delete a key. You wanted to set an expires and that expires was not set or maybe a set is growing unbounded, you know, you were supposed to trim the set but you did not. This will help you in that, you know, what it will tell you is that, you know, what I expected the size of this to be 100, but it is actually thousands and then you can go and find out. It's a great tool in finding out where the bottlenecks is, which key is using the most of your memory. So it's pretty useful. You should try that out. Yeah, that's it from me. Here's a quick bio and I'm open to questions if you have any I could just take them. So Redis does not have a background thread or sort of thing which goes and bleeds keys. What it does is it spares, it's got, it's a single threaded application. So what it does is it spares some number of CPU cycles to go and clean up memory once in every number of time interval, right? So the other thing, so it's not as aggressive in, as say memcast in going in reclaiming memory. Does that answer your question or did I get that wrong? No, I'm sorry, I'm not following your question. So Redis is not persistent in the sense that if it goes out of memory, it is there in the disk and so if there's a cache message, no, it's not like that. Redis, think of Redis as just a pure memory store. It's not like if a key expires, if a key expires it's gone. Just like memcast, it's not like a key is expired and it goes out of memory and is stable in the disk and when next time somebody asks it to load it from the disk, it doesn't work like that. So it is an in-memory data store whatever is stored in the disk is think of it as like a pack up or think of it as like if the server restarts, how do I get it back? But it's not like, you know, it was implemented in an earlier version of Redis, but it is no longer maintained. It does not have the option of taking memory keys which have expired and store it on the disk. It does not have that. Oh yeah, actually, I mean, so this does exactly that. This is what it does is it looks into the Redis dump file and it sort of reverse engineers into the the way Redis stores its data structures and on that basis it will come up with a number which is very close to the actual number that you would see. So I mean, if you take your dump file and run this, you will be at least in the 90% ballpark. Anything else? Alright, thanks guys.