Thursday, April 13, 2017

Redis Misc



https://zhuanlan.zhihu.com/p/41754417
所以Antirez才使用了如今使用的方案——异步线程,这套方案就简单多了,释放内存不用为每种数据结构适配一套渐进式释放策略,也不用搞个自适应算法来仔细控制回收频率。将对象从全局字典中摘掉,然后往队列里一扔,主线程就去干别的去了。异步线程从队列里取出对象来,直接走正常的同步释放逻辑就可以了。

所以antirez为了支持懒惰删除,将对象共享机制彻底抛弃,它将这种对象结构称为「share-nothing」,也就是无共享设计。但是甩掉对象共享谈何容易!这种对象共享机制散落在源代码的各个角落,牵一发而动全身,改起来犹如在布满地雷的道路上小心翼翼地行走。
研究完这些加锁解锁的代码后,我开始有点当心主线程的性能。我们都知道加锁解锁是一个相对比较耗时的操作,尤其是悲观锁最为耗时。如果删除很频繁,主线程岂不是要频繁加锁解锁。所以这里肯定还有优化空间,Java的ConcurrentLinkQueue就没有使用这样粗粒度的悲观锁,它优先使用cas来控制并发。
https://redislabs.com/blog/5-key-takeaways-for-developing-with-redis/

2. Keep Track of the Length of Your Key Names

3. Use the Right Data Structures
  • When applicable, use lists instead of sets. If you don’t need the set’s properties for ensuring uniqueness or checking membership, a list will consume less memory and perform inserts faster.
  • Sorted sets are the most expensive data structure, both in terms of memory consumption and basic operations complexity (e.g. ZADDing a new member). If you just require a way to look up scores and ordering isn’t important, consider using hashes instead.
  • An often overlooked feature in Redis are bitmaps, or bitsets (available since v2.2). Bitsets allow you to perform several bit-level operations on Redis values, which could mean storing large amounts of data efficiently. This can be used, for example, for some lightweight analytics.

4. Use SCAN, Never Use KEYS

The SCAN command is available starting with Redis v2.8, and enables you to retrieve keys in the keyspace using a cursor. This behavior is unlike that of the (hiss) KEYS command, which returns all matching elements at once, but is considered risky in production because it may block your Redis server and even exhaust its RAM resources. SCAN, on the other hand, makes it possible to inspect the data without the risk of blocking your server or having to rely on a slave.
Note that SCAN requires you to read a cursor value that’s passed to the subsequent call to SCAN. SCAN also accepts a keyname pattern and an optional count argument. Another difference between SCAN and KEYS is that it is possible to get the same key name more than once with SCAN.
SCAN is accompanied by SSCAN, HSCAN and ZSCAN, which allow you to iterate the contents of sets, hashes and sorted sets (respectively).

https://mp.weixin.qq.com/s/BVgwGis_yrNs_HzoxmXMZw
        回过头来看一个极小问题,直接导致线上所有服务均受影响,这就要求我们要时刻小心谨慎,因为每一次读写均要多思考,因为一个点回影响一个大的整体。

        再有就是一个大value数据,会对redis进行阻塞,影响其他get mGet命令性能。

       再有就是对应用技术要有深入认识,不然就很容易发生这种很难定位问题。

       对于数据大小一定进行检查,不然对于集群是破坏性伤害。每一步都要进行检查,越编程人会越谨慎,因为发现没注意到的问题最终都会变成一个问题。并且可能还会影响全局。

https://mp.weixin.qq.com/s/vAb9TVAyiCYsmLNPQLDxpw
首先锁服务必须足够稳定,假设无法获取锁,那么竞争任务的将无法执行。其次,执行竞争任务的过程不能够死锁或者无限等待,否则将无法释放锁且改任务也无法执行完成。所以在设计锁的时候还需要考虑两个因素:锁必须要有过期时间及获取及释放锁过程的高可用或者锁错误时的异常处理。
所以,归纳一下分布式锁在设计时通常要考虑的几个要素是:
  1. 分布式锁一定要保证多客户端竞争临界资源时的绝对互斥;
  2. 分布式锁要设计一定的超时时间,防止在获得锁的服务阻塞或者崩溃引起的锁无法释放;
  3. 分布式要针对业务场景设计锁机制异常降级措施,防止因为锁获取错误导致无法获取临界资源的后果。
关于第2点的要素,还有一些要注意的东西,假设报表服务A在获取到锁之后,出现了很长的FULL GC,系统出现暂停,在此期间,锁已经超时了,报表服务B又重新拿到了锁并向用户发送了报表,在客户端AFull GC结束后,同样再去执行报表发送任务,就会导致执行结果出错。
这种场景往往需要个性化的处理,现在业界大部分的分布式锁都会出现这种情况,因为系统暂停导致的锁失效往往很难去避免,因为系统暂停可能出现在任何时候。 通常情况下,我们需要预估访问竞争资源的时间,确定好超时时间并在访问结束后进行数据比对和必要的数据补偿。

Redis具体实现分布式锁

在redis命令集合中,有一个命令叫做SETNX,具体命令格式是:
  1. SETNX key value
该命令的作用是如果key存在,则什么都不做,并且返回0,如果key不存在则将key的值设置成value,并且返回1,该命令是原子性的。我们可以利用该命令来实现分布式锁。
  1. 获取锁:获取当前的timestamp,并将客户端ID作为key,该timestamp作为value调用SETNX,并设置锁的TTL,处理获取锁的异常。
  2. 确认锁状态,如果成功获取锁,则访问临界资源,否则根据业务场景间隔一定时间再次尝试获取锁。
  3. 访问临界资源
  4. 释放锁
  1. //获取锁
  2. timeStamp = getCurrentTimeStamp();
  3. try{
  4.    lock=SET CLIENT_ID timeStamp NX PX TIMEOUT;
  5. }catch(Exception e){
  6.    //处理获取锁的异常
  7.    return;
  8. }
  9. try{
  10.    if(lock == 0){
  11.        return;
  12.    }else{
  13.        //访问临界资源
  14.        do();
  15.    }
  16. }finally{
  17.    //释放锁
  18.    del CLIENT_ID;
  19. }
这种实现分布式锁的方式是很多开发者最喜欢用的,但是如何保证redis的可用性呢,如果我们使用一个redis节点,当其因为不可控原因宕机时,锁机制将不可用。有人可能会说,可以使用redis主从集群复制,主挂了,从可以接替上,但是这估计依然不能解决问题,因为redis主从复制是异步的,谁能保证主挂了,从节点上一定有锁数据呢?

redis官网上介绍了一种red lock算法,该算法弃用了单redis节点,采用N个(官网推荐5个)独立的redis节点作为锁服务,客户端要获取锁,必须向N/2+1(绝大部分)节点成功申请锁后,才能访问临界资源。


Zset
https://redislabs.com/ebook/part-1-getting-started/chapter-1-getting-to-know-redis/1-2-what-redis-data-structures-look-like/1-2-5-sorted-sets-in-redis/
ZSETs have the unique property in Redis of being able to be accessed by member (like a HASH), but items can also be accessed by the sorted order and values of the scores

ZADD
Adds member with the given score to the ZSET
ZRANGE
Fetches the items in the ZSET from their positions in sorted order
ZRANGEBYSCORE
Fetches items in the ZSET based on a range of scores
ZREM
Removes the item from the ZSET, if it exists

redis 127.0.0.1:6379> zadd zset-key 728 member1
(integer) 1
redis 127.0.0.1:6379> zadd zset-key 982 member0
(integer) 1
redis 127.0.0.1:6379> zadd zset-key 982 member0
(integer) 0
When we add items to a ZSET, the command returns the number of new items.
redis 127.0.0.1:6379> zrange zset-key 0 -1 withscores
1) "member1"
2) "728"
3) "member0"
4) "982"
We can fetch all of the items in the ZSET, which are ordered by the scores, and scores are turned into floats in Python.
redis 127.0.0.1:6379> zrangebyscore zset-key 0 800 withscores
1) "member1"
2) "72
http://blog.text.wiki/2015/04/09/use-sorted-set-to-storage-dynamically-changing-list.html
http://antirez.com/news/109
The 24 bits in the object are enough to store the least significant
bits of the current unix time in seconds. This representation, called
“LRU clock” inside the source code of Redis, takes 194 days to overflow. Keys metadata are updated much often, so this was good enough.


The initial Redis algorithm was as simple as that: when there is to evict
a key, select 3 random keys, and evict the one with the highest
idle time. Basically we do random sampling over the key space and evict
the key that happens to be the better. Later this “3 random keys”
was turned into a configurable “N random keys” and the algorithm
speed was improved so that the default was raised to 5 keys sampling
without losing performances.

LRU V2: don’t trash away important information
The most obvious way to improve
the vanilla algorithm used by Redis was to accumulate the otherwise
trashed information in a “pool” of good candidates for eviction.

Basically when the N keys sampling was performed, it was used to
populate a larger pool of keys (just 16 keys by default).
This pool has the keys sorted by idle time, so new keys only enter
the pool when they have an idle time greater than one key in the
pool or when there is empty space in the pool.

we
said that LRU is actually kinda a trick. What we really want is to
retain keys that have the maximum probability of being accessed in the
future, that are the keys *most frequently accessed*, not the ones with
the latest access.

The algorithm evicting the keys with the least number of accesses
is called LFU. It means Least Frequently Used, which is the feature of
the keys that it attempts to kill to make space for new keys.

In theory LFU is as simple as associating a counter to each key. At
every access the counter gets incremented, so that we know that a given
key is accessed more frequently than another key.

1. With LFU you cannot use the “move to head” linked list trick used for LRU in order to take elements sorted for eviction in a simple way, since keys must be ordered by number of accesses in “perfect LFU”. Moving the accessed key to the right place can be problematic because there could be many keys with the same score, so the operation can be O(N) in the worst case, even if the key frequency counter changed just a little. Also as we’ll see in point “2” the accesses counter does not always change just a little, there are also sudden large changes.

2. LFU can’t really be as trivial as, just increment the access counter
on each access. As we said, access patterns change over time, so a key
with an high score needs to see its score reduced over time if nobody
keeps accessing it. Our algorithm must be albe to adapt over time.

In Redis the first problems is not a problem: we can just use the trick
used for LRU: random sampling with the pool of candidates. The second
problem remains. So normally LFU implementations have some way in order
to decrement, or halve the access counter from time to time.
Implementing LFU in 24 bits of space
===

LFU has its implementation peculiarities itself, however in Redis all
we can use is our 24 bit LRU field in order to model LFU. To implement
LFU in just 24 bits per objects is a bit more tricky.

What we need to do in 24 bits is:

1. Some kind of access frequency counter.
2. Enough information to decide when to halve the counter.

My solution was to split the 24 bits into two fields:

           16 bits      8 bits
      +----------------+--------+
      + Last decr time | LOG_C  |
      +----------------+--------+

The 16 bit field is the last decrement time, so that Redis knows the
last time the counter was decremented, while the 8 bit field is the
actual access counter.
You are thinking that it’s pretty fast to overflow an 8 bit counter,
right? Well, the trick is, instead of using just a counter, I used
a logarithmic counter. This is the function that increments the
counter during accesses to the keys:

  uint8_t LFULogIncr(uint8_t counter) {
      if (counter == 255) return 255;
      double r = (double)rand()/RAND_MAX;
      double baseval = counter - LFU_INIT_VAL;
      if (baseval < 0) baseval = 0;
      double p = 1.0/(baseval*server.lfu_log_factor+1);
      if (r < p) counter++;
      return counter;
  }

Basically the greater is the value of the counter, the less probable
is that the counter will really be incremented: the code above computes
a number ‘p’ between 0 and 1 which is smaller and smaller as the counter
increases. Then it extracts a random number ‘r’ between 0 and 1 and only
increments the counter if ‘r < p’ is true.


https://redis.io/topics/lru-cache

If a command results in a lot of memory being used (like a big set intersection stored into a new key) for some time the memory limit can be surpassed by a noticeable amount.

Approximated LRU algorithm

Redis LRU algorithm is not an exact implementation. This means that Redis is not able to pick the best candidate for eviction, that is, the access that was accessed the most in the past. Instead it will try to run an approximation of the LRU algorithm, by sampling a small number of keys, and evicting the one that is the best (with the oldest access time) among the sampled keys.
However since Redis 3.0 the algorithm was improved to also take a pool of good candidates for eviction. This improved the performance of the algorithm, making it able to approximate more closely the behavior of a real LRU algorithm.
What is important about the Redis LRU algorithm is that you are able to tune the precision of the algorithm by changing the number of samples to check for every eviction. This parameter is controlled by the following configuration directive:
maxmemory-samples 5
The reason why Redis does not use a true LRU implementation is because it costs more memory. However the approximation is virtually equivalent for the application using Redis. 

Starting with Redis 4.0, a new Least Frequently Used eviction mode is available. This mode may work better (provide a better hits/misses ratio) in certain cases, since using LFU Redis will try to track the frequency of access of items, so that the ones used rarely are evicted while the one used often have an higher chance of remaining in memory.
If you think at LRU, an item that was recently accessed but is actually almost never requested, will not get expired, so the risk is to evict a key that has an higher chance to be requested in the future. LFU does not have this problem, and in general should adapt better to different access patterns.

https://www.couyon.net/blog/using-redis-as-a-lru-cache-dont-do-it
The major drawbacks to memcached are its slab allocator memory model and maximum object size at startup. Let's take a look at those.
The slab allocator is one way to carve up a finite amount of memory space. You tell memcached how much memory you want to use, and it carves up the memory into differing sized chunks or slabs. For example: you'll get 1000 1KB slabs, 500 2KB slabs, 256 512KB slabs, etc. Those aren't actual numbers, but you get the idea. It goes all the way up to the maximum object size. The maximum object size is determined by the config file at startup; it cannot be resized on the fly. You have a little bit of control over how the slab allocation works (starting size and stepping of the slabs), but nothing really specific. It takes a while to tune the slab allocations to your use case. So with lots of tuning and restarting your caches required (thus emptying the cache),
Let's back up a few steps and examine how to size memory values in memcached. In memcached, you roughly take 1GB (or so) less than installed RAM and use that as your cache size. 72GB of RAM? 71GB cache. 144GB of RAM? 143GB cache. That's basically it. The slab allocator will break up your cache into slabs and that's what you get. Run out of 512KB slabs but have lots of 2MB slabs when you want to write a 500KB object? Too bad. A 512KB object will be evicted even though you have "free space." Sounds lame. Maybe redis does it better?



In redis you set a "maxmemory" value. You're told this is the maximum size of the cache. That's true; it is the maximum size of the cache. However, it's not the maximum amount of memory that redis will use to store the cache. Redis allocates memory for objects on the fly up until its maxmemory value. After that, redis will "remove" an object from memory and insert the new object. The devil is in the details. Redis can't guarantee that the new value is inserted into old object's memory address space or even that it will completely fill the free space. Over time, this leads to gaps in the memory space, or what's known as memory fragmentation.
https://redis.io/topics/transactions
If you have a relational databases background, the fact that Redis commands can fail during a transaction, but still Redis will execute the rest of the transaction instead of rolling back, may look odd to you.
However there are good opinions for this behavior:
  • Redis commands can fail only if called with a wrong syntax (and the problem is not detectable during the command queueing), or against keys holding the wrong data type: this means that in practical terms a failing command is the result of a programming errors, and a kind of error that is very likely to be detected during development, and not in production.
  • Redis is internally simplified and faster because it does not need the ability to roll back.

WATCH is used to provide a check-and-set (CAS) behavior to Redis transactions.
WATCHed keys are monitored in order to detect changes against them. If at least one watched key is modified before the EXEC command, the whole transaction aborts, and EXEC returns a Null reply to notify that the transaction failed.

WATCH is used to provide a check-and-set (CAS) behavior to Redis transactions.
WATCHed keys are monitored in order to detect changes against them. If at least one watched key is modified before the EXEC command, the whole transaction aborts, and EXEC returns a Null reply to notify that the transaction failed.
For example, imagine we have the need to atomically increment the value of a key by 1 (let's suppose Redis doesn't have INCR).

Thanks to WATCH we are able to model the problem very well:
WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC
So what is WATCH really about? It is a command that will make the EXEC conditional: we are asking Redis to perform the transaction only if none of the WATCHed keys were modified. (But they might be changed by the same client inside the transaction without aborting it. More on this.) Otherwise the transaction is not entered at all. (Note that if you WATCH a volatile key and Redis expires the key after you WATCHed it, EXEC will still work. More on this.)
WATCH can be called multiple times. Simply all the WATCH calls will have the effects to watch for changes starting from the call, up to the moment EXEC is called. You can also send any number of keys to a single WATCH call.
When EXEC is called, all keys are UNWATCHed, regardless of whether the transaction was aborted or not. Also when a client connection is closed, everything gets UNWATCHed.
It is also possible to use the UNWATCH command (without arguments) in order to flush all the watched keys. Sometimes this is useful as we optimistically lock a few keys, since possibly we need to perform a transaction to alter those keys, but after reading the current content of the keys we don't want to proceed. When this happens we just call UNWATCH so that the connection can already be used freely for new transactions.

Using WATCH to implement ZPOP

A good example to illustrate how WATCH can be used to create new atomic operations otherwise not supported by Redis is to implement ZPOP, that is a command that pops the element with the lower score from a sorted set in an atomic way. This is the simplest implementation:
WATCH zset
element = ZRANGE zset 0 0
MULTI
ZREM zset element
EXEC

If EXEC fails (i.e. returns a Null reply) we just repeat the operation.

Redis script is transactional by definition, so everything you can do with a Redis transaction, you can also do with a script, and usually the script will be both simpler and faster.
https://github.com/xetorthio/jedis/wiki/AdvancedUsage

Transactions

To do transactions in Jedis, you have to wrap operations in a transaction block, very similar to pipelining:
jedis.watch (key1, key2, ...);
Transaction t = jedis.multi();
t.set("foo", "bar");
t.exec();
Note: when you have any method that returns values, you have to do like this:
Transaction t = jedis.multi();
t.set("fool", "bar"); 
Response<String> result1 = t.get("fool");

t.zadd("foo", 1, "barowitch"); t.zadd("foo", 0, "barinsky"); t.zadd("foo", 0, "barikoviev");
Response<Set<String>> sose = t.zrange("foo", 0, -1);   // get the entire sortedset
t.exec();                                              // dont forget it

String foolbar = result1.get();                       // use Response.get() to retrieve things from a Response
int soseSize = sose.get().size();                      // on sose.get() you can directly call Set methods!

// List<Object> allResults = t.exec();                 // you could still get all results at once, as before
Note that a Response Object does not contain the result before t.exec() is called (it is a kind of a Future). Forgetting exec gives you exceptions. In the last lines, you see how transactions/pipelines were dealt with before version 2. You can still do it that way, but then you need to extract objects from a list, which contains also Redis status messages.
Note 2: Redis does not allow to use intermediate results of a transaction within that same transaction. This does not work:
// this does not work! Intra-transaction dependencies are not supported by Redis!
jedis.watch(...);
Transaction t = jedis.multi();
if(t.get("key1").equals("something"))
   t.set("key2", "value2");
else 
   t.set("key", "value");


However, there are some commands like setnx, that include such a conditional execution. Those are of course supported within transactions. You can build your own customized commands using eval/ LUA scripting.
MyListener l = new MyListener();

jedis.subscribe(l, "foo");
https://www.jianshu.com/p/d777eb9f27df
        public void actomicAdd(){
            cli.watch(incKeyStr);
            boolean flag =true;
            while(flag){
                String countStr = cli.get("redis_inc_key");
                int countInt = Integer.parseInt(countStr);
                int expect = countInt+1;
                Transaction tx = cli.multi();                   
                tx.set(incKeyStr, String.valueOf(expect));
                List<Object> list = tx.exec();
                //如果事务失败了exec会返回null
                if(list==null){
                    System.out.println("multi shut down");
                    continue;
                }
                else{
                    //如果达到期望值那么结束while循环
                    flag=false;
                }
                System.out.println("my expect num is "+expect);         
                System.out.println("seting....");   
            }
            Countor.incrementAndGet();  
        }
        
    }
    
}
这样我们就利用Redis实现了一个类似于Java 的原子类的功能。在实际的Web开发中,我们可以利用redis来解决资源重复修改或争用的问题。


https://dzone.com/articles/watch-command-maintaining
http://fivezh.github.io/2017/05/24/Redis-cas/
Redis的事务机制Transaction通过四个命令来完成:MULTI, EXEC, DISCARD and WATCH
  • 事务内的命令要么全部执行(仅指入带执行的命令队列成功),要么都不执行
    Either all of the commands or none are processed, so a Redis transaction is also atomic.
multi执行之前断开连接,则全部命令不会执行;exec执行之后,所有命令操作都会被执行。
注意:这里仅仅指命令的执行,但同一事务内命令执行与否,并不意味着执行成功(存在命令执行后,返回异常的情况)。
  • 事务期间的错误情况
  1. 命令入待执行命令队列失败,此时在exec前就会有错误:命令语法错误、内存不足等极端情况, 此时一般会中断事务,自动discard事务。

CAS(Check-And-Set)支持

  • watch已监视的key,只允许在当前终端的multiexec见被修改,其他情况的修改都将导致watch和此事务的失败。
CAS的实现主要通过watch命令完成,也就是说在watch一个key后,其他终端修改此key的值时,都将触发当前事务的失败。


Pipelines in Redis
Redis provides a mechanism for faster execution, called pipeline. This groups up all the commands as one command block and sends it to the server for execution. The results of all the commands get queued in a response block and sent back.

if a movie-attendence variable is incremented in a transaction and the movie-attendence value is modified by another client, like we demonstrated, then the transaction will fail following an optimistic locking to Redis keys, such as movie-attendence, using the WATCH command with check-and-set (CAS) approach.
atomicity - single thread model

redis pipeline vs transaction
https://stackoverflow.com/questions/29327544/pipelining-vs-transaction-in-redis
Pipelining is primarily a network optimization. It essentially means the client buffers up a bunch of commands and ships them to the server in one go. The commands are not guaranteed to be executed in a transaction. The benefit here is saving network round trip time for every command.
Redis is single threaded so an individual command is always atomic, but two given commands from different clients can execute in sequence, alternating between them for example.
Multi/exec, however, ensures no other clients are executing commands in between the commands in the multi/exec sequence.
http://www.terminalstate.net/2011/05/redis-pipelines-and-transactions.html
One way of improving this is by using pipelining, in which case you send your commands to the server in sequence and read the combined responses at the end in one block, when the pipeline is "closed".
Pipelining is a common technique in programming and it works particularly well for Redis. Of course you can not pipeline commands that share any kind of conditional dependency but if your unit of work is composed of several commands that can be run sequentially, you should consider pipelining them.
Pipeline pipeline = jedis.pipelined();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
pipeline.set("" + i, "" + i);
}
List<Object> results = pipeline.execute();
Every call to pipeline.set() effectively sends the SET command to Redis (you can easily see this by setting a breakpoint inside the loop and querying Redis with redis-cli). The call to pipeline.execute() is when the reading of all the pending responses happens. A pipeline that issues a huge number of commands might cause an out of memory error or a timeout exception at this point despite the fact that the data has safely attained the database. So be careful and think about what this implies for your particular use case.

Essentially, pipelining is a client-side operation supported by the ability of the Redis protocol to handle bulk responses. Remember that pipelining does not imply that the commands are queued on the client side before being sent - only reading responses is delayed and handled in bulk at the end. Pipelining does not guarantee that the commands are atomic either; that's the role of transactions.

a transaction is initiated by a client call of the MULTI command. All the commands issued subsequently by the same client are queued on the server till the transaction gets executed or discarded. For every queued command, the server returns QUEUED as a result right away. Once a transaction is executed by a call to the EXEC command, the server will execute all the queued command sequentially and atomically.
One interesting property of Redis transactions is that the server will go on executing all of the commands without interruption even if one (or several) commands fails. At the end of a transaction the server returns the list of results to the client

transactions offer stronger guarantees and are managed mainly on the server. It makes sense then to combine the two features to enjoy their combined benefits. The next example does just that.


long start = System.currentTimeMillis();
Pipeline pipeline = jedis.pipelined();
pipeline.multi();
for (int i = 0; i < 100000; i++) {
pipeline.set("" + i, "" + i);
}
pipeline.exec();
List<Object> results = pipeline.execute();
This time, the average is around 0.8 second, which shows that pipelining the transaction in our example turned out to be a good idea. The combined gain would be even greater if the server is accessed by multiple concurrent clients. Only caveat: the size of the returned result list is doubled this time because of the combined queuing and execution results.

However, when executed, the result shows a certain tendency that starting from a reasonably small command sequence (5 to 10), pipelined transactions outperform plain ones. In most cases, pipelining transaction pays off since a typical transaction is composed from at least 4 commands (MULTI, a couple of commands followed by EXEC or DISCARD).
https://zhuanlan.zhihu.com/p/34133067
那么问题的后半部分,是 Redis 如何实现,这个问题这么问肯定是有坑的,那就是redis肯定不是这样实现的。

Redis的LRU实现

如果按照HashMap和双向链表实现,需要额外的存储存放 next 和 prev 指针,牺牲比较大的存储空间,显然是不划算的。所以Redis采用了一个近似的做法,就是随机取出若干个key,然后按照访问时间排序后,淘汰掉最不经常使用的,具体分析如下:
为了支持LRU,Redis 2.8.19中使用了一个全局的LRU时钟,server.lruclock,定义如下,
#define REDIS_LRU_BITS 24
unsigned lruclock:REDIS_LRU_BITS; /* Clock for LRU eviction */
默认的LRU时钟的分辨率是1秒,可以通过改变REDIS_LRU_CLOCK_RESOLUTION宏的值来改变,Redis会在serverCron()中调用updateLRUClock定期的更新LRU时钟,更新的频率和hz参数有关,默认为100ms一次,如下,
#define REDIS_LRU_CLOCK_MAX ((1<<REDIS_LRU_BITS)-1) /* Max value of obj->lru */
#define REDIS_LRU_CLOCK_RESOLUTION 1 /* LRU clock resolution in seconds */

void updateLRUClock(void) {
    server.lruclock = (server.unixtime / REDIS_LRU_CLOCK_RESOLUTION) &
                                                REDIS_LRU_CLOCK_MAX;
}
server.unixtime是系统当前的unix时间戳,当 lruclock 的值超出REDIS_LRU_CLOCK_MAX时,会从头开始计算,所以在计算一个key的最长没有访问时间时,可能key本身保存的lru访问时间会比当前的lrulock还要大,这个时候需要计算额外时间,如下,
/* Given an object returns the min number of seconds the object was never
 * requested, using an approximated LRU algorithm. */
unsigned long estimateObjectIdleTime(robj *o) {
    if (server.lruclock >= o->lru) {
        return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
    } else {
        return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) *
                    REDIS_LRU_CLOCK_RESOLUTION;
    }
}
Redis支持和LRU相关淘汰策略包括,
  • volatile-lru 设置了过期时间的key参与近似的lru淘汰策略
  • allkeys-lru 所有的key均参与近似的lru淘汰策略
当进行LRU淘汰时,Redis按如下方式进行的,
......
            /* volatile-lru and allkeys-lru policy */
            else if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU ||
                server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU)
            {
                for (k = 0; k < server.maxmemory_samples; k++) {
                    sds thiskey;
                    long thisval;
                    robj *o;

                    de = dictGetRandomKey(dict);
                    thiskey = dictGetKey(de);
                    /* When policy is volatile-lru we need an additional lookup
                     * to locate the real key, as dict is set to db->expires. */
                    if (server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU)
                        de = dictFind(db->dict, thiskey);
                    o = dictGetVal(de);
                    thisval = estimateObjectIdleTime(o);

                    /* Higher idle time is better candidate for deletion */
                    if (bestkey == NULL || thisval > bestval) {
                        bestkey = thiskey;
                        bestval = thisval;
                    }
                }
            }
            ......
Redis会基于server.maxmemory_samples配置选取固定数目的key,然后比较它们的lru访问时间,然后淘汰最近最久没有访问的key,maxmemory_samples的值越大,Redis的近似LRU算法就越接近于严格LRU算法,但是相应消耗也变高,对性能有一定影响,样本值默认为5。
https://mp.weixin.qq.com/s/507jyNbL4xCkxyW6Xk15Xg
Redis有哪些数据结构?
字符串String、字典Hash、列表List、集合Set、有序集合SortedSet。
如果你是Redis中高级用户,还需要加上下面几种数据结构HyperLogLog、Geo、Pub/Sub。
如果你说还玩过Redis Module,像BloomFilter,RedisSearch,Redis-ML,面试官得眼睛就开始发亮了。
使用过Redis分布式锁么,它是什么回事?
先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。
这时候对方会告诉你说你回答得不错,然后接着问如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?
这时候你要给予惊讶的反馈:唉,是喔,这个锁就永远得不到释放了。紧接着你需要抓一抓自己得脑袋,故作思考片刻,好像接下来的结果是你主动思考出来的,然后回答:我记得set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!对方这时会显露笑容,心里开始默念:摁,这小子还不错。
假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来?
使用keys指令可以扫出指定模式的key列表。
对方接着追问:如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?
这个时候你要回答redis关键的一个特性:redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。
使用过Redis做异步队列么,你是怎么用的?
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
如果对方追问可不可以不用sleep呢?list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。
如果对方追问能不能生产一次消费多次呢?使用pub/sub主题订阅者模式,可以实现1:N的消息队列。
如果对方追问pub/sub有什么缺点?在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。
如果对方追问redis如何实现延时队列?
使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。
如果有大量的key需要设置同一时间过期,一般需要注意什么?
如果大量的key过期时间设置的过于集中,到过期的那个时间点,redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。
Redis如何做持久化的?
bgsave做镜像全量持久化,aof做增量持久化。因为bgsave会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要aof来配合使用。在redis实例重启时,会使用bgsave持久化文件重新构建内存,再使用aof重放近期的操作指令来实现完整恢复重启之前的状态。
对方追问那如果突然机器掉电会怎样?取决于aof日志sync属性的配置,如果不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。但是在高性能的要求下每次都sync是不现实的,一般都使用定时sync,比如1s1次,这个时候最多就会丢失1s的数据。
对方追问bgsave的原理是什么?你给出两个词汇就可以了,fork和cow。fork是指redis通过创建子进程来进行bgsave操作,cow指的是copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。
Pipeline有什么好处,为什么要用pipeline?
可以将多次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有因果相关性。使用redis-benchmark进行压测的时候可以发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。
Redis的同步机制了解么?
Redis可以使用主从同步,从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。
是否使用过Redis集群,集群的原理是什么?
Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。
Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。


https://medium.com/@strayobject/dump-all-keys-stored-in-redis-9ae91a6542fe
Dump all keys stored in redis
redis-cli KEYS \* |xargs -n 1 -P8 redis-cli dump
http://stackoverflow.com/questions/11368615/redis-cli-csv-option-exporting-to-csv
redis-cli --csv your-command > stdout.csv 2> stderr.txt
redis-cli KEYS Session-\* | xargs -n 1 redis-cli get
https://redis.io/commands/zcount
Returns the number of elements in the sorted set at key with a score between min and max.
The min and max arguments have the same semantic as described forZRANGEBYSCORE.
ZCOUNT myzset -inf +inf
http://stackoverflow.com/questions/11504154/get-all-members-in-sorted-set
as zrange does not take scores, but indices. 0 is the first index, and -1 will be interpreted as the last index:
zrange key 0 -1
To get a range by score, you would call zrangebyscore instead -- where -inf and +inf can be used to denote negative and positive infinity, respectively, as Didier Spezia notes in his comment:
zrangebyscore key -inf +inf
Install
https://redis.io/download
https://unix.stackexchange.com/questions/94479/jemalloc-and-other-errors-making-redis-on-centos-6-4
I ran into the same issue on centos 6.4 and had to run the following commands:
cd deps
make hiredis jemalloc linenoise lua geohash-int
cd ..
make install
cd
https://gist.github.com/todgru/14768fb2d8a82ab3f436
$ sudo yum install gcc This may return an "already installed" message. That's OK.
$ wget http://download.redis.io/redis-stable.tar.gz && tar xvzf redis-stable.tar.gz && cd redis-stable && make
http://stackoverflow.com/questions/21795340/linux-install-redis-cli-only
Ubuntu (tested on 14.04) has package called redis-tools which contains redis-cli among other tools. To install it type:
sudo apt-get install redis-tools
https://www.devops.zone/ha-performance-caching/installing-redis-cli-without-installing-server/
On Debian actually there is only one package redis-server in order to install the server and its tools. If you would like to have the client only, you can achieve this as follows.
As a prerequesite you need to install these packages:
apt-get install libjemalloc1 libjemalloc-dev gcc make
Then proceed with the actual installation:
cd /tmp
wget http://download.redis.io/redis-stable.tar.gz
tar xvzf redis-stable.tar.gz
cd redis-stable
make
cp src/redis-cli /usr/local/bin/
chmod 755 /usr/local/bin/redis-cli
Then connect to your remote server for example like this:
redis-cli -h 192.268.34.32
yum update
https://redis.io/topics/benchmarks
  • Redis is a single-threaded server. It is not designed to benefit from multiple CPU cores. People are supposed to launch several Redis instances to scale out on several cores if needed. It is not really fair to compare one single Redis instance to a multi-threaded data store.
 -c <clients>       Number of parallel connections (default 50)
 -n <requests>      Total number of requests (default 100000)
 -d <size>          Data size of SET/GET value in bytes (default 2)

http://stackoverflow.com/questions/33950984/how-to-understand-redis-clis-result-vs-redis-benchmarks-result
the discrepancy comes from scheduling artifacts (i.e. to the behavior of the operating system scheduler or the network loopback).
redis-cli latency is implemented by a loop which only sends a PING command before waiting for 10 ms. Let's try an experiment and compare the result of redis-cli --latency with the 10 ms wait state and without.
f you are experiencing latency problems, probably you know how to measure it in the context of your application, or maybe your latency problem is very evident even macroscopically. However redis-cli can be used to measure the latency of a Redis server in milliseconds, just try:
redis-cli --latency -h `host` -p `port`
https://redis.io/commands/slowlog
slowlog get 2
SLOWLOG RESET

https://redis.io/topics/latency

Latency baseline

There is a kind of latency that is inherently part of the environment where you run Redis, that is the latency provided by your operating system kernel and, if you are using virtualization, by the hypervisor you are using.
While this latency can't be removed it is important to study it because it is the baseline, or in other words, you'll not be able to achieve a Redis latency that is better than the latency that every process running in your environment will experience because of the kernel or hypervisor implementation or setup.
We call this kind of latency intrinsic latency, and redis-cli starting from Redis version 2.8.7 is able to measure it. 
./redis-cli --intrinsic-latency 100
 the argument 100 is the number of seconds the test will be executed. The more time we run the test, the more likely we'll be able to spot latency spikes. 

Since Redis 2.8.13, Redis provides latency monitoring capabilities that are able to sample different execution paths to understand where the server is blocking.

Virtualized environments will not show so good numbers, especially with high load or if there are noisy neighbors. 

Redis uses a mostly single threaded design. This means that a single process serves all the client requests, using a technique called multiplexing. This means that Redis can serve a single request in every given moment, so all the requests are served sequentially. This is very similar to how Node.js works as well. However, both products are often not perceived as being slow. This is caused in part by the small amount of time to complete a single request, but primarily because these products are designed to not block on system calls, such as reading data from or writing data to a socket.
I said that Redis is mostly single threaded since actually from Redis 2.4 we use threads in Redis in order to perform some slow I/O operations in the background, mainly related to disk I/O, but this does not change the fact that Redis serves all the requests using a single thread.

https://redis.io/topics/latency-monitor
latency latest
as per our documentation the CONFIG command is currently restricted[1] and the unknow command error is the expected behaviour:

telnet test-slowlog.XXX.0001.euw1.cache.amazonaws.com 6379
Trying 172.31.0.130...
Connected to test-slowlog.XXX.0001.euw1.cache.amazonaws.com.
Escape character is '^]'.
CONFIG GET *
-ERR unknown command 'CONFIG'

I am not familiar with Redmon but the slowlog command is no restricted, I performed a test without any error:

telnet test-slowlog.XXX.0001.euw1.cache.amazonaws.com 6379
Trying 172.31.0.130...
Connected to test-slowlog.XXX.0001.euw1.cache.amazonaws.com.
Escape character is '^]'.
slowlog get 2


In order to deliver a managed service experience, ElastiCache restricts access to certain cache engine-specific commands that require advanced privileges.
  • For cache clusters running Memcached, there are no restricted commands.
  • For cache clusters running Redis, the following commands are unavailable:
    • bgrewriteaof
    • bgsave
    • config
    • debug
    • migrate
    • save
    • slaveof
    • shutdown
http://blog.kennejima.com/post/1226487020/thoughts-on-redis#
https://blog.newrelic.com/2015/05/11/redis-performance-metrics/
http://skipperkongen.dk/2013/08/27/how-many-requests-per-second-can-i-get-out-of-redis/
To test (set and get):
redis-benchmark -p 7777 -t set,get -q
You should use the redis-benchmark tool to benchmark Redis, for exactly the reasons mentioned in the pitfalls and misconception section on the Redis benchmarking page. The primary reason is that the tool uses multiple connections, and easily enables commands to be pipelined.
This command above uses the -p switch to set the port, uses the -t to limit the commands we test, and finally the -q switch to limit the output to just the throughput.
http://stackoverflow.com/questions/10489298/redis-is-single-threaded-then-how-does-it-do-concurrent-i-o
In server-side software, concurrency and parallelism are often considered as different concepts. In a server, supporting concurrent I/Os means the server is able to serve several clients by executing several flows corresponding to those clients with only one computation unit. In this context, parallelism would mean the server is able to perform several things at the same time (with multiple computation units), which is different.
For instance a bartender is able to look after several customers while he can only prepare one beverage at a time. So he can provide concurrency without parallelism.
This question has been debated here: Concurrency vs Parallelism - What is the difference?
A single-threaded program can definitely provides concurrency at the I/O level by using an I/O (de)multiplexing mechanism and an event loop (which is what Redis does).
Parallelism has a cost: with the multiple sockets/multiple cores you can find on modern hardware, synchronization between threads is extremely expensive. On the other hand, the bottleneck of an efficient storage engine like Redis is very often the network, well before the CPU. Isolated event loops (which require no synchronization) are therefore seen as a good design to build efficient, scalable, servers.
The fact that Redis operations are atomic is simply a consequence of the single-threaded event loop. The interesting point is atomicity is provided at no extra cost (it does not require synchronization). It can be exploited by the user to implement optimistic locking and other patterns without paying for the synchronization overhead.
Redis is single-threaded at user-level, OTOH, all asynchronous I/O is supported by kernel thread pools and/or split-level drivers.
'Concurrent', to some, includes distributing network events to socket state-machines. It's single-threaded, runs on one core, (at user level), so I would not refer to this as concurrent. Others differ..
'scale indefinitely in terms of I/O concurrency' is just being economical with the truth. They may get more belief if they said 'can scale better than one-thread-per-client, providing the clients don't ask for much', though they may then feel obliged to add 'blown away on heavy loading by other async solutions that use all cores at user level'.
https://redis.io/commands/monitor
MONITOR is a debugging command that streams back every command processed by the Redis server. It can help in understanding what is happening to the database. This command can both be used via redis-cli and via telnet.


http://blog.kennejima.com/post/1226487020/thoughts-on-redis#

private JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost");
Jedis jedis = pool.getResource();
jedis.set("MSG", "Hello World");
String result = jedis.get("MSG");
pool.returnResource(jedis);
pool.destroy();

shutdown nosave/save
public class JedisWrapper {
  static JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost");");");");");

  public void set(String key,String value){
    Jedis jedis = pool.getResource();
        jedis.set(key, value);
        pool.returnResource(jedis);
  }

  public String get(String key){
    Jedis jedis = pool.getResource();
        String result = jedis.get("MSG"); ");");");");
        pool.returnResource(jedis);
        return result;
  }
}
redis-benchmark
SETNX key: This key sets a value against a key if key doesn't exist - no overwrite is done.
GETSET key: This key gets the old value and sets a new value.

SET PX/ EX: Removes the values and the key gets expired after expiry time in milliseconds.
SETEX: Removes the values and the key gets expired after the expiry time in seconds

    jedis.hset(commonkey, "publisher", "Packt Publisher");
    System.out.println(jedis.hgetAll(commonkey));
    jedis.hmset(commonkey, attributes);

LPUSHX: This command prepends the values to the list from the left of the list if the key exists.

https://redis.io/commands/INFO
The keyspace section provides statistics on the main dictionary of each database. The statistics are the number of keys, and the number of keys with an expiration.

For each database, the following line is added:
dbXXX: keys=XXX,expires=XXX

https://redis.io/topics/clients
Redis accepts clients connections on the configured listening TCP port and on the Unix socket if enabled. When a new client connection is accepted the following operations are performed:
  • The client socket is put in non-blocking state since Redis uses multiplexing and non-blocking I/O.
  • The TCP_NODELAY option is set in order to ensure that we don't have delays in our connection.
  • readable file event is created so that Redis is able to collect the client queries as soon as new data is available to be read on the socket.
After the client is initialized, Redis checks if we are already at the limit of the number of clients that it is possible to handle simultaneously (this is configured using the maxclients configuration directive

  • It only performs a single read() system call every time there is something new to read from the client socket, in order to ensure that if we have multiple clients connected, and a few are very demanding clients sending queries at an high rate, other clients are not penalized and will not experience a bad latency figure.
  • However once new data is read from a client, all the queries contained in the current buffers are processed sequentially. This improves locality and does not need iterating a second time to see if there are clients that need some processing time.
In Redis 2.6 this limit is dynamic: by default is set to 10000 clients, unless otherwise stated by the maxclientsdirective in Redis.conf.
$ ./redis-server --maxclients 100000
[41422] 23 Jan 11:28:33.179 # Unable to set the max number of files limit to 100032 (Invalid argument), setting the max clients configuration to 10112.
By default recent versions of Redis don't close the connection with the client if the client is idle for many seconds: the connection will remain open forever.
However if you don't like this behavior, you can configure a timeout, so that if the client is idle for more than the specified number of seconds, the client connection will be closed.
client list

http://stackoverflow.com/questions/5415042/using-ehcache-in-front-of-memcached
http://blog.wix.engineering/2015/03/18/scaling-to-100m-to-cache-or-not-to-cache/

https://redis.io/topics/lru-cache
When Redis is used as a cache, often it is handy to let it automatically evict old data as you add new one. 

Starting with Redis version 4.0, a new LFU (Least Frequently Used) eviction policy was introduced. 

The maxmemory configuration directive is used in order to configure Redis to use a specified amount of memory for the data set. It is possible to set the configuration directive using the redis.conf file, or later using the CONFIG SETcommand at runtime.
maxmemory 100mb
Setting maxmemory to zero results into no memory limits.

https://mp.weixin.qq.com/s/YkygrSjCUJOSOAktdS81aQ
它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。


       1、redis由非阻塞单线程模式实现。编程简洁不易出bug,而且qps不低,可以说是极高,另一个无阻塞单线程是nginx,所以说单线程不一定性能差,关键是要看怎样合理使用,设计架构。非阻塞io各个操作系统下



       2、命令式设计,客户端与服务端交互采用命令式进行交互,客户端发送命令到服务端,服务端接收命令后根据命令类型优先级进行相应解析处理。

       

       3、单线程设计,大的数据请求会阻塞后续请求,以致后续整体tp99都会降低使用时需要注意。

       4、超时数据会单独存储,采用定时过期以及访问时过期两种方式。定时过期是随机随段进行过期,非全部遍历进行过期处理。访问时过期,访问数据时,入数据过期进行过期处理。

       5、hash数据结构,hash是kv存储基石,时间复杂度o(n),高效数据结构,快速读写都是通过hash数据机构作为基础实现。

       6、当redis进行扩容或者缩容时,rehash渐进的对原有数据ht[0]生成到新的ht[1]面,当复制完成后。ht[0]销毁,ht[1]变为ht[0],完成扩容或者缩容过程。

      redis分布式相关:
       1、codis集群方案通过codis-proxy代理来管理redis分片,通过代理方式能实现无需客户端更新的升级扩容,以及有问题节点替换处理。通过zookeeper来存储集群元信息。

  2、redis3.0 提供redis cluster集群方案,官方集群方案采用p2p形式。每个节点存储其他节点信息,当数据请求不在节点时,节点返回数据所在节点,通过两次请求完成数据获取。架构好处是非中心架构,通过grossip协议来同步获取集群信息。缺点是集群分片节点过多(比如分片多于600),会导致集群节点间通信过多导致集群性能下降。

       3、单机不能处理超越内存数据量问题,分布式集群方式解决。分布式有cluster和集中存储元数据两种方式。redis分布式一个重要概念是slot,由槽组成分片,多个分片构成集群。

       4、集群监控,内存、连接数、超时时间,这是管理平台设计时需要考虑的,通过配置平台实现配置下发以及集群状态管理。
       
       5、存储系统,当下设计重点热点处理,自动扩容。实际使用时避免单点,热升级,热点分裂。当访问量或数据减少后为了节省资源,增加资源利用率要进行所容。

       redis使用时需要注意和避免的问题:
       1、限制key长度特别是在lrange命令下,数据条数过多会阻塞redis访问。导致其他命令阻塞导致tp99上升。
       
      2、限制单个value大小,业务使用存储一个值有几兆大,访问频率低没有问题,当新上key key访问次数加大,相应接口tp99由毫秒到几秒。

      3、单个key避免读写热电,写热点包含单个key长度写的特别大,再有就是进行访问计数器计算计算写的特别频繁也是热点。

      4、读的分片热点可以通过动态扩容解决,但单个key热点只能增加slave个数并且客户端增加随机访问master以及slave来分散访问热点,再有就是热key通过本地缓存来避免redis热点数据。会导致tp99升高,双11压测线上服务全线性能不行,是热点key。

      5、一次redis整个集群吞吐量下降,mGet了太多数据,618前扩容成为压垮redis的最后一根稻草,过多key拉取会阻塞redis集群。业务涉及要尽量避免过多mGet批量获取。


Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts