这是一篇晚来的字节客户端凉面
字节跳动:
算法:
大数单链表做减法
单链表自己实现
GCRoot可以用什么作为引用对象:
在java语言中,什么对象可作为GCRoot的对象?
a. java虚拟机栈中的引用的对象。 b.方法区中的类静态属性引用的对象。 (一般指被static修饰的对象,加载类的时候就加载到内存中。) c.方法区中的常量引用的对象。 d.本地方法栈中的JNI(native方法)引用的对象
a.java虚拟机栈中的引用的对象
b.方法区中的类静态属性引用的对象(一般指被static修饰的对象,加载类的时候就加载到内存中。)
c.方法区中的常量引用的对象
d.本地方法栈中的JNI(native方法)引用的对象
五:最后注意
六:方法区中的数据什么时候被回收
DNS缓存中有缓存,没有网可以访问到吗:
我不知道当时随便答了
I++是原子性的吗:
会被打断
俩线程交叉打印i++;
数据库索引的结构
你说你知道集群,那集群使用了什么算法呢:
一致性哈希算法
防止过热
你说的算法这个了解具体实现的吗:
负载均衡里面好像dubbo用了
我这个上链接了:
本文由 简悦 SimpRead 转码, 原文地址 www.paperplanes.de
一致哈希的简单魔法(The Simple Magic of Consistent Hashing)
Mathias Meyer 马蒂亚斯 · 梅耶
December 9, 2011 2011年12月9日
Tags
标签
The simplicity of consistent hashing is pretty mind-blowing. Here you have a number of nodes in a cluster of databases, or in a cluster of web caches. How do you figure out where the data for a particular key goes in that cluster?
一致哈希的简单令人兴奋。在这里,在一个数据库集群或一个 web 缓存集群中有许多节点。如何确定特定密钥的数据在集群中的位置?
You apply a hash function to the key. That’s it? Yeah, that’s the whole deal of consistent hashing. It’s in the name, isn’t it?
对键应用一个散列函数。就这样?是的,这就是一致哈希的全部。在名字里,是吗?
The same key will always return the same hash code (hopefully), so once you’ve figured out how you spread out a range of keys across the nodes available, you can always find the right node by looking at the hash code for a key.
相同的密钥总是返回相同的哈希代码(希望如此) ,因此一旦您了解了如何在可用的节点之间分布一系列密钥,您总是可以通过查看密钥的哈希代码来找到正确的节点。
It’s pretty ingenious, if you ask me. It was cooked up in the lab chambers at Akamai, back in the late nineties. You should go and read the original paper right after we’re done here.
如果你问我的话,我会说这很有创意。这是90年代末在 Akamai 的实验室里做出来的。你应该在我们完成之后马上去读原始报纸。
Consistent hashing solves the problem people desperately tried to apply sharding to pretty nicely and elegantly. I’m not going to bore you with the details on how exactly consistent hashing works. Mike Perham does a pretty good job at that already, and there are many more blog posts explaining implementations and theory behind it. Also, that little upcoming book of mine has a full-length explanation too. Here’s a graphic showing the basic idea of consistent hashing, courtesy of Basho.
一致哈希分片解决了人们拼命想要将分片巧妙而优雅地应用到的问题。我不打算用一致哈希的工作原理的细节来烦你。Mike Perham 在这方面已经做得相当不错了,还有更多的博客文章解释了其背后的实现和理论。而且,我即将出版的那本小书也有一个完整的解释。这里有一张由 Basho 提供的图表,展示了一致哈希的基本概念。
Instead I want to look at the practical implications of consistent hashing in distributed databases and cache farms.
相反,我想看看一致哈希在分布式数据库和缓存农场中的实际意义。
Easier to Avoid Hotspots
更容易避开热点
When you put data on nodes based on a random result, which is what the hash function calculates, a value that’s a lot more random than the key it’s based on, it’s easier to avoid hotspots. Why?
当你基于一个随机结果将数据放置在节点上时,这就是散列函数计算的结果---- 一个比它所基于的密钥更随机的值---- 这样就更容易避开热点。为什么?
Assume a key based on an increasing value, or a simple range of keys, based on the hour of the day, like 2011-12-11-13. You add new hours and therefore new data as time passes, and keys are stored based on the range they fall in. For example, the keys 2011-12-11-18 until 2011-12-11-23 are stored on the same node, with the rest of the keys stored on other nodes, just because the ranges or the partitioning scheme happen to be set up this way.
假设一个键基于一个递增的值,或一个简单的键范围,基于一天中的小时,如2011-12-11-13。您可以添加新的小时,因此随着时间的推移也会添加新的数据,并且键将根据它们所在的范围存储。例如,2011-12-11-18到2011-12-11-23之间的密钥存储在同一个节点上,其余的密钥存储在其他节点上,只是因为范围或分区模式恰好是这样设置的。
For a consumer-facing site, the evening hours are usually the busiest time of the day. They create more data, more writes, and possibly more reads too. For the hours between 18:00 and 23:00, all the load goes to the single node that carries all the relevant data.
对于面向消费者的网站,晚上通常是一天中最繁忙的时间。他们创造了更多的数据,更多的写,可能还有更多的读。在18:00到23:00之间的几个小时里,所有的负载都转移到了一个节点上,这个节点承载了所有相关的数据。
But when you determine the location in the cluster based solely on the hash of the key, chances are much higher that two keys lexicographically close to each other end up on different nodes. Thus, the load is shared more evenly. The disadvantage is that you lose the order of keys.
但是,当您仅仅根据键的散列值确定集群中的位置时,两个键在字母顺序上彼此接近的情况最终出现在不同节点上的可能性要高得多。因此,负载分担得更加均匀。缺点是你会丢失按键的顺序。
There are partitioning schemes that can work around this, even with a range-based key location. HBase (and Google’s BigTable, for that matter) stores ranges of data in separate tablets. As tablets grow beyond their maximum size, they’re split up and the remaining parts re-distributed. The advantage of this is that the original range is kept, even as you scale up.
有一些分区方案可以解决这个问题,即使使用基于范围的键位置。HBase (以及谷歌的 BigTable)将数据范围存储在单独的平板电脑中。当平板电脑超过它们的最大尺寸时,它们就会被分开,剩下的部分会被重新分配。这样做的好处是,即使在你扩大规模的时候,原来的范围也保持不变。
Consistent Hashing Enables Partitioning
支持分区一致哈希
When you have a consistent hash, everything looks like a partition. The idea is simple. Consistent hashing forms a keyspace, which is also called continuum, as presented in the illustration. As a node joins the cluster, it picks a random number, and that number determines the data it’s going to be responsible for. Everything between this number and one that’s next in the ring and that has been picked by a different node previously, is now belong to this node. The resulting partition could be of any size theoretically. It could be a tiny slice, or a large one.
当您有一个一致的散列时,所有内容看起来都像一个分区。这个想法很简单。一致哈希形成了一个密钥空间,也称为连续体,如图所示。当一个节点加入集群时,它选择一个随机数,这个数决定了它将要负责的数据。这个数字和环中下一个数字之间的所有数字,以及之前被另一个节点选中的数字,现在都属于这个节点。由此产生的分区在理论上可以是任何大小。它可以是一小片,也可以是一大片。
First implementations of consistent hashing still had the problem that a node picking a random range of keys resulted in one node potentially carrying a larger keyspace than others, therefore still creating hotspots.
一致哈希的第一个实现仍然存在一个问题,即一个节点选择一个随机的密钥范围导致一个节点潜在地比其他节点携带更大的密钥空间,因此仍然创建热点。
But the improvement was as simple as it was ingenious. A hash function has a maximum result set, a SHA-1 function has a bit space of 2^160. You do the math. Instead of picking a random key, a node could choose from a fixed set of partitions, like equally size pizza slices. But instead of picking the one with the most cheese on, everyone gets an equally large slice. The number of partitions is picked up front, and practically never changes over the lifetime of the cluster.
但是,这种改进既简单又巧妙。哈希函数有一个最大结果集,SHA-1函数的位空间为2 ^ 160。你自己算算。节点可以从一组固定的分区中进行选择,而不是选择一个随机的键,比如等大小的比萨饼切片。但不是选择奶酪最多的那个,而是每个人都得到同样大的一片。分区的数量是提前获取的,并且在集群的生存期内几乎不会发生任何更改。
For good measure, here’s a picture of a sliced pizza.
另外,这里还有一张比萨片的图片。
Partitioning Makes Scaling Up and Down More Predictable
分区使向上和向下扩展更具可预测性
With a fixed number of partitions of the same size, adding new nodes becomes even less of a burden than with just consistent hashing. With the former, it was still unpredictable how much data had to be moved around to transfer ownership of all the data in the range of the new node. One thing’s for sure, it already involved a lot less work than previous methods of sharding data.
对于同样大小的固定数量的分区,添加新节点的负担甚至比只添加一致哈希节点更轻。对于前者,仍然无法预测需要移动多少数据才能转移新节点范围内所有数据的所有权。有一件事是肯定的,它所涉及的工作已经比以前的数据分片方法少得多。
With partitioning, a node simply claims partitions, and either explicitly or implicitly asks the current owners to hand off the data to them. As a partition can only contain so many keys, and randomness ensures a somewhat even spread of data, there’s a lot less unpredictability about the data that needs to be transferred.
使用分区,节点只是声明分区,并显式或隐式地请求当前所有者将数据交给它们。由于一个分区只能包含那么多的键,而随机性又确保了数据在某种程度上的均匀分布,因此需要传输的数据的不可预测性就大大降低了。
If that partitions just so happens to carry the largest object by far in you whole cluster, that’s something even consistent hashing can’t solve. It only cares for keys.
如果这个分区恰好携带了整个集群中最大的对象,那么这个问题即使是一致哈希也无法解决。它只关心钥匙。
Going back to HBase, it cares for keys and the size of the tablet the data is stored in, as it breaks up tablets once they reach a threshold. Breaking up and reassigning a tablet requires coordination, which is not an easy thing to do in a distributed system.
回到 HBase,它关心键和数据存储在平板电脑的大小,因为一旦平板电脑达到一个临界值,它就会分解。分解和重新分配平板电脑需要协调,这在分布式系统中不是一件容易的事。
Consistent Hashing and Partitioning Enable Replication
一致哈希和分区支持复制
Consistent hashing made one thing a lot easier: replicating data across several nodes. The primary means for replication is to ensure data survives single or multiple machine failures. The more replicas you have, the more likely is your data to survive one or more hardware crashes. With three replicas, you can afford to lose two nodes and still serve the data.
一致哈希使一件事情变得简单多了: 在多个节点上复制数据。复制的主要方法是确保数据在单个或多个机器故障后仍然存在。您拥有的副本越多,您的数据在一个或多个硬件崩溃中存活的可能性就越大。使用三个副本,您可以丢失两个节点,但仍然可以提供数据。
With a fixed set of partitions, a new node can just pick the ones it’s responsible for, and another stack of partitions it’s going to be a replica for. When you really think about it, both processes are actually the same. The beauty of consistent hashing is that there doesn’t need to be master for any piece of data. Every node is simply a replica of a number of partitions.
对于一个固定的分区集,一个新的节点可以只挑选它负责的分区,而另一个分区堆栈将是它的副本。当你仔细想想,这两个过程实际上是一样的。一致哈希的美妙之处在于不需要掌握任何数据。每个节点只是一些分区的副本。
But replication has another purpose besides ensuring data availability.
但是复制除了确保数据可用性之外还有另一个目的。
Replication Reduces Hotspots (Even More!!!)
复制减少热点(甚至更多! !)
Having more than one replica of a single piece of data means you can spread out the request load even more. With three replicas of that data, residing on three different nodes, you can now load-balance between them. Neat!
拥有单个数据块的多个副本意味着您可以进一步分散请求负载。有了这些数据的三个副本(驻留在三个不同的节点上) ,现在就可以在它们之间实现负载平衡。太棒了!
With that, consistent hashing enables a pretty linear increase in capacity as you add more nodes to a cluster.
通过这种方式,当你向集群添加更多的节点时,一致哈希可以实现容量的线性增长。
Consistent Hashing Enables Scalability and Availability
一致哈希可扩展性和可用性
Consistent hashing allows you to scale up and down easier, and makes ensuring availability easier. Easier ways to replicate data allows for better availability and fault-tolerance. Easier ways to reshuffle data when nodes come and go means simpler ways to scale up and down.
一致哈希可以让你更容易地扩展和缩小规模,并使确保可用性更容易。复制数据的更简单方法允许更好的可用性和容错性。当节点来来去去时,重新分配数据更简单的方法意味着更简单的上下扩展方法。
It’s an ingenious invention, one that has had a great impact. Look at the likes of Memcached, Amazon’s Dynamo, Cassandra, or Riak. They all adopted consistent hashing in one way or the other to ensure scalability and availability.
这是一个独创性的发明,产生了巨大的影响。看看 Memcached,亚马逊的 Dynamo,Cassandra,或者 Riak。它们都以这样或那样的方式采用了一致哈希技术,以确保可扩展性和可用性。
Want to know more about distributed databases in general and Riak in particular? You’ll like the Riak Handbook, a hands-on guide full of practical examples and advice on how to use Riak to ensure scalability and availability for your data.
想知道更多关于分布式数据库的信息吗?您会喜欢 Riak 手册,这是一本实践指南,充满了关于如何使用 Riak 来确保数据的可伸缩性和可用性的实用示例和建议。
In the next installment we’re looking at the consequences and implications of losing key ordering in a Riak cluster.
在下一期文章中,我们将讨论在 Riak 集群中丢失密钥排序的后果和影响。
[
Previous 上一页
An Update On The NoSQL Handbook NoSQL 手册的更新](/2011/9/7/an-update-on-the-nosql-handbook.html)[
Next 下一个
List All of the Riak Keys 列出所有的里亚克钥匙](/2011/12/13/list-all-of-the-riak-keys.html)
线程池了解吗?
七大参数三种类型四大拒绝策略
有实际使用过吗?
没有
你说的强软弱虚拟引用在开发的时候用过吗?
Java中的四种引用
Java中有四种引用类型:强引用、软引用、弱引用、虚引用。
Java为什么要设计这四种引用
Java的内存分配和内存回收,都不需要程序员负责,都是由伟大的JVM去负责,一个对象是否可以被回收,主要看是否有引用指向此对象,说的专业点,叫可达性分析。
Java设计这四种引用的主要目的有两个:
可以让程序员通过代码的方式来决定某个对象的生命周期;
有利用垃圾回收。
强引用
强引用是最普遍的一种引用,我们写的代码,99.9999%都是强引用:
Object o = new Object();
这种就是强引用了,是不是在代码中随处可见,最亲切。
只要某个对象有强引用与之关联,这个对象永远不会被回收,即使内存不足,JVM宁愿抛出OOM,也不会去回收。
那么什么时候才可以被回收呢?当强引用和对象之间的关联被中断了,就可以被回收了。
我们可以手动把关联给中断了,方法也特别简单:
o = null;
我们可以手动调用GC,看看如果强引用和对象之间的关联被中断了,资源会不会被回收,为了更方便、更清楚的观察到回收的情况,我们需要新写一个类,然后重写finalize方法,下面我们来进行这个实验:
public class Student { @Override protected void finalize() throws Throwable { System.out.println("Student 被回收了"); } } public static void main(String[] args) { Student student = new Student(); student = null; System.gc(); }运行结果:
Student 被回收了
可以很清楚的看到资源被回收了。
当然,在实际开发中,千万不要重写finalize方法
在实际的开发中,看到有一些对象被手动赋值为NULL,很大可能就是为了“特意提醒”JVM这块资源可以进行垃圾回收了。
软引用
下面先来看看如何创建一个软引用:
SoftReference<Student>studentSoftReference=new SoftReference<Student>(new Student());
软引用就是把对象用SoftReference包裹一下,当我们需要从软引用对象获得包裹的对象,只要get一下就可以了:
SoftReference<Student>studentSoftReference=new SoftReference<Student>(new Student()); Student student = studentSoftReference.get(); System.out.println(student);软引用有什么特点呢:当内存不足,会触发JVM的GC,如果GC后,内存还是不足,就会把软引用的包裹的对象给干掉,也就是只有在内存不足,JVM才会回收该对象。
还是一样的,必须做实验,才能加深印象:
SoftReference<byte[]> softReference = new SoftReference<byte[]>(new byte[1024*1024*10]); System.out.println(softReference.get()); System.gc(); System.out.println(softReference.get()); byte[] bytes = new byte[1024 * 1024 * 10]; System.out.println(softReference.get());我定义了一个软引用对象,里面包裹了byte[],byte[]占用了10M,然后又创建了10Mbyte[]。
运行程序,需要带上一个参数:
-Xmx20M
代表最大堆内存是20M。
运行结果:
[B@11d7fff [B@11d7fff null可以很清楚的看到手动完成GC后,软引用对象包裹的byte[]还活的好好的,但是当我们创建了一个10M的byte[]后,最大堆内存不够了,所以把软引用对象包裹的byte[]给干掉了,如果不干掉,就会抛出OOM。
软引用到底有什么用呢?比较适合用作缓存,当内存足够,可以正常的拿到缓存,当内存不够,就会先干掉缓存,不至于马上抛出OOM。
弱引用
弱引用的使用和软引用类似,只是关键字变成了WeakReference:
WeakReference<byte[]> weakReference = new WeakReference<byte[]>(new byte[1024*1024*10]); System.out.println(weakReference.get());弱引用的特点是不管内存是否足够,只要发生GC,都会被回收:
WeakReference<byte[]> weakReference = new WeakReference<byte[]>(new byte[1]); System.out.println(weakReference.get()); System.gc(); System.out.println(weakReference.get());运行结果:
[B@11d7fff null可以很清楚的看到明明内存还很充足,但是触发了GC,资源还是被回收了。弱引用在很多地方都有用到,比如ThreadLocal、WeakHashMap。
虚引用
虚引用又被称为幻影引用,我们来看看它的使用:
ReferenceQueue queue = new ReferenceQueue(); PhantomReference<byte[]> reference = new PhantomReference<byte[]>(new byte[1], queue); System.out.println(reference.get());虚引用的使用和上面说的软引用、弱引用的区别还是挺大的,我们先不管ReferenceQueue 是个什么鬼,直接来运行:
null
竟然打印出了null,我们来看看get方法的源码:
public T get() { return null; }这是几个意思,竟然直接返回了null。
这就是虚引用特点之一了:无法通过虚引用来获取对一个对象的真实引用。
那虚引用存在的意义是什么呢?这就要回到我们上面的代码了,我们把代码复制下,以免大家再次往上翻:
ReferenceQueue queue = new ReferenceQueue(); PhantomReference<byte[]> reference = new PhantomReference<byte[]>(new byte[1], queue); System.out.println(reference.get());创建虚引用对象,我们除了把包裹的对象传了进去,还传了一个ReferenceQueue,从名字就可以看出它是一个队列。
虚引用的特点之二就是 虚引用必须与ReferenceQueue一起使用,当GC准备回收一个对象,如果发现它还有虚引用,就会在回收之前,把这个虚引用加入到与之关联的ReferenceQueue中。
我们来用代码实践下吧:
ReferenceQueue queue = new ReferenceQueue(); List<byte[]> bytes = new ArrayList<>(); PhantomReference<Student> reference = new PhantomReference<Student>(new Student(),queue); new Thread(() -> { for (int i = 0; i < 100;i++ ) { bytes.add(new byte[1024 * 1024]); } }).start(); new Thread(() -> { while (true) { Reference poll = queue.poll(); if (poll != null) { System.out.println("虚引用被回收了:" + poll); } } }).start(); Scanner scanner = new Scanner(System.in); scanner.hasNext(); }运行结果:
Student 被回收了 虚引用被回收了:java.lang.ref.PhantomReference@1ade6f1我们简单的分析下代码:
第一个线程往集合里面塞数据,随着数据越来越多,肯定会发生GC。 第二个线程死循环,从queue里面拿数据,如果拿出来的数据不是null,就打印出来。
从运行结果可以看到:当发生GC,虚引用就会被回收,并且会把回收的通知放到ReferenceQueue中。
虚引用有什么用呢?在NIO中,就运用了虚引用管理堆外内存。
你在实际开发中这四种引用哪个多呢?
我不会猜了个软的
实际是弱的,他也没有告诉我我也没有问太紧张了
说了个weakHashMap()
后来发现是想把我往ThreadLocal里面引
线程我基本没有看过
JVM分区,tcp/udp,HTTP1.0~3.0就不放了哦
#字节跳动##面经##java工程师#