ARTS_WEEK11

二分查找

Posted by CDz on February 16, 2020

这周开始有工作了,所以开始学习时间上有所挤压,内容上主要还是围绕专栏与视频课展开。最主要的还是二分查找的算法练习。在写的过程中,发现二分查找思想很简单,但是真正写出一个没有BUG的程序却并不容易(推荐一篇LeetCode上讲解二分查找的文章写得很详细)。

  • 专栏
    • 数据结构与算法
      • 二分查找
      • 调表
    • Java并发实战
    • MySQL实战
    • 架构课
  • 视频
    • 多线程核心
  • LeetCode
    • 二分查找

1. although

二分查找,要写出正确的程序,这一周经验总结,确定边界范围、停止条件画图理清思路

1.1. 二分查找基础

特别注意: 终止条件、区间上下界更新方法、返回值选择。

1.1.1. 循环求解二分查找

/**
     * 循环求解二分查找
     * @param arr 查找数组
     * @param n 数组长度
     * @param val 查找元素
     * @return
     */
    public int bsearch(int[] arr,int n,int val){
        int low = 0;
        int high = n-1;
        //返回下标
        int index = -1;
        while (low<=high){
            //int mid = low+(high-low)/2;
            //更有效率写法
            //注意:位运算的优先级最低
            int mid = low+((high-low)>>2);
            if (val>arr[mid]){
                low = mid+1;
            }else if (val<arr[mid]){
                high = mid-1;
            }else if (val==arr[mid]){
                index = mid;
                break;
            }
        }
        return index;
    }

1.1.2. 使用递归

/**
     * 使用递归
     * @param arr 查找数组
     * @param n 数组长度
     * @param val 查找元素
     * @return
     */
    public int bsearch2(int[] arr,int n,int val){
        return bsearch(arr,0,n-1,val);
    }

    private int bsearch(int[] arr, int low, int high, int val) {

        if (low>high)return -1;

        int mid = low + ((high-low)>>2);

        if (val>arr[mid]){
            low = mid+1;
        }else if (val<arr[mid]){
            high = mid -1;
        }else if (val==arr[mid]){
            return mid;
        }
        return bsearch(arr,low,high,val);
    }

1.1.3. 查找第一个值等于给定值的元素

关键在相等的时候, 如果相等 判断是否是第一个

	//查找第一个值等于给定值的元素
    public int bsearch3(int[] arr,int n,int val){
        int low = 0;
        int high = n-1;
        //返回下标
        int index = -1;
        while (low<=high){
            //int mid = low+(high-low)/2;
            //更有效率写法
            //注意:位运算的优先级最低
            int mid = low+((high-low)>>2);
            if (val>arr[mid]){
                low = mid+1;
            }else if (val<arr[mid]){
                high = mid-1;
            }else if (val==arr[mid]){
                //关键在相等的时候
                //如果相等 判断是否是第一个
                if (mid==0||arr[mid-1]!=val){
                    index = mid;
                    break;
                }
                high = mid -1;
            }
        }
        return index;
    }

1.1.4. 查找最后一个值等于给定值的元素

注意:位运算的优先级最低

//查找最后一个值等于给定值的元素
    public int bsearch4(int[] arr,int n,int val){
        int low = 0;
        int high = n-1;
        //返回下标
        int index = -1;
        while (low<=high){
            //int mid = low+(high-low)/2;
            //更有效率写法
            //注意:位运算的优先级最低
            int mid = low+((high-low)>>2);
            if (val>arr[mid]){
                low = mid+1;
            }else if (val<arr[mid]){
                high = mid-1;
            }else if (val==arr[mid]){
                //关键在相等的时候
                //如果相等 判断是否是最后一个
                if (mid==arr.length-1||arr[mid+1]!=val){
                    index = mid;
                    break;
                }
                low = mid +1;
            }
        }
        return index;
    }

1.1.5. 查找第一个大于等于给定值的元素

	//查找第一个大于等于给定值的元素
    public int bsearch5(int[] arr,int n,int val){
        int low = 0;
        int high = n-1;
        //返回下标
        int index = -1;
        while (low<=high){
            //int mid = low+(high-low)/2;
            //更有效率写法
            //注意:位运算的优先级最低
            int mid = low+((high-low)>>2);
            if (val>arr[mid]){
                low = mid+1;
            }else if (val<=arr[mid]){
                if (mid==0||arr[mid-1]<val){
                    return mid;
                }
                high = mid-1;
            }
        }
        return index;
    }

1.1.6. 查找最后一个小于等于给定值的元素

	//查找最后一个小于等于给定值的元素
    public int bsearch6(int[] arr,int n,int val){
        int low = 0;
        int high = n-1;
        //返回下标
        int index = -1;
        while (low<=high){
            //int mid = low+(high-low)/2;
            //更有效率写法
            //注意:位运算的优先级最低
            int mid = low+((high-low)>>2);
            if (val>=arr[mid]){
                if (mid==arr.length-1||arr[mid+1]>val){
                    return mid;
                }
                low = mid+1;
            }else if (val<arr[mid]){

                high = mid-1;
            }
        }
        return index;
    }

1.2. LeetCode

LeetCode上的通过率也可以看出,就算是简单的题目,也是基本上很多题目都在50%以下,说明二分查找思想简单,但是实现起来有很多需要注意的地方,几次将人写崩溃。

总结:注意二分切割区间

1.2.1. 第一个错误的版本

package com.myserious.code.cdz.week20;

/**
 * 278. 第一个错误的版本
 * 你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
 * <p>
 * 假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。
 * <p>
 * 你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
 * <p>
 * 示例:
 * <p>
 * 给定 n = 5,并且 version = 4 是第一个错误的版本。
 * <p>
 * 调用 isBadVersion(3) -> false
 * 调用 isBadVersion(5) -> true
 * 调用 isBadVersion(4) -> true
 * <p>
 * 所以,4 是第一个错误的版本。 
 * <p>
 * 来源:力扣(LeetCode)
 * 链接:https://leetcode-cn.com/problems/first-bad-version
 * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
 */
public class Week11EFirstBadVersion278 extends VersionControl {
    public int firstBadVersion(int n) {

        int low = 1;
        int high = n;

        while (high > low) {
            int mid = low + ((high - low) >> 2);
            if (isBadVersion(mid)) {
                //true 说明在 左边
                high = mid;
            } else {
                low = mid+1;
            }
        }

        return low;

    }
}

class VersionControl{
    boolean isBadVersion(int version){
        return false;
    }
}

1.2.2. x 的平方根

一个>2的数的平方根一定在2~n/2之间。

package com.myserious.code.cdz.week20;

/**
 * 69. x 的平方根
 * 实现 int sqrt(int x) 函数。
 * <p>
 * 计算并返回 x 的平方根,其中 x 是非负整数。
 * <p>
 * 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
 * <p>
 * 示例 1:
 * <p>
 * 输入: 4
 * 输出: 2
 * 示例 2:
 * <p>
 * 输入: 8
 * 输出: 2
 * 说明: 8 的平方根是 2.82842...,
 *      由于返回类型是整数,小数部分将被舍去。
 * <p>
 * 来源:力扣(LeetCode)
 * 链接:https://leetcode-cn.com/problems/sqrtx
 * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
 *
 *
 * 进阶:如何编程实现“求一个数的平方根”?要求精确到小数点后 6 位。
 */
public class Week11EMySqrt69 {

    /**
     * 一个数的平方根
     *
     * 在 0~x/2 之间
     * @param x
     * @return
     */
    public int mySqrt(int x) {
        if (x<2){
            return x;
        }
        long low = 2;
        long high = x;
        while (high>=low){
            long mid = low + ((high-low)>>2);
            long q = mid*mid;
            if (q==x){
                return (int) mid;
            }else if (q>x){
                high = mid-1;
            }else {
                low = mid + 1;
            }
        }
        return (int) high;
    }
}

1.2.3. 寻找比目标字母大的最小字母

主要当需要查找的元素大于数组的最大元素时,取第一个。

package com.myserious.code.cdz.week20;

/**
 * 744. 寻找比目标字母大的最小字母
 * <p>
 * 给定一个只包含小写字母的有序数组letters 和一个目标字母 target,寻找有序数组里面比目标字母大的最小字母。
 * <p>
 * 数组里字母的顺序是循环的。举个例子,如果目标字母target = 'z' 并且有序数组为 letters = ['a', 'b'],则答案返回 'a'。
 * <p>
 * 示例:
 * <p>
 * 输入:
 * letters = ["c", "f", "j"]
 * target = "a"
 * 输出: "c"
 * <p>
 * 输入:
 * letters = ["c", "f", "j"]
 * target = "c"
 * 输出: "f"
 * <p>
 * 输入:
 * letters = ["c", "f", "j"]
 * target = "d"
 * 输出: "f"
 * <p>
 * 输入:
 * letters = ["c", "f", "j"]
 * target = "g"
 * 输出: "j"
 * <p>
 * 输入:
 * letters = ["c", "f", "j"]
 * target = "j"
 * 输出: "c"
 * <p>
 * 输入:
 * letters = ["c", "f", "j"]
 * target = "k"
 * 输出: "c"
 * 注:
 * <p>
 * letters长度范围在[2, 10000]区间内。
 * letters 仅由小写字母组成,最少包含两个不同的字母。
 * 目标字母target 是一个小写字母。
 * <p>
 * 来源:力扣(LeetCode)
 * 链接:https://leetcode-cn.com/problems/find-smallest-letter-greater-than-target
 * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
 */
public class Week11ENextGreatestLetter744 {
    public char nextGreatestLetter(char[] letters, char target) {

        //分割的区间 是左闭右开 [0,n)
        // 切割方式 [0,mid) [mid+1,n)
        //原因:这里需要查找的是比target第一大的字母,而不是找其本身
        int low = 0;
        int high = letters.length;

        while (high>low){
            int mid = low + ((high-low)>>2);

            if (letters[mid]<=target){
                low = mid+1;
            }else {
                high = mid;
            }
        }
        return letters[high%letters.length];
    }


    public static void main(String[] args) {
        Week11ENextGreatestLetter744 letter = new Week11ENextGreatestLetter744();
        char[] arr = new char[]{'c','f','j'};
        System.out.println(letter.nextGreatestLetter(arr, 'j'));
    }
}

1.2.4. 寻找旋转排序数组中的最小值

一个有序数组旋转之后,使用二分法,切分成两个数组,左边与右边,其中必定有一边为有序另一边为无序

package com.myserious.code.cdz.week20;

/**
 * 153. 寻找旋转排序数组中的最小值
 * <p>
 * 假设按照升序排序的数组在预先未知的某个点上进行了旋转。
 * <p>
 * ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
 * <p>
 * 请找出其中最小的元素。
 * <p>
 * 你可以假设数组中不存在重复元素。
 * <p>
 * 示例 1:
 * <p>
 * 输入: [3,4,5,1,2]
 * 输出: 1
 * 示例 2:
 * <p>
 * 输入: [4,5,6,7,0,1,2]
 * 输出: 0
 * <p>
 * 来源:力扣(LeetCode)
 * 链接:https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array
 * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
 */
public class Week11MFindMin153 {

    public int findMin(int[] nums) {

        if (nums.length==1){
            return nums[0];
        }
        int low = 0;
        int high = nums.length-1;

        while (high>low){
            int mid = low + ((high-low)>>2);
            // [low,mid] [mid+1,high]

            //判断两边有序度
            if (nums[mid]-nums[low]>=0&&nums[high]-nums[mid+1]>=0){
                //两边都有序
                //比较两边 最小值
                if (nums[low]>nums[mid+1]){
                    return nums[mid +1];
                }else {
                    return nums[low];
                }
            }else if (nums[mid]-nums[low]<0){
                //左边无序
                high = mid;
            }else {
                low = mid +1;
            }
        }

        return low;
    }

    public static void main(String[] args) {
        Week11MFindMin153 findMin = new Week11MFindMin153();

        int[] arr = new int[]{2,3,1};
        System.out.println(findMin.findMin(arr));
    }
}

1.2.5. 搜索旋转排序数组

package com.myserious.code.cdz.week20;

/**
 * 33. 搜索旋转排序数组
 * 假设按照升序排序的数组在预先未知的某个点上进行了旋转。
 * <p>
 * ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
 * <p>
 * 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
 * <p>
 * 你可以假设数组中不存在重复的元素。
 * <p>
 * 你的算法时间复杂度必须是 O(log n) 级别。
 * <p>
 * 示例 1:
 * <p>
 * 输入: nums = [4,5,6,7,0,1,2], target = 0
 * 输出: 4
 * 示例 2:
 * <p>
 * 输入: nums = [4,5,6,7,0,1,2], target = 3
 * 输出: -1
 * <p>
 * 来源:力扣(LeetCode)
 * 链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array
 * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
 */
public class Week11MSearch33 {

    /**
     * 重点理解
     * 折叠的有序数组
     * 其实二分法 找到中间位置
     * 如果左边有序 那么右边一定无序,反之
     *
     * @param nums
     * @param target
     * @return
     */
    public int search(int[] nums, int target) {
        int low = 0;
        int high = nums.length - 1;

        while (high >= low) {
            int mid = low + ((high - low) >> 2);
            //判断那边有序
            if (target == nums[mid]) {
                return mid;

            }else if (nums[mid] >= nums[low]) {
                //这里只能切割 [low,mid] (mid,high] 这样的区间(当只有两个元素时并且 两个元素为倒序情况,只有这样才能满足)

                //左边有序
                //判断 target 是否在 左边区间内
                if (target >= nums[low] && target < nums[mid]) {
                    // 在左边区间内
                    high = mid - 1;
                } else {
                    //右边区间内
                    low = mid + 1;
                }
            } else {
                //右边有序
                if (target > nums[mid] && target <= nums[high]) {
                    //在右边区间内
                    low = mid + 1;
                } else {
                    high = mid - 1;
                }
            }
        }
        return -1;
    }
    public static void main(String[] args) {
        Week11MSearch33 search = new Week11MSearch33();
        int[] ints = {3, 1};
        System.out.println(search.search(ints, 1));

    }
}

1.2.6. 在排序数组中查找元素的第一个和最后一个位置

  • 思路找到第一个 target相等位置
  • 然后一直加加直到不相等
package com.myserious.code.cdz.week20;

import java.util.Arrays;

/**
 * 34. 在排序数组中查找元素的第一个和最后一个位置
 * 给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
 * <p>
 * 你的算法时间复杂度必须是 O(log n) 级别。
 * <p>
 * 如果数组中不存在目标值,返回 [-1, -1]。
 * <p>
 * 示例 1:
 * <p>
 * 输入: nums = [5,7,7,8,8,10], target = 8
 * 输出: [3,4]
 * 示例 2:
 * <p>
 * 输入: nums = [5,7,7,8,8,10], target = 6
 * 输出: [-1,-1]
 * <p>
 * 来源:力扣(LeetCode)
 * 链接:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array
 * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
 */
public class Week11MSearchRange34 {
    public int[] searchRange(int[] nums, int target) {

        int firstInx = -1;
        int lastInx = -1;

        int low = 0;
        int high = nums.length-1;

        /**
         * 思路找到第一个 target相等位置
         * 然后一直加加直到不相等
         */
        while (high>=low){
            int mid = low + ((high-low)/2);
            if (nums[mid]>target){
                high = mid -1;
            }else if (nums[mid]<target){
                low = mid +1;
            }else {
                if (mid==0||nums[mid-1]!=target){
                    //找到了
                    firstInx = mid;
                    lastInx = mid;
                    while (nums.length>lastInx+1&&nums[lastInx+1]==target){
                        lastInx++;
                    }
                    break;
                }else {
                    high = mid -1;
                }
            }
        }
        return new int[]{firstInx,lastInx};
    }

    public static void main(String[] args) {
        Week11MSearchRange34 searchRange = new Week11MSearchRange34();
        int[] ints = {5, 7, 7, 8, 8, 10};
        int[] ints1 = searchRange.searchRange(ints, 3);
        System.out.println(Arrays.toString(ints1));
    }
}

1.2.7. 有序数组中的单一元素

这一题的注意点是,切割两数组时,分为两种情况,查找的元素相同两元素的第一个或者第二个

  • 第二个
    • b为true时表示的是右边为偶数个
  • 第一个
    • b为true时 表示的是右边为奇数个
package com.myserious.code.cdz.week20;

/**
 * 540. 有序数组中的单一元素
 * 给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
 * <p>
 * 示例 1:
 * <p>
 * 输入: [1,1,2,3,3,4,4,8,8]
 * 输出: 2
 * 示例 2:
 * <p>
 * 输入: [3,3,7,7,10,11,11]
 * 输出: 10
 * 注意: 您的方案应该在 O(log n)时间复杂度和 O(1)空间复杂度中运行。
 * <p>
 * 来源:力扣(LeetCode)
 * 链接:https://leetcode-cn.com/problems/single-element-in-a-sorted-array
 * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
 */
public class Week11MSingleNonDuplicate540 {
    public int singleNonDuplicate(int[] nums) {

        //因为其他元素都是成对出现
        //只有一个元素是单个的
        //所以 数组必定是奇数个
        //二分法,排除中间的两个,
        //再判断 左右两边,那边是奇数个,继续找奇数个的

        // [3,3,7,7 ,10,11,11] mid=3
        // [1,3,3,4,4] mid=3
        // 并且 中间的数字必须等于后一个数字,奇数个数组的特性
        // [low,mid-1] [mid+2,high]
        int low = 0;
        int high = nums.length-1;

        while (low<high){
            int mid = low + ((high-low)/2);
            //关键在这里
            //当 nums[mid]==nums[mid+1] 与 nums[mid-1]==nums[mid]
            // 他们表示的含义是不一样的
            //当nums[mid]==nums[mid+1] 标点mid   b为true时表示的是右边为偶数个
            //当nums[mid-1]==nums[mid] 标点mid-1 b为true时 表示的是右边为奇数个
            //这是因为标点变了
            boolean b = (high - mid+1) % 2 == 0;

            if (nums[mid]==nums[mid+1]){
                if (b){
                    //在左边
                    high = mid -1;
                }else {
                    //在右边
                    low = mid +2;
                }
            }else if (nums[mid-1]==nums[mid]){
                if (b){
                    //在右边
                    low = mid +1;
                }else {
                    //在左边
                    high = mid - 2;
                }
            }else {
                return nums[mid];
            }
        }

        return nums[low];
    }

    public int singleNonDuplicate1(int[] nums) {
        int lo = 0;
        int hi = nums.length - 1;
        while (lo < hi) {
            int mid = lo + (hi - lo) / 2;
            boolean halvesAreEven = (hi - mid) % 2 == 0;
            if (nums[mid + 1] == nums[mid]) {
                if (halvesAreEven) {
                    lo = mid + 2;
                } else {
                    hi = mid - 1;
                }
            } else if (nums[mid - 1] == nums[mid]) {
                if (halvesAreEven) {
                    hi = mid - 2;
                } else {
                    lo = mid + 1;
                }
            } else {
                return nums[mid];
            }
        }
        return nums[lo];
    }

    public static void main(String[] args) {
        Week11MSingleNonDuplicate540 duplicate = new Week11MSingleNonDuplicate540();
        int[] ints =    new int[]{3,7,7} ;
        System.out.println(duplicate.singleNonDuplicate(ints));
    }
}

2. review

俄国小哥,React核心开发人员的十年历程

  • 积极参与开源项目
    • 也是这个小哥的转折点
  • 英文很重要
  • 实战才能有真提升
  • 演讲能力
    • 预备演讲
      • Like most important things in my life, it happened randomly.

3. tip

3.1. 数据结构与算法

3.1.1. 二分查找适用场景

  • 数组(可随机访问)
  • 插入、删除操作不频繁(一次排序,多次查找)
  • 有序
  • 数组容量不易过小
    • 例外比较操作耗时时,二分查找比较次数最低
  • 数据量过大也不适合
    • 因为数组需要一篇连续的内存在存储,多大的话可能内存不够。

      3.1.2. 二分查找程序注意

      注意二分切割区间

3.2. 多线程

3.2.1. RPC其实是异步

RPC其实是异步的,RPC框架通过编程的方式,将异步转化为了同步,方便代码编写,如dubbo

3.2.2. 生产中什么场景下会发生死锁?

  • 同一个方法中获取多个锁
  • 一个方法中获取一个锁,但是调用的其他方法也会获取其他锁,导致循环锁

    3.2.3. ReadWriteLock读写锁

    特点:

  • 读可以重入
  • 写时互斥 使用注意事项:
  • 锁不能升级
    • 读锁内部 不能使用写锁
  • 锁可以降级
    • 写锁内部可以使用读锁

https://time.geekbang.org/column/article/88909

4. share

4.1. 数据结构与算法

4.1.1. 跳表的结构

4.2. 多线程

4.2.1. 创建多少线程才合适?

CPU密集型:线程数=CPU核心数(逻辑核心数)+1 I/O密集型:线程数=CPU核心数*(1+I/O耗时/CPU耗时)

实际开发中最难确定的是I/O耗时/CPU耗时这个参数,

一般使用apm压测的方式,来主要观察I/O耗时与CPU耗时。

部署分容器与物理机,

容器部署一般一个容器中只有一个Java程序在跑,可以使用Runtime.getRuntime().availableProcessors()来获取容器中的线程数,新版本Java才支持

而在机器上跑,很多时候并不只有一个Java程序,所以还需要根据实际的情况从理论值开始调节。

4.2.2. 活锁是什么?

  • 一直尝试获取锁,却一直获取不到,一直进行循环执行
  • 效果与死锁一致,但是危害比死锁要大,因为一直循环占用CPU资源
  • 结果和活锁一样,不同的是,活锁没有堵塞,顾名思义,一直在运行,但是没有执行有效代码。

这里为什么会出现活锁? 可能导致活锁 ,并不一定会活锁。 原因:双方同时为对方转账时,同时进入第一个锁,但是发现获取不到第二个锁,于是释放,进行下一次循环。 下一次循环同样的结果,这就是出现了活锁。

解决办法:加入随机因素

消息队列中,活锁的出现:

  • 依赖服务出现问题(宕机),消息消费失败
  • 导致消息队列一直重试发送消息

解决办法:

  • 放到队列尾部
  • 重试限制

4.2.3. 饥饿是什么意思?

线程等待得不到资源执行。

饥饿的一个场景: 浏览器的设计,也有后台程序运行,与前台程序运行。 后台程序部分:下载文件 前台显示部分:浏览网页,打开设置

如果后台程序沾满了CPU,那么前台程序就得不到运行,点击就会导致无响应的情况。

4.3. 架构

4.3.1. MySQL主从读写分离解决方案

数据库读写分离一般应用于什么场景?能支撑多大的业务规模?

在单机MySQL在,缓存和索引优化后,已经支撑不了的情况下再进行读写分离。 一般有些慢的情况,其实优化慢查询,和添加缓存机制,基本上都可以解决。

读写分离最大的问题:

主从复制延迟,有时候会延迟1秒,有时候可能会延迟1分钟。

解决办法:

  1. 写操作后,读操作指定发送给主库(不可取,容易出BUG)
  2. 两次读取,当读取不到内容,去主库读取
    • 缺点:可能黑客暴力破解账户,导致主库承受巨大压力
  3. 关键业务操作指向主机
    • 比如用户注册业务,所有操作都在主库中完成,这样就不会有延迟
    • 而用户修改个人资料这样的信息,可以使用读写分离
    • 这也就是很多修改完了资料信息,而没有立马展示的原因

读写分离实现:

  1. 代码层面(代码程序封装)
    • 实现简单,根据业务制定化
    • 每个编程语言都要实现一遍,无法通用
    • 故障下,切换主从,需要修改代码配置,重启服务
    • 开源实现淘宝的:DTTL
  2. 中间件封装
    • 支持多语言,标准的SQL入口
    • 实现复杂度高,长时间才能稳定
    • 所有请求都在中间件处理,并实现读写分离,性能要求高
    • 主从切换无感知
    • 可选:
      • MySQL Proxy一直没有正式 GA(稳定版本)
      • 现在 MySQL 官方推荐 MySQL Router
      • 360开源Atlas,基于MySQL Proxy

        4.4. 疫情下做饭感悟