给高中学弟学妹们

毕业已10年,但是高中的岁月仿佛昨日,雪天的打闹,同桌的微笑,青春的烦恼;英语的周报,黄冈的卷子,海淀的大纲。

以下是一个过来人的经验之谈,望学弟学妹们看完以后有一些心得。

  1. 专业问题:我只说说计算机专业,其他的大家可以网上找一些资料看。计算机专业主要学习内容偏逻辑和数学。

毕业以后三个主要方向:一个是工程类型,在公司实际编写软件,主要内容是应用语言以及现有框架,实现逻辑,完成功能,俗称码农(www.coolshell.cn,是一个高级工程师的博客,可以看看);另一个是研究类型,主要是编写、完善和优化各种基础软件,为工程码农提供便利(www.apache.com 是一个基金会,全世界最有名的开源软件社区,包含很多种框架类软件,大家可以看看功能);最后一个是学术类型,主要是搞理论研究,搞底层算法,发表论文,是揭示真理的人员(http://www.yinwang.org/ 是一个搞计算机语言分析的工程师的博客,有才但是愤世嫉俗)。

  1. 学习问题:高中阶段学习固然重要,但是我认为大学和研究生阶段的学习更加重要。高中学习有升学压力,学习相对注重应试,学习方向清晰,到了大学以后反而迷茫起来(最起码我当时是)。现在回头想想,人的任何提升,本质上都是认知的提升,回想一下我的往事,凡是有价值的东西,其实都是各种学习和经历得来的,正因为大学的自由,所以大学期间,我们可以从许多方向上提升认知,大学期间至少应该做这些事情:

  2. 学习一种乐器,以后学起来比较困难

  3. 多去图书馆,多读书,特别是名人传记,这样可以参考其他人的奋斗和生活方式
  4. 确定奋斗方向,一旦确定,就全力以赴,老师、同学、学生会、社团甚至学校都是你的资源
  5. 谈一场恋爱,老司机除外

希望大家可以少走弯路,有任何问题,欢迎qq咨询,753567658。

使用 spring AOP 监控代码的执行耗时

前言

使用spring提供的aop功能,我们可以很方便的实现动态代理的功能。在使用上,spring提供了两种不同的实现,分别是Spring AOP 和 AspectJ。

提供方式以及对比

spring AOP

概述

  1. 纯java实现,不需要额外的编译流程,不需要引用其他三方包。
  2. 适合集成到Servlet容器或者应用服务中
  3. 仅支持方法级别的代理,不支持成员变量
  4. 设计宗旨是集成IoC,并且有效的解决大部分企业级应用中的需求,不同于AspectJ的细粒度。
  5. 与AspectJ进行互补

非侵略性是spring设计的一个中心原则,一般情况下,不会有spring的代码存在与业务代码中。但是某些情况中,不是这样,比如注解。

实现

默认使用标准JDK中的 动态代理是实现,针对任意接口,都可以实现。

可以配置使用CGLIB进行代理,可以针对类进行代理。CGLIB的使用,对开发者是透明得到,在针对没有实现接口的类进行代理的时候,spring会自动使用CGLIB进行实现。

一般情况下,建议业务类都实现一个接口,是比较好的编程实现

这个另说把,不必要的接口实现了以后,除了繁琐没有其他作用

在一个类实现了多个接口的时候,可以强制使用CGLIB。

很多考虑的地方 spring文档
xml
<aop:config proxy-target-class="true">
<!-- other beans defined here... -->
</aop:config>

案例

项目需要统计一下一个service类的核心方法执行时间

但是通常,一个核心方法内部有很多的子方法,如何在做代理的时候,将所所有方法进行代理

本次的实现,使用了AspectJ。

代码如下:

代理配置

package com.xxx.utils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * Created by xxxx on 17/3/22.
 */
@Aspect
@Component
public class CostTimeAspect {

    @Around("execution(* com.xxx.service.AService.*(..))")
    public Object printTimeMethod(ProceedingJoinPoint pjp) throws Throwable {

        long time = System.currentTimeMillis();
        Object obj = pjp.proceed();
        long cost = System.currentTimeMillis() - time;
        if (cost > 0) {
            System.out.println(pjp.getSignature().toShortString() + "costs mills:" + cost);
        }
        return obj;
    }

}

spring.xml

<aop:aspectj-autoproxy />

pom.xml

需要在 build 节点中增加,并且要适配指定的jdk版本

<plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>1.4</version>
            <dependencies>
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjrt</artifactId>
                    <version>1.7.3</version>
                </dependency>
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjtools</artifactId>
                    <version>1.7.3</version>
                </dependency>
            </dependencies>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>test-compile</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <outxml>true</outxml>
                <verbose>true</verbose>
                <showWeaveInfo>true</showWeaveInfo>
                <aspectLibraries>
                    <aspectLibrary>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-aspects</artifactId>
                    </aspectLibrary>
                </aspectLibraries>
                <source>1.6</source>
                <target>1.6</target>
            </configuration>
        </plugin>

Java线程安全-《深入了解Java虚拟机》读书笔记

Java线程安全

《深入了解Java虚拟机》读书笔记

阿姆达尔定律(Amdahl law)

Amdahl加速定律定义了一个系统进行并行化改造时候可以提升的性能占比

公式大约如下:
$$
S’=\cfrac{1}{1-f+\cfrac{f}{S}}=\cfrac{1}{1-(1-\cfrac{1}{S})f}
$$

其中,

S’=是整体加速比

f=加速的部分占到整体系统的比重

S=加速了的部分的加速比重

举例:假设某个程序中,你优化了80%的代码,对这80%的代码你获得了加速比10,那么对整个程序而言,你的优化获得的加速比为:1/(1–0.8+0.8/10)=3.57,这远小于10。

S无限增大时候,S’逼近
$$
\cfrac{1}{1-f}
$$
也就是说,优化程序80%的代码,最大获得的加速比为5。

线程安全的定义

当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那么这个对象是线程安全的

——Brian Goetz《Java Concurrency In Practice》

Java中各种操作共享数据分类

不可变

Immutable,JDK1.5内存模型被修正以后的Java语言

Java语言中,如果共享数据是一个基本数据类型,只要在定义时使用final关键字修饰,可以保证是不可变的。

如果是一个对象,那就需要保证对象的行为不会对状态产生任何影响,比如java.lang.String中,substring()、replace()、concat()等方法,都不会影响它原来的值

保证对象行为不受影响的途径,最简单的就是把对象中带有状态的变量都声明为final,比如Integer类

Java API中,不可变的类型包括

  • String
  • 枚举类
  • java.lang.Number的部分子类
  • 不含AtomicInteger,AtomicLong,这两个类使用unsafe 的CAS操作 进行实现

绝对线程安全

完全满足Brian Goetz 提出的要求,调用者也不需要额外的同步措施,Java中基本没有类似的实现。

即使是在全部方法中使用了 synchronized 关键字修饰的 Vector类,在多线程的场景中调用方法,也还是会出现线程问题,虽然每个操作都是原子的,都会线程安全,但是多个原子操作的顺序却可能导致线程问题

相对线程安全

通常意义上的线程安全,保证一个对象单独的操作是线程安全的即可。Java中,大部分线程安全类都是属于这种类型,比如Vector、HashTable、Collections的synchronizedCollection()等

线程兼容

指对象本身不是线程安全的,需要调用者进行正确的同步手段来保证对象在并发环境中可以安全使用。与之前Vector和HashTable对应的ArrayList、HashMap等就是。

Java中绝大多数的API属于这类型

线程对立

指无论调用端是否采取了同步措施,都无法在多线程的环境中并发使用的代码。

Java中此类例子很少,常见的对立的例子如下:

  • Thread类的 suspend()方法以及resume()方法,如果有两个线程同时持有一个线程对象,一个尝试去中断线程,另一个尝试去恢复线程,如果并发进行的话,无论调用时候是否同步,目标线程都有死锁的风险。如果subpend()中断的线程就是即将要执行resume的那个线程,那就肯定要死锁了。由于以上原因,suspend()、resume()方法已经被JDK声明废弃了
  • System.sctIn()
  • System.setOut()
  • System.runFinalizersOnExit()

线程安全实现方式

互斥同步

悲观锁的思路体现、也称阻塞同步

常见的并发正确性方案,同步指的是多个线程并发访问共享数据的时候,保证共享数据在同一时刻只被一个或者一些(信号量时候)线程使用。

互斥是实现同步的一种手段,主要的实现方式如下,互斥是因,同步是果,互斥是方法、同步是目的

  • 临界区
  • 互斥量
  • 信号量

Java中,synchronize关键字是最基本的互斥同步手段。synchronized关键字经过编译以后,会在同步块的前后分别形成monitorenter和 monitorexit两个字节码指令。

字节码需要一个reference类型的参数来指明要锁定和解锁的对象

  • 如果java程序中的synchronized明确指定了对象参数,那就是这个对象的reference
  • 如果没有指定
  • synchronized修饰的是实例方法,取对应的对象实例
  • 修饰静态类方法,取Class对象来作为锁对象

monitorenter 进行锁对象计数器+1,monitorexit进行锁对象计数器-1。

synchronized同步块对于同一线程来说是可重入的,不会出现自己把自己锁死的问题。

阻塞或者唤醒一个线程,都需要操作系统来帮忙完成,这就需要从用户态转化为和心态,因此状态转换会耗费很多的处理器时间。对于简单的同步块,比如getter方法,状态转换可能比用户代码执行的时间还要长。

synchronized在java中是一个重量级操作,非必要情况下不要使用。当然JVM也会做一些优化

使用ReentryLock实现锁

reentryLock与synchronized很相似,具备一样的线程重入特性,不过增加了一些高级功能

  • 等待可中断
  • 当等待锁的线程等待时间太长,可以中断等待,改为处理其他事情
  • 公平锁
  • 多个线程等待同一个锁时候,必须按照申请的时间顺序来依次获得锁,synchronized是非公平的
  • reentryLock可以通过构造参数选择是否公平
  • 锁绑定多个条件
  • 一个ReentryLock可以同时绑定多个Condition对象,synchronized中,wait\notify\notifyAll方法可以实现一个隐含的条件
  • newCondition可以实现多个条件
  • JDK1.6以上,ReentryLock与synchronized的性能完全持平,没有上述场景的前提下,建议使用原生的synchronized实现功能

非阻塞同步

随着硬件指令集的发展,基于冲突检测的乐观并发策略称为 非阻塞同步,区别于互斥同步,是一种先进行操作,发生冲突以后补偿的乐观锁思路。

这种策略需要操作和冲突检测这两个步骤具有原子性(当然不是使用synchronized。。。),常用指令有:

  • 测试并设置(Test-and-Set)
  • 获取并增加(Fetch-and-Increment)
  • 交换(Swap)
  • 比较并交换(Compare-and-Swap,CAS)
  • 加载连接、条件存储(Load Linked/Store Conditional,LL/SC)

前3条是老的指令,后2条是现代处理器新增加的

CAS

三个操作数,分别是内存位置V、旧的预期值A、新值B

CAS操作的时候,当且仅当V符合旧的预期值A时候,处理器会用新值B更新V值,否则不执行

JDK1.5以后,可以使用sun.misc.Unsafe类中的对应方法实行,并且限制了只有启动类加载器加载的class才能访问它,因此,如果不采用反射手段,只能通过其他Java的API来间接使用

AtomicLong等就是这样实现的

无同步方案

没有或者不涉及共享数据的前提下,可以采用这种方案。简单介绍2类

  • 可重入代码(Reentrant Code)所有的的可重入代码都是线程安全的。可重入代码有一些共同的特征,例如不依赖存储在堆上的数据和公用的系统资源,用到的状态量都是由参数中传入、不调用非可重入的方法等。
  • 一个简单的原则可以判断:如果一个方法,它的返回结果可以预测,只要输入了相同的数据,都能返回相同的结果,那就满足可重入的要求
  • 满足条件:

    (1)可以在执行的过程中可以被打断;

    (2)被打断之后,在该函数一次调用执行完之前,可以再次被调用(或进入,reentered)。

    (3)再次调用执行完之后,被打断的上次调用可以继续恢复执行,并正确执行。

  • 可以结合JVM来分析Java中的可重入代码,如果一个函数的所有访问变量,都是以值传递的方式传入局部变量表,那么这个方法就是可重入的。
  • 线程本地存储(Thread Local Storage):在Java中,就是ThreadLocal这个类。共享数g据的可见范围设置在同一个线程以内
  • ThreadLoca类的实现
  • 如果一个变量要被多线程访问,可以使用 volatile关键字声明
  • 经典Web交互模型中,“一个请求对应一个服务器线程”的处理模型

锁优化

自旋锁与自适应自旋

指的是让等待锁的线程通过空循环(自旋),而不进行让出CPU的操作,避免操作系统进行状态切换带来的时间损失。

  • 物理机器需要有一个以上的处理器,能让两个或以上的线程同时并行执行
  • 自旋等待的时间必须有一定的限度
  • -XX:+UseSpinning 开启自旋,1.6以后默认开启
  • -XX:PreBlockSpin 可以修改自旋次数
  • 1.6以后引入的自适应的自旋锁,通过程序运行和性能监控的不断完善,虚拟机对锁进行状况预测

锁消除

如果一段代码中,堆上所有的数据都不会逃逸出去从而被其他线程访问,那就可以认为数据是线程私有的,相当于栈上数据

锁粗化

有一系列的代码需要加锁,那么虚拟机会把加锁同步的范围扩展,粗化到整个操作序列的外部

轻量级锁

JDK1.6引入,在对象头中指定了一个标记位,加锁时候,通过CAS操作将线程栈与对象联系起来

  • 使用CAS操作替代互斥量操作,如果一个对象,只有一个线程要获取锁,那么使用这种情况
  • “对于绝大多数的锁,在整个同步周期内都是不存在竞争的”
  • 如果存在锁竞争,那么除了互斥量的开销以外,还额外发生了CAS操作

偏向锁

JDK1.6引入,同轻量级锁比较,偏向所就是直接将CAS操作也一并省略掉的技术

  • 一个线程在获取对象的锁同时,对象会在标记位中记录下这个线程的id
  • 后续的执行过程中,如果锁没有被其他线程获取,则持有偏向锁的线程将永远不需要再进行同步
  • -XX:+UseBiasedLocking JDK1.6默认开启
  • 有的时候禁止偏向锁可以提高性能

redis知识点

redis

概述、适合场景

summary

  • 支持发布订阅、主从复制、磁盘持久化
  • 多种数据结构
  • 短事务
  • 高吞吐量、低延迟
  • geo支持(地理位置支持,geo作为一个单独的数据结构,有自己的增上改查命令)
  • geoadd
Name Type Data Storage options Query types Additional features Ops Latency
Redis In- memory non- relational database Strings, lists, sets, hashes, sorted sets, geo Commands for each data type for common access patterns, and partial transaction support Publish/ Subscribe, master/ slave replication, disk persistence, scripting (stored procedures) 120000 <1ms
MongoDB On-disk non- relational document store Databases of tables of schema-less BSON documents Commands for create, read, update, delete, conditional queries, and more Supports map- reduce operations, master/ slave replication, sharding, spatial indexes 3500 <100ms
Memcache In-memory key-value cache Mapping of keys to values Commands for create, read, update, delete, and a few others Multithreaded server for additional performance 80000 <1ms
MySQL Relational database Databases of tables of rows, views over tables, spatial and third-party extensions Select, insert, update, delete, functions, stored procedures ACID compliant (with InnoDB), master/ slave and master/ master replication 900 >100ms

Redis介绍

存储实现

内存数据结构

  • redisServer
  • redisDb
    • dict
    • dictht
      • dictEntry(链表)
      • redisObject
        • type
        • encoding
        • ptr
    • expires(过期属性)

redisObject分类

每一种类型的object都有两种实现,分别是压缩和非压缩

  • String
  • int(压缩)
  • sds
  • List
  • linkedlist
  • ziplist(压缩)
  • Hash
  • ht
  • ziplist(压缩)
  • Set
  • ht
  • intset(压缩)
  • Zset
  • skiplist
  • ziplist(压缩)

数据存储类型

  • INT:压缩存储string

  • 常量数字对象共享(0~9999)

  • SDS:存储string

  • int len;

    > int free;
    
    > char buf[]
    
  • 变长字符数组

  • 常用字符串共享

  • 优化长度计算

  • LinkedList 双向列表,存储list

  • 优化长度计算

  • 支持双端遍历

  • HT(hashtable):存储set和hash

  • 扩展、收缩,根据填充绿 used/size

  • 渐进式hash:hash操作、事件触发

  • INTSET:压缩存储set(整数)

  • SKIPLIST:跳表,存储有序集

  • ZIPLIST:压缩存储hash、list、zset

内存管理、淘汰

内存管理
  • Zmalloc(匹配系统,根据系统使用不同的分配算法)
  • Tcmalloc、Jemalloc、Malloc(MAC)
    • 根据不同系统的内存分配长度不同,提前计算,分配指定长度
  • 内存统计
内存淘汰
  • 被动淘汰
  • 达到maxmemory,采用淘汰策略
    • lru
    • volatile-lru/allkeys-lru/
    • random
    • vloatile-random/allkeys-random/
    • ttl
    • vloatile-ttl/
    • Noeviction(simple)
    • 访问时淘汰
  • 主动淘汰(random)

事件模型

网络模型-libae
  • Epoll
  • Select
  • Kqueue
  • Evport(Solaris)
事件类型
  • TimeEvent
  • FileEvent

线程模型

  • MainThread
  • BioThread

重要feature

主从同步

  • 常规同步:异步复制
  • 修改不会直接到从库
  • 主库收到cmd以后,同时修改master_repl_off(repl状态表)
  • 使用real_buf存储最后修改的数据
  • 主库针对 每一个slave有一个output buf
  • 定时 分别将每一个output buf传输到指定的 slave
  • slave返回 real_off响应
  • 主库 根据返回值 修改 master_repl_off表
  • 增量同步
  • Redis状态机

持久化

  • RDB(snapshot)
  • Save(阻塞)/bgsave(非阻塞)
  • 文件格式

    • Redis
    • rdb-version
    • select-db
    • key-value-pairs
    • eof
    • check-sum
    • db-data
    • optional-expire-time
    • type-of-value
    • key
    • value
  • rdbload,全量数据load到内存

  • 每一条数据 checksum检查
  • 通信协议:RESP(redis serialization protocol)
  • 便于实现、解析、理解、二进制安全
  • AOF(oplog)
  • 三种配置, 进行fsync
    • AOF_FSYNC_NO
    • AOF_FSYNC_EVERYSEC(每一秒写入)
    • AOF_FSYNC_ALWAYS(每一条cmd写入磁盘)
  • AOF load(fake client)
  • AOF rewrite
    • merge 不同的cmd,聚合cmd,最后只有一个cmd

集群方案

Twemproxy

​ 高效的路由转发

  • 根据key哈希,数据分片
  • 实现
  • client_in
  • server_in
  • server_out

redis-cluster

​ P2P

  • Gossip协议
  • Auto failover
  • replicas migration
  • online reshard,多个实例会成为一个分片

Mysql&Redis增量同步方案

  • 通过proxy分发请求
  • 写请求发送mysql
  • 读请求发送redis
  • Mysql-binLog 获取数据库增量更新
  • Databus、Broker进行消息队列写入
  • Redis根据队列内容增量更新

大数据系统性能分析

大数据系统性能分析

单机瓶颈分析

增加并发和吞吐

增加并发的方式

  • 主要是增加对CPU的利用
  • 多机
  • 单机多CPU
  • 单CPU多core
  • 单core超线程

方法

  • IO密集型应用:降低线程切换对cpu的浪费
  • 多进程 – 多线程 – 事件驱动 – 协程
  • CPU 密集性应用:增加计算的并发
  • 多进程 – 多线程
  • 除非资源到了瓶颈,否则不排队
  • 合适的并发模型
    • 比如ependingpool、事件触发、异步队列等
  • 队伍要均衡
    • 保证能够打到充分的并发
  • 过长的队伍、及时柔性处理(可丢服务)
    • 大数据系统中经常遇到数据堵塞等场景,需要有良好的柔性处理机制,如优先级机制,清除过期数据的机制,部分服务可丢等机制来解决
  • 能并发的任务不串行
  • 并发情况下,影响等待时间的主要是最长的任务时间
  • 串行情况下,是所有任务时间之和

去除不必要的动作

  • 减少网络重连
  • 长连接
  • 降低连接数
  • 连接池
  • 减少线程切换
  • 线程池
  • 减少内存分配和释放
  • 内存池
  • 减少耗时的操作合运算
  • memset,浮点运算,除法,指数,对数运算,慎用 stl
  • 在线转离线
  • 离线提前进行耗时运算

避免冲突

  • 多线程无锁算法
  • 无锁共享数据、无锁数据结构、Copy on write,更新不影响读取
  • HASH冲突解决(经常遇到由于hash冲突或者锁冲突导致的性能下降)
  • 合理的使用锁
    • 锁的时间尽可能短
    • 降低冲突概率
    • 避免死锁
    • 锁的影响范围尽可能小:blockingQueue的分段锁机制

IO优化

关于磁盘I/O的性能,引用一组Kafka官方给出的测试数据(Raid-5,7200rpm)

  • SequenceI/O: 600MB/s
  • RandomI/O: 100KB/s

随机修改

  • WAL(write ahead
    logging):随机写入转化为顺序的写入,写入成功即可返回,在故障时候通过 log 恢复
  • LSM-Tree:适合的应用场景:insert数据量大,读、update数据量不高,并且一般针对最新数据
  • 方法:写入数据到内存即返回,缓冲到一定量再写入磁盘;读取时候需要merge磁盘读取合内存中的内容(bigtable,
    tera)
  • 减少IO次数,批量去重
  • 适用于请求中重复度较高的,如链接库的写入,后链的特点就是重复度很高,批量去重能够去除较大部分的重复数据,降低对后端服务的io压力

随机读取

  • 尽量减少IOPS,无cache的情况下做到每请求只消耗1IO
  • 通过优化cache淘汰算法提高cache命中率
  • 基于统计的淘汰策略
  • 多级LRU队列
  • 合理李勇Flash存储,通过压缩等手段降低Flash压力

其他

  • 预处理,预充Cache,预热再服务
  • 充分利用硬件
  • 存储速度: 内存 >> SSD >> sata
  • 示例:kafka (顺序读写 + page cache)
    • 顺序写磁盘效率比随机写内存还要高,这是Kafka高吞吐率的一个很重要的保证
    • 充分利用pagecache,直接内存读取直接发送

集群瓶颈分析

减少数据传输量

数据压缩-CPU与网络io的权衡

  1. 减少跨机房io
  2. 打包访问
  3. 减少交互次数
  4. 数据压缩:CPU与网络IO的权衡

压缩算法对比

数据来自于hbase
| 算法 | remaining(%) | Encoding | Decoding |
| ———— | ———— | ——– | ——– |
| GZIP | 13.4% | 21MB/s | 118MB/s |
| LZO | 20.5% | 135MB/s | 410MB/s |
| Zippy/Snappy | 22.2% | 172MB/s | 409MB/s |

Snappy 在 Google 内部被广泛的使用,从 BigTable 到 MapReduce 以及内部的 RPC
系统

均衡

  1. 负载均衡,常用,一个好的负载均衡方法是保证整个分布式系统性能的基础

  2. RR,Random,Locality-aware,hash

  3. 热点+打散

  4. 自动拆分和融合节点

  5. 自动伸缩容量,弹性

  6. 消除长尾(比如分布式系统中有一个实例老是响应时间长,此时可以屏蔽这个节点)

  7. 拆分、并发

  8. 消峰、限流、缓冲+延迟处理(优先级机制)

1.
消息队列中使用优先级方式,可以一方面保证高优请求很快得到处理,另一方面达到全局缓冲

  1. 丢弃、降级服务

  2. 丢弃是说检测到服务扛不住的时候,自动丢弃一些请求
    2.
    降级这里说的是人工方式处理,比如搜索服务中,在高峰期可以关闭广告处理、甚至关闭另一个引擎

案例:mr任务老是跑很长时间,个别子分片总是运行不完

均匀分片

擅用cache

cache种类
  1. 内存 cache、分布式cache
  2. 有结果 cache/ 无结果cache/ 超时cache
  3. 只读cache/ 读写 cache
提升命中率
  1. 合理的cache key 设计
  2. 需要全局考虑一下,兼容各种访问

  3. 有效的淘汰算法

  4. 保存命中率高的item

案例:上游hash寻址下游(us寻址gss)

  1. 容易造成热点问题

Cache对延迟的优化效果

  1. 节省大量耗时操作时间:不必要的计算、网络、IO

  2. 案例7 均值200ms的服务,加了cache,命中70%

  3. 200×0.3+1×0.7=67ms

系统优化

容器+混步

  1. IAAS
  2. matrix
  3. PAAS
  4. Jpass
  5. Galaxy
  6. Beehive

全量模型->增量模型

  1. 适用于 全量数据量大,而增量更新比例小的情况
    1. 实例 linkbase3.0,将连接库从全量+patch
      改造为增量实时读写模型,节约8000槽位x36小时的计算资源
    2. spider实时统计策略,从1500太机器节约到500台以下

避免局部瓶颈

分布式环境下,每个子系统都非常重要

​ 木桶效应

案例9:一个分布式系统,消息队列发给模块a,模块a负责读取存储系统,merge数据,在写回存储系统(即
read-modify-write模型),性能非常低,加并发、加机倌增量更新比例小的情况
1. 实例 linkbas点影响了整体服务

分治的方法,让A模块只处理匹配分片的存储资源,不要全局访问节点

这样出现故障的话,不会影响全局的性能

不要牺牲可维护性

​ 尽量避免设计过度复杂的系统,人力成本也是成本,一定要可维护性高

tera + 每秒400W qps

类似 google 的 bigtable

2016美团面试经历

这是一个雾霾的下午,我来到了美团的办公楼,开始了面试,不知怎么的,来之前,底气有些不足
面的越多,就会越知道自己的不足吧。
事实证明,美团的面试确实不简单,要比百度的更加详细一些,非常的具体,每个问题基本都是技术的更进一步,怎么理解呢?
基本上就是日常工作中多问几个为什么,还真的需要积累

美团的面试只有一轮,在我这里(以下问题不根据时间排序)

java方面

  1. java中的引用有几种?

    1. 强引用,正常创建的new的对象,这些对象一旦被创建,不会被垃圾回收线程销毁
    2. 软引用,如果内存空间够,那么不会被回收,如果空间不够,那么会被回收
    3. 弱引用,下一次垃圾回收肯定会被回收
    4. 虚引用,和回收队列联合使用
    5. final引用
  2. Java中的threadlocal是怎么用的? threadlocal中的内部实现是怎么样的? 哪种引用?

  3. java中的”final”关键字在多线程的语义中,有什么含义
  4. 说说nio的架构,为什么变快了,说说select和buffer都是怎么用的?
  5. 在操作系统中的实现原理? 如果都是cpu轮训话,会不会对cpu影响太大?
  6. 应用到了linux中的什么特性?
  7. nio中, 如果不显式的调用 system.gc() 那会出现什么问题?
  8. jvm的垃圾回收分为哪些种类?每一种都是怎么去实现的?讲述一下G1的回收策略?
    7.
    jvm中的参数分为哪些种类,都是做什么的?jvm的监控怎么做?实际项目上线以后的监控怎么做?
  9. JVM中,如果把堆内存参数配置的超过了本地内存,会怎么样?
  10. JVM中的内存结构分为哪些方面?
  11. 栈空间是怎么样的?每个线程只有一个栈吗?
  12. 栈空间的内部结构是怎么样的?
  13. 堆内存为什么要设计为分代?
  14. ArrayList的实现原理,如何测试ArrayList动态分配内存中带来的内存、cpu变化
  15. ArrayList是不是线程安全的? 怎么实现线程安全的?
  16. synchronizedlock有什么区别?
  17. volatile的作用,如果volatile修饰的对象经过了大量的写,会出现什么问题?
  18. String的+和StringBuilder有什么区别? 放在循环中有什么问题?
  19. 日志打印的过程中,使用String的+操作和使用占位符输出,对性能上有什么区别
  20. SimpleDateFormat如果是一个全局变量的话,有什么问题?
  21. HashMap的操作中,直接使用keySet()遍历有什么问题?
  22. Random中,种子一样的话,就一定会生成一样的随机数吗?
  23. spring的aop是怎么实现的?除了字节码扩展以外,还有其他实现机制吗?
  24. 类加载器的概念,简述一个类加载器的加载过程
  25. 简述一个类的加载过程

计算机结构方面

  1. linux中awk命令的使用?
  2. nginx是多线程还是单线程?
  3. linux中如何监控和查看内存、cpu情况?
  4. 负载分为哪些类别和层次?你们项目中是怎么用的?
  5. mq是如何使用的?
  6. http协议建立连接的过程是怎么样的?
  7. https建立连接的的过程是怎么样的?
  8. forward和redirect有什么区别?
    5.
    linux如何实现nginx的高性能?有什么特性被应用了?直接来说,就是基于linux的网络编程
  9. redis中,是如何进行

数据库方面

  1. 数据流的锁级别,乐观锁和悲观锁的概念,是不是只有悲观锁?
  2. 数据库如何实现事务?
  3. 有没有什么研究深入的技术,或者比较满意的项目?

《三体》和大刘的科幻

《三体》和大刘的科幻


好多年前的一个傍晚,夕阳把树干照的金黄,踩在脚下的落叶碎裂开来,仿佛能闻到一股树木的清香,面前就是常来的那家书店,我知道,人群之中,会有老友静静的等我。那天是5号,《科幻世界》杂志如期而至,与我不见不散。


《科幻世界》的一群老朋友中,最让人钦佩景仰的非刘慈欣先生莫属了。是的,他就是小说《三体》的作者,也是第一位中国科幻文学
“雨果奖”
的加冕者。《三体》,利用三部曲小说,世界观宏大而真实,故事细腻而富有感染力,真令人手不释卷,拍案叫绝。


我第一次认识刘慈欣,是从《科幻世界》上的不定期刊载他文章开始的。当时的刘慈欣,还没有雨果奖的光环加身,但是在我们科幻迷的小圈子中,早已成了偶像一般的人物,并且亲切的称呼刘慈欣先生为
“大刘”。


在当时的科幻文学圈子中,不乏各种各样风格的作家,如王晋康、何夕,与大刘并称带动90年代科幻文学复兴的“三驾马车”,然而他们作品风格迥异,前者苍凉沉郁,冷峻峭拔,后者富有哲理,深不可测;如韩松,作为新华社记者的他,作品带有鲜明的后现代主义文本特征,以卡夫卡式的阴暗绝望,梦魇般的非理性风格,自成一派;如钱莉芳,作为历史老师的她,作品架构于历史之上,却又高于历史,以做学问般的钻研劲儿去写科幻。

​ 但是大刘为何能独树一帜呢?

​ 其实在科幻文学的分支中,素有 “软” 和 “硬” 之分,‘‘软科幻“
尽量以故事情节、寓意与人物性格取胜,降低科学技术和物理定律。“硬科幻”
则在科学技术和物理定律之上加以展开合理的想象,情节往往基于当前的现实展开,充分利用和补充前沿的科学技术和物理学定律。

​ 大刘就是 “硬科幻”
的首席选手。他本身作为娘子关的工程师,对科学技术具有想当的了解,并且针对每种技术具有入木三分的想象,同时他编写的故事富有英雄主义,有激情和热血,场面宏大,磅礴大气。大刘作品不计其数,每一篇都带有大刘对待事物的看法。


《乡村教师》,以山区的教师为原型,借孩子们的口,消除了外星人对地球的攻击,同时歌颂了下乡教室的伟大和勇气;《带上她的眼睛》则借助虚拟视觉和地心探测,描写了一个委婉感人的爱情故事;《圆圆的肥皂泡》,则描写了一个执着的女孩子,靠着对肥皂泡的执着,最后依靠肥皂泡,完成改善了西北缺水情况的壮举;《中国太阳》描述了一种巨型的镜面,在太空中依赖反射太阳光针对某个地区局部加热,达到改善气候的作用,而主人公
“水娃”
则是太空巨型镜面一名普通的清洁人员;《球状闪电》中,大刘靠着神奇的想象力,近乎完美的解释了“球状闪电“这一神奇的现象,同时刻画了简易的中国军人的形象;终于到了关键的《三体》,不客气的说,三体作为一个长篇小说,其中的任意一个想法单独摘录出来,甚至都可以单独成文,可是大刘却毫不吝惜的都写到了一部作品之中。《三体》故事讲述的是地球人和外星人的一系列博弈和战争,以及地球人在广阔、充满敌意或者善意的众多宇宙文明面前,进行对策和一系列产生的后果。难能可贵的是,针对《三体》中所有应用到的物理知识和科学技术,都是在现有基础之上进行合理的扩展和想象,纵观全书,几乎没有一个概念是说不清楚或者无法引用据点的。

​ 不仅如此,大刘本人还是一名非常具有 “闹腾精神”
的理工男,用现在的流行词解释,那就是“极客”。这种示例不胜枚举,比如老早,大刘就编写了一套程序,专门用来生成现代诗,我亲自实验过,生成的现代诗还是不错的。不仅如此,大刘还有针对生命游戏的设计,以及对于足球运动的丰富幻想。

2016-58同城面试经历

58同城的办公地点坐落在酒仙桥的电子产业园,毗邻电影博物馆和798艺术中心。附近没有地铁站。
但是有公交车站以及班车。

58在产业园有3栋楼,还有一栋正在大兴土木。

还没有进入园区,就听到了58的slogan,一直在滚动播放。58的主楼是105号楼。一进入大厅,左边是前台,前台的装饰类似饭店的收银台,正面是供给访客的休息区,休息区左侧是由9块屏幕组成的LED阵列,上面显示58一天内的各种访问数据,每秒去刷新一次。休息区右侧则是58获取的所有奖杯。奖杯墙直通2楼,墙壁上是58创始至今的所有重大事件的timeline。

前台姑娘一看我填的表,惊喜的赞叹竟然是老乡

这次一共进行了两轮技术面试

第一轮面试

面试官主要是根据你项目经验以及实现中的技术进行提问

  1. 说说tomcat中classloader的加载过程
  2. 区别一下jetty和tomcat,两者有何不同
  3. tomcat调优是怎么做的
  4. redis的使用,何时做数据备份,何时部署集群
  5. Map接口有多少种实现,TreeMap内部的数据结构是怎样的,HashMap内部数据结构是怎样的
  6. 介绍一下ElasticSearch,和solr,lucene有何不同,适用何种场景
  7. 说说Java的内存结构
  8. 说说看过的技术书籍
  9. 说说位图法,目前的工作中,都适用于哪些场景

第二轮面试

这一轮也不是纯粹的技术面试了,面试官还会介绍一些其他信息

  1. 介绍一下职业规划
  2. 介绍一下之前公司的离职原因
  3. 之前面试了哪些公司,进入了哪些阶段
  4. 白纸写一个字符串包含的程序,其实是kmp算法