阿里面试官亲述:如何利用设计模式改善业务代码

简介: 在业务部门的开发中,大多数的我们在完成的业务的各种需求和提供解决方案,很多场景下的我们通过 CRUD 就能解决问题,但是这样的工作对技术人的提升并不多,如何让自己从业务中解脱出来找到写代码的乐趣呢,我做过一些尝试,使用设计模式改善自己的业务代码就是其中的一种。让代码变得更加简洁和提升健壮性,从代码中寻找一些欢乐。

在业务部门的开发中,大多数的我们在完成的业务的各种需求和提供解决方案,很多场景下的我们通过 CRUD 就能解决问题,但是这样的工作对技术人的提升并不多,如何让自己从业务中解脱出来找到写代码的乐趣呢,我做过一些尝试,使用设计模式改善自己的业务代码就是其中的一种。让代码变得更加简洁和提升健壮性,从代码中寻找一些欢乐。

前言

阿里优秀的人很多,他们身上都有着共同的特质,就是看问题的思考能力,让我最佩服的是思考力强的人,对事情有深入见解和观点的人,大多数人还是停留在表面看问题,很多禁锢在思想里逃不出来,古人说,立德立言立功为三不朽,立言就是思考力和认知力,人和人的差异,在长久的职场中或者生活中,除去运气外,其实就是认知和思考力的差异。所以除去繁琐的工作后,如何在有限的时间从代码中寻找欢乐,需要提高的是思考和规划能力。整理了一份562页设计模式PDF文档

责任链设计模式

▐ 模式定义

image.png

责任链模式(Chain of Responsibility Pattern), 是行为型设计模式之一。这种模型结构有点类似现实生活中铁链,由一个个铁环首尾相接构成一条链,如果这种结构用在编程领域,则每个节点可以看做一个对象,每个对象有不同的处理逻辑,将一个请求从链的首端发出,沿着链的路径依次传递每个节点对象,直到有对象处理这个请求为止,我们将这样一种模式称为责任链模式。

▐ 适用场景

适用于多节点的流程处理,每个节点完成各自负责的部分,节点之间不知道彼此的存在,比如 OA 的审批流,Java Web 开发中的 Filter 机制。

  • 多个对象可以处理同一个请求,但具体由哪个对象处理则在运行时动态决定。

  • 在请求处理者不明确的情况下向对个对象中的一个提交一个请求。

  • 需要动态处理一组对象处理请求。

举一个生活中的例子,比如你突然想世界那么大你想去看看,但是处于现实的你还不能丢了工作,得到请假的OA申请,请假天数如果是半天到1天,可能直接主管批准即可;如果是1到3天的假期,需要部门经理批准;如果是3天到30天,则需要总经理审批;大于30天,正常不会批准。这种简单的流程即可试用于我们当前业务场景。

▐ 实践经验

业务流程很简单:

  • 打电话注销信用卡

  • 工作人员注销信用卡

注销信用卡有个背景是这样的,如果信用卡存在账单未还清,存在溢出款,存在特殊年费未使用等情况是不允许注销信用卡的,鉴于此,我们在注销之前加了一套是否允许注销的检验逻辑。

大体如下:

  • 是否存在账单未还清,比如有已出账单未还清,有未出账单未还清,有年费管理费等未还清等。

  • 是否存在溢出款多余的钱。

  • 是否存在高额积分未使用,需用户确认放弃积分等。

针对这几类情况建立了三类过滤器,分别是:

  • UserLogoutUnpaidBillsLimitFilter:是否存在未还清金额。

  • UserLogoutOverflowLimitFilter:是否存在溢出款。

  • UserLogoutGiveUpPointsLimitFilter:是否放弃高额金额。

判断逻辑是先通过UserLogoutUnpaidBillsLimitFilter判断当前用户是否可以注销信用卡。如果允许继续由 UserLogoutOverflowLimitFilter 判断是否存在溢出款,是否可以注销信用卡;如果没有溢出款继续由UserLogoutGiveUpPointsLimitFilter 判断当前用户是否存在高额积分,前面三条判断,只要有一个不满足就提前返回。

public boolean canLogout(String userId) {
        //获取用户信息
        UserInfo userInfo = getUserInfo(userId);

        // 构造注销信用卡限制过滤器链条
        LogoutLimitFilterChain filterChain = new LogoutLimitFilterChain();
        filterChain.addFilter(new UserLogoutUnpaidBillsLimitFilter());
        filterChain.addFilter(new UserLogoutOverflowLimitFilter());
        filterChain.addFilter(new UserLogoutGiveUpPointsLimitFilter());
        boolean checkResult = filterChain.doFilter(filterChain, userInfo);

        //filterChain.doFilter方法
        public boolean doFilter (LogoutLimitFilterChain filterChain, UserInfo userInfo){
            //迭代调用过滤器
            if (index < filters.size()) {
                return filters.get(index++).doFilter(filterChain, userInfo);
            }
        }
    }

    //UserLogoutUnpaidBillsLimitFilter.doFilter方法
    public boolean doFilter(LogoutLimitFilterChain filterChain, UserInfo userInfo) {
        //获取用户当前欠款金额
        UserCardBillInfo userCardBillInfo = findUserCardBillInfo(userInfo);

        // 判断当前卡用户是否允许消费
        if (userCardBillInfo != null) {
            if ((!CAN_LOGOUT.equals(userCardBillInfo.getEnabledLogout()))) {
                return false;
            }
        }
        //其余情况,继续往后传递
        return filterChain.doFilter(filterChain, memberInfo, consumeConfig);
    }

    //UserLogoutOverflowLimitFilter.doFilter方法
    public boolean doFilter(LogoutLimitFilterChain filterChain, UserInfo userInfo) {
        //判断用户是否存在溢出款
        UserCardDeposit userCardDeposit = findUserCardDeposit(userInfo);

        // 判断当前卡用户是否允许消费
        if (userCardDeposit != null) {
            if (userCardDeposit.getDeposit() != 0) {
                return false;
            }
        }
        //其余情况,继续往后传递
        return filterChain.doFilter(filterChain, memberInfo, consumeConfig);
    }

总结:将每种限制条件的判断逻辑封装到了具体的 Filter 中,如果某种限制条件的逻辑有修改不会影响其他条件,如果需要新加限制条件只需要重新构造一个 Filter 织入到 FilterChain 上即可。

责任链中一个处理者对象,其中只有两个行为,一是处理请求,二是将请求转送给下一个节点,不允许某个处理者对象在处理了请求后又将请求转送给上一个节点的情况。对于一条责任链来说,一个请求最终只有两种情况,一是被某个处理对象所处理,另一个是所有对象均未对其处理,前一种情况称该责任链为纯的责任链,对于后一种情况称为不纯的责任链,实际应用中,多为不纯的责任链。整理了一份562页设计模式PDF文档

策略设计模式

▐ 模式定义

image.png

策略这个词应该怎么理解,打个比方说,我们出门的时候会选择不同的出行方式,比如骑自行车、坐公交、坐火车、坐飞机等等,这些出行方式,每一种都是一个策略。

再比如我们去逛商场,商场现在正在搞活动,有打折的、有满减的、有返利的等等,其实不管商场如何进行促销,说到底都是一些算法,这些算法本身只是一种策略,并且这些算法是随时都可能互相替换的,比如针对同一件商品,今天打八折、明天满100减30,这些策略间是可以互换的。

策略模式(Strategy Pattern)是定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换。

▐ 适用场景

主要是为了消除大量的 if else 代码,将每种判断背后的算法逻辑提取到具体的策略对象中,当算法逻辑修改时对使用者无感知,只需要修改策略对象内部逻辑即可。这类策略对象一般都实现了某个共同的接口,可以达到互换的目的。

  • 多个类只有算法或行为上稍有不同的场景

  • 算法需要自由切换的场景

  • 需要屏蔽算法规则的场景

▐ 实践经验

业务流程很简单:

  • 挑选商品

  • 选择不同的优惠方式结账

比如即将到来的双十一活动某些线下商家举办活动,折扣力度如下满300-80,部分商品5折,根据不同会员等级享受不同的折扣最低7折,周年庆活动可享8折等等。假如这些活动折扣不可同享,那么如何去实现以及考虑可扩展性的话策略模式是一种不错的选择。

/**
 * 抽象折扣策略接口
 */

public abstract class DiscountStrategy {
  /**
   * 计算折扣后的价格
    * @param price      原价
   * @return           折扣后的价格
   */
  public abstract CalculationResult getDiscountPrice(Long userId ,BigDecimal price);
}

/**
 * 满减活动 -- 满300减80
 */
public class FullReductionStrategyOne extends DiscountStrategy {
    /**
     * 计算折扣后的价格
     * @param price      原价
     * @return
     */
    @Override
    public CalculationResult getDiscountPrice(Long userId ,BigDecimal price) {
        if (price.doubleValue() < 300) {
            return price;
        }
        BigDecimal dealPrice= price.subtract(BigDecimal.valueOf(80));
        return getCalculationResult(userId,dealPrice);
    }
}

/**
 * 部分商品5折
 */
public class MerchandiseDiscountStrategy extends DiscountStrategy {
    /**
     * 计算折扣后的价格
     * @param price      原价
     * @return
     */
    @Override
    public CalculationResult getDiscountPrice(BigDecimal price) {
        BigDecimal dealPrice=  price.multiply(BigDecimal.valueOf(0.5));
        return getCalculationResult(userId,dealPrice);
    }
}

/**
*当有新的需求式,我们只需要添加一个新的接口即可,不需要修改原有的具体策略实现代码即可完成。
*定义完策略后,我们再定义一个”环境角色”,假设我们这个环境角色就使用价格对象吧
*/

public class Price {

    private DiscountStrategy discountStrategy;

    /**
     * 定义一个无参构造,用于实例对象
     */
    private Price(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }

    /**
     * 获取折扣后的价格
     *
     * @param price 原始价格
     * @return
     */
    public CalculationResult discount(Long userId,BigDecimal price) {
        return discountStrategy.getDiscountPrice(userId ,price);
    }
}

策略模式是一种行为型模式,将算法的使用和算法本身分割开,委派给不同的对象管理。策略实现类一般是封装好的轻量级的算法,当客户端(调用方)遇到不同的情况时,这些算法可以根据需要动态地去互相替换。策略的选择完全是由客户端进行的,这就要求客户端必须理解所有的策略实现类,虽然提高了系统的灵活性,但也增加了客户端的使用难度。策略模式体现了开闭原则——“对扩展开放,对修改关闭”。新的策略增加时,不会影响其他类的修改,增加了扩展性,对扩展开放;只依赖于抽象,而不依赖于具体实现,所以对修改是关闭的。这样在提高代码扩展性的同时,也降低了耦合。

总结:将每种通道的推送逻辑封装到了具体的策略中,某种策略的变更不会影响其他策略,由于实现了共同接口,所以策略可以互相替换,对使用者友好。比如 Java ThreadPoolExecutor 中的任务拒绝策略,当线程池已经饱和的时候会执行拒绝策略,具体的拒绝逻辑被封装到了 RejectedExecutionHandler 的 rejectedExecution 中。

模板设计模式

▐ 模式定义

image.png

模板的价值就在于骨架的定义,骨架内部将问题处理的流程已经定义好,通用的处理逻辑一般由父类实现,个性化的处理逻辑由子类实现。

比如炒土豆丝和炒麻婆豆腐,大体逻辑都是:

1、切菜

2、放油

3、炒菜

4、出锅

1,2,4 都差不多,但是第 3 步是不一样的,炒土豆丝得拿铲子翻炒,但是炒麻婆豆腐得拿勺子轻推,否则豆腐会烂。

▐ 使用场景

不同场景的处理流程,部分逻辑是通用的,可以放到父类中作为通用实现,部分逻辑是个性化的,需要子类去个性实现。

模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。

▐ 实践经验

还是接着之前商品折扣的例子来说,后期我们新加了两个需求:

  • 用户享受不同折扣增加 trace。

  • 用户享受折扣后是否升级会员等级。

所以现在的流程变成了这样:

1、trace 开始。

2、计算用户不同折扣力度。

3、是否允许升级会员等级,如果允许执行升级会员等级逻辑。

4、trace 结束。

其中 1 和 4 是通用的,2 和 3 是个性化的,鉴于此可以在折扣策略之前增加了一层父类的策略,将通用逻辑放到了父类中。

修改后的代码如下:

abstract class AbstractDiscountStrategy implements DiscountStrategy{

    @Override
    public CalculationResult getDiscountPrice(Long userId ,BigDecimal price) {

        //1.构造span
        Span span = buildSpan();
        //2.具体通道推送逻辑由子类实现
        CalculationResult  calculationResult =getCalculationResult(userId,price);

        //3.是否允许升级会员等级,如果允许执行升级逻辑
        if(!calculationResult.isSuccess() && canUpgrade()){
            upgradeLevel(userId,calculationResult);
        }

        //4.trace结束
        span.finish();
        return calculationResult;
    } 

    //具体推送逻辑由子类实现
    protected abstract CalculationResult getCalculationResult(Long userId,BigDecimal price) ;

    //是否允许升级会员等级由子类实现
    protected abstract boolean canUpgrade(Long userId,CallResult callResult);

}

/**
 * 满减活动 -- 满300减80
 */
public class FullReductionStrategyOne extends AbstractDiscountStrategy {
    @Override
    protectedCalculationResult getCalculationResult(Long userId,BigDecimal price){
        //执行折扣逻辑
    }

    @Override
    protected  boolean canUpgrade(Long userId,CallResult callResult){
        return false
    }
}

观察者设计模式

▐ 模式定义

image.png

拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价 这种模式就可以使用观察者模式。顾名思义,此模式需要有观察者(Observer)和被观察者(Observable)两类角色。当 Observable 状态变化时会通知 Observer,Observer 一般会实现一类通用的接口。

比如 java.util.Observer,Observable 需要通知 Observer 时,逐个调用 Observer 的 update 方法即可,Observer 的处理成功与否不应该影响 Observable 的流程。

▐ 使用场景

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

一个对象(Observable)状态改变需要通知其他对象,Observer 的存在不影响 Observable 的处理结果,Observer 的增删对 Observable 无感知。

比如 Kafka 的消息订阅,Producer 发送一条消息到 Topic,至于是 1 个还是 10 个 Consumer 订阅这个 Topic,Producer 是不需要关注的。

▐ 实践经验

在责任链设计模式那块我通过三个 Filter 解决了注销信用卡限制检验的问题,其中有一个 Filter 是用来检验用户积分的,我这里只是读取用户的积分总额和次数,那么消费次数获得积分的累加是怎么完成的呢?

其实累加这块就用到了观察者模式,具体来讲是这样,当交易系统收到支付成功回调时会通过 Spring 的事件机制发布“支付成功事件”。

这样负责积分消费次数累加和负责语音播报的订阅者就会收到“支付成功事件”,进而做各自的业务逻辑。

画个简单的图描述一下:

image.png

/**
支付回调处理者
*/
PayCallBackController implements ApplicationContextAware {
     private ApplicationContext applicationContext;

    //如果想获取applicationContext需要实现ApplicationContextAware接口,Spring容器会回调setApplicationContext方法将applicationContext注入进来
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext = applicationContext;
    }

     @RequestMapping(value = "/pay/callback.do")
     public View callback(HttpServletRequest request){
        if(paySuccess(request){
            //构造支付成功事件
            PaySuccessEvent event = buildPaySuccessEvent(...);
            //通过applicationContext发布事件,从而达到通知观察者的目的
            this.applicationContext.publishEvent(event);
        } 
    }
}
/**
 * 语音播报处理者
 *
 */
public class VoiceBroadcastHandler implements ApplicationListener<PaySuccessEvent>{
    @Override
    public void onApplicationEvent(PaySuccessEvent event) {
        //语音播报逻辑
    }
}

//其他处理者的逻辑类似

总结:观察者模式将被观察者和观察者之间做了解耦,观察者存在与否不会影响被观察者的现有逻辑。

装饰器设计模式

▐ 模式定义

image.png

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

装饰器用来包装原有的类,在对使用者透明的情况下做功能的增强,比如 Java 中的 BufferedInputStream 可以对其包装的 InputStream 做增强,从而提供缓冲功能。

▐ 使用场景

在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。 当不能采用继承的方式对系统进行扩展或者继承。希望对原有类的功能做增强,但又不希望增加过多子类时,可以使用装饰器模式来达到同样的效果。

▐ 实践经验

有一个咖啡店,销售各种各样的咖啡,拿铁,卡布奇洛,蓝山咖啡等,在冲泡前,会询问顾客是否要加糖,加奶,加薄荷等。这样不同的咖啡配上不同的调料就会卖出不同的价格。

/**
* 抽象类Coffee
*/
public abstract class Coffee {
    /**
     * 获取咖啡得名字
     */
    public abstract String getName();

    /**
     * 获取咖啡的价格
     */
    public abstract double getPrice();
}

/**
*利用继承和组合的结合,现在我们可以考虑设计出一个装饰类,它也继承自coffee,
*并且它内部有一个coffee的实例对象
*/
public abstract class CoffeeDecorator implements Coffee {
    private Coffee delegate;

    public CoffeeDecorator(Coffee coffee) {
        this.delegate = coffee;
    }

    @Override
    public String getName() {
        return delegate.getName();
    }

    @Override
    public double getPrice() {
        return delegate.getPrice();
    }
}

/**
*牛奶咖啡的装饰者模式的案例
*/
public class MilkCoffeeDecorator extends CoffeeDecorator {
    public MilkCoffeeDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getName() {
        return "牛奶, " + super.getName();
    }

    @Override
    public double getPrice() {
        return 1.1 + super.getPrice();
    }
}

//其他咖啡的模式类似

/**
*测试案例 可以通过加入不用内容 咖啡名称和价格都是不同的
*/
public class App {
    public static void main(String[] args) {
        // 得到一杯原始的蓝山咖啡
        Coffee blueCoffee = new BlueCoffee();
        System.out.println(blueCoffee.getName() + ": " + blueCoffee.getPrice());

        // 加入牛奶
        blueCoffee = new MilkCoffeeDecorator(blueCoffee);
        System.out.println(blueCoffee.getName() + ": " + blueCoffee.getPrice());

        // 再加入薄荷
        blueCoffee = new MintCoffeeDecorator(blueCoffee);
        System.out.println(blueCoffee.getName() + ": " + blueCoffee.getPrice());

        // 再加入糖
        blueCoffee = new SugarCoffeeDecorator(blueCoffee);
        System.out.println(blueCoffee.getName() + ": " + blueCoffee.getPrice());
    }
}

总结:使用装饰器模式做了功能的增强,对使用者来说只需要做简单的组合就能继续使用原功能。装饰器模式充分展示了组合的灵活。利用它来实现扩展。它同时也是开闭原则的体现。如果相对某个类实现运行时功能动态的扩展。这个时候你就可以考虑使用装饰者模式!

桥接设计模式

▐ 模式定义

image.png

桥接模式是一种结构型设计模式, 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构, 从而能在开发时分别使用。

桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。

▐ 使用场景

如果一个系统需要在抽象类和具体类之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系,通过桥接模式可以使它们在抽象层建立一个关联关系。

抽象部分和实现部分可以以继承的方式独立扩展而互不影响,在程序运行时可以动态的将一个抽象类子类的对象和一个实现类子类的对象进行组合,及系统需要对抽象类角色和实现类角色进行动态耦合。

一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。

对于那些不希望使用继承或因为多层继承导致系统的个数急剧增加的系统,桥接模式尤为适用。

▐ 实践经验

性能管理系统中,数据产生后需要经过采集,汇聚,入库三个流程,用户才能查询使用。采集可以是snmp采集,也可以是ems采集;汇聚可以使storm汇聚,也可以是spark汇聚;入库可以是hdfs入库,也可以是mppdb入库。针对不同场景,我们可以灵活选择不同的采集,汇聚,入库方式。这种一个功能需要多种服务支持,每种服务又有不同类型的实现,使用桥接模式再适合不过。

桥接模式,顾名思义,就是把每种服务看做一座桥,我们可以根据实际场景选择不同的桥。上述例子表示数据产生到可以使用之前需要经过三座桥:采集桥->汇聚桥->入库桥。每座桥可以选择不同的构造。整理了一份562页设计模式PDF文档

image.png

/**
 * 
 * 采集桥采集服务
 *
 */
public abstract class CollectionService 
{
    abstract void execute();
    public void run()
{
        execute();
    }
}

/**
 *  汇聚桥  汇聚服务
 *
 */
public abstract class AggregationService 
{
    public void run()
{
        if(null != collectionService)
        {
            collectionService.run();
        }
        execute();
    }

    abstract void execute();
    CollectionService collectionService;
    public AggregationService(CollectionService collectionService)
{
        this.collectionService = collectionService;
    }
}

/**
 * 
 * 入库桥   入库服务
 *
 */
public abstract class StoreService 
{
    public void run()
{
        if(null != aggregationService)
        {
            aggregationService.run();
        }
        execute();
    }

    abstract void execute();
    AggregationService aggregationService;
    public StoreService(AggregationService aggregationService)
{
        this.aggregationService = aggregationService;
    }
}

/**
*
* EMS采集桥
*
*/

public class EMSCollectionService extends CollectionService
{
    @Override
    void execute() 
{
        System.out.println("EMS collection.");
    }
}

/**
*
* SNMP采集桥
*
*/
public class SNMPCollectionService extends CollectionService
{
    @Override
    void execute() 
{
        System.out.println("SNMP collection.");
    }
}

/**
*
*  Storm汇聚桥
*
*/
public class StormAggregationService extends AggregationService
{
    public StormAggregationService(CollectionService collectionService) 
{
        super(collectionService);
    }

    @Override
    void execute() 
{
        System.out.println("Storm aggregation.");
    }
}

/**
*
*  Spark汇聚桥
*
*/
public class SparkAggregationService extends AggregationService
{
    public SparkAggregationService(CollectionService collectionService) 
{
        super(collectionService);
    }

    @Override
    void execute() 
{
        System.out.println("Spark aggregation.");
    }
}

/**
*
*  MPPDB汇聚桥
*
*/
public class MPPDBStoreService extends StoreService
{
    public MPPDBStoreService(AggregationService aggregationService)
{
        super(aggregationService);
    }

    @Override
    void execute() 
{
        System.out.println("MPPDB store.");
    }
}

/**
*
*  HDFS汇聚桥
*
*/
public class HDFSStoreService extends StoreService
{
    public HDFSStoreService(AggregationService aggregationService) 
{
        super(aggregationService);
    }

    @Override
    void execute() 
{
        System.out.println("HDFS store.");
    }
}

/**
 *  
 * 类功能说明:   桥接模式测试
 */
public class BridgeTest 
{
    public static void main(String[] args)
{
        CollectionService snmpService = new SNMPCollectionService();
        AggregationService stormService = new StormAggregationService(snmpService);
        StoreService hdfsService = new HDFSStoreService(stormService);
        hdfsService.run();
    }
}

总结:桥接模式可以将系统中稳定的部分和可扩展的部分解耦,使得系统更加容易扩展,且满足OCP原则,对调用者修改关闭。

作者:阿里巴巴淘系技术官方
**
my.oschina.net/u/4662964/b…*

相关文章
|
24天前
|
存储 关系型数据库 MySQL
阿里面试:为什么要索引?什么是MySQL索引?底层结构是什么?
尼恩是一位资深架构师,他在自己的读者交流群中分享了关于MySQL索引的重要知识点。索引是帮助MySQL高效获取数据的数据结构,主要作用包括显著提升查询速度、降低磁盘I/O次数、优化排序与分组操作以及提升复杂查询的性能。MySQL支持多种索引类型,如主键索引、唯一索引、普通索引、全文索引和空间数据索引。索引的底层数据结构主要是B+树,它能够有效支持范围查询和顺序遍历,同时保持高效的插入、删除和查找性能。尼恩还强调了索引的优缺点,并提供了多个面试题及其解答,帮助读者在面试中脱颖而出。相关资料可在公众号【技术自由圈】获取。
|
4天前
|
SQL 关系型数据库 MySQL
阿里面试:1000万级大表, 如何 加索引?
45岁老架构师尼恩在其读者交流群中分享了如何在生产环境中给大表加索引的方法。文章详细介绍了两种索引构建方式:在线模式(Online DDL)和离线模式(Offline DDL),并深入探讨了 MySQL 5.6.7 之前的“影子策略”和 pt-online-schema-change 方案,以及 MySQL 5.6.7 之后的内部 Online DDL 特性。通过这些方法,可以有效地减少 DDL 操作对业务的影响,确保数据的一致性和完整性。尼恩还提供了大量面试题和解决方案,帮助读者在面试中充分展示技术实力。
|
1月前
|
消息中间件 存储 canal
阿里面试:canal+MQ,会有乱序的问题吗?
本文详细探讨了在阿里面试中常见的问题——“canal+MQ,会有乱序的问题吗?”以及如何保证RocketMQ消息有序。文章首先介绍了消息有序的基本概念,包括全局有序和局部有序,并分析了RocketMQ中实现消息有序的方法。接着,针对canal+MQ的场景,讨论了如何通过配置`canal.mq.partitionsNum`和`canal.mq.partitionHash`来保证数据同步的有序性。最后,提供了多个与MQ相关的面试题及解决方案,帮助读者更好地准备面试,提升技术水平。
阿里面试:canal+MQ,会有乱序的问题吗?
|
27天前
|
消息中间件 架构师 Java
阿里面试:秒杀的分布式事务, 是如何设计的?
在40岁老架构师尼恩的读者交流群中,近期有小伙伴在面试阿里、滴滴、极兔等一线互联网企业时,遇到了许多关于分布式事务的重要面试题。为了帮助大家更好地应对这些面试题,尼恩进行了系统化的梳理,详细介绍了Seata和RocketMQ事务消息的结合,以及如何实现强弱结合型事务。文章还提供了分布式事务的标准面试答案,并推荐了《尼恩Java面试宝典PDF》等资源,帮助大家在面试中脱颖而出。
|
29天前
|
设计模式 算法 数据库连接
PHP中的设计模式:提高代码的可维护性和扩展性
【10月更文挑战第13天】 本文将探讨PHP中常见的设计模式及其在实际项目中的应用。通过对比传统编程方式,我们将展示设计模式如何有效地提高代码的可维护性和扩展性。无论是单例模式确保类的单一实例,还是观察者模式实现对象间的松耦合,每一种设计模式都为开发者提供了解决特定问题的最佳实践。阅读本文后,读者将能更好地理解和应用这些设计模式,从而提升PHP编程的效率和质量。
|
30天前
|
SQL 关系型数据库 MySQL
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
尼恩,一位40岁的资深架构师,通过其丰富的经验和深厚的技術功底,为众多读者提供了宝贵的面试指导和技术分享。在他的读者交流群中,许多小伙伴获得了来自一线互联网企业的面试机会,并成功应对了诸如事务ACID特性实现、MVCC等相关面试题。尼恩特别整理了这些常见面试题的系统化解答,形成了《MVCC 学习圣经:一次穿透MYSQL MVCC》PDF文档,旨在帮助大家在面试中展示出扎实的技术功底,提高面试成功率。此外,他还编写了《尼恩Java面试宝典》等资料,涵盖了大量面试题和答案,帮助读者全面提升技术面试的表现。这些资料不仅内容详实,而且持续更新,是求职者备战技术面试的宝贵资源。
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
|
30天前
|
Kubernetes 架构师 算法
阿里面试:全国14亿人,统计出重名最多的前100个姓名
文章介绍了如何解决“从全国14亿人的数据中统计出重名人数最多的前100位姓名”的面试题,详细分析了多种数据结构的优缺点,最终推荐使用前缀树(Trie)+小顶堆的组合。文章还提供了具体的Java代码实现,并讨论了在内存受限情况下的解决方案,强调了TOP N问题的典型解题思路。最后,鼓励读者通过系统化学习《尼恩Java面试宝典》提升面试技巧。
阿里面试:全国14亿人,统计出重名最多的前100个姓名
|
1月前
|
存储 缓存 NoSQL
阿里面试题:缓存的一些常见的坑,你遇到过哪些,怎么解决的?
阿里面试题:缓存的一些常见的坑,你遇到过哪些,怎么解决的?
|
1月前
|
设计模式 缓存 Java
面试题:谈谈Spring用到了哪些设计模式?
面试题:谈谈Spring用到了哪些设计模式?
|
1月前
|
设计模式 SQL 安全
PHP中的设计模式:单例模式的深入探索与实践在PHP开发领域,设计模式是解决常见问题的高效方案集合。它们不是具体的代码,而是一种编码和设计经验的总结。单例模式作为设计模式中的一种,确保了一个类仅有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的基本概念、实现方式及其在PHP中的应用。
单例模式在PHP中的应用广泛,尤其在处理数据库连接、日志记录等场景时,能显著提高资源利用率和执行效率。本文从单例模式的定义出发,详细解释了其在PHP中的不同实现方法,并探讨了使用单例模式的优势与注意事项。通过对示例代码的分析,读者将能够理解如何在PHP项目中有效应用单例模式。