第二十七天

练习

 

N 字形变换

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:

P   A   H   N
A P L S I I G
Y   I   R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);
 

示例 1:

输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"
示例 2:
输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P     I    N
A   L S  I G
Y A   H R
P     I
示例 3:

输入:s = "A", numRows = 1
输出:"A"
 

提示:

1 <= s.length <= 1000
s 由英文字母(小写和大写)、',' 和 '.' 组成
1 <= numRows <= 1000
class Solution {
    public String convert(String s, int numRows) {
        if(numRows == 1)
            return s;

        int len = Math.min(s.length(), numRows);
        String []rows = new String[len];
        //为了初始化,做字符串拼接,如果不初始化就是undefined
        for(int i = 0; i< len; i++) rows[i] = "";
        int loc = 0;
        boolean down = false;

        for(int i=0;i<s.length();i++) {
            rows[loc] += s.substring(i,i+1);
            if(loc == 0 || loc == numRows - 1)
                down = !down;
            loc += down ? 1 : -1;
        }

        String ans = "";
        for(String row : rows) {
            ans += row;
        }
        return ans;
    }
}

自从开始写算法题,基本上没几个是自己做出来的

说说一开始我自己的想法,看到这题后想的是:这不是竖着来再横着读嘛,简单

简单个p,想了半小时才想出来怎么写,但也仅限于第一行,我就想着要不要整个二维数组,利用对角线一个一个的填上去,最后横着遍历。

想太多,人大神的代码有更好的方法,一个string就够了,根据numRows的数量定义string,这样的一维数组只需要遍历s记录到对应的位置就行,例如题解1给的例子,最终结果就是

string[0]=pahn string[1]=aplsiig string[2]=yir

然后让他们遍历拼接就行了,不用我想的那么复杂,反正又没说把字符串的顺序遍历出来。

在解决numRows来回增减问题,我想着是做一个判断,当它=0或者它=numRows-1时,变成递增或者递减,但是怎么实现的,我就不写了,头疼半天还有一些问题(因为我一开始想的是二位数组),然后大神代码就告诉我,判断可以做一个开关即可

所以

代码思路

1.判断s和numRows的长度(实际上就numRows就行,为了节省空间),选出最短的,初始化rows

2.初始化完成后,遍历s,做判断,当指针=0时,递增,指针=numRows-1时,递减。这里用的down做判断,当满足时取反,根据down的false和true取值,true时取1 false取-1 并给指针做递增递减法。通过rows[指针] +=s.substring(i,i+1)来拼接字符串

3.因为rows的字符串拼接,所以最终遍历时row会横向遍历rows,所以最终返回ans

八股

集群环境下保证id不重复

原来的Synchronized + Lock只能锁单机,也就是只能在一个JVM环境下

而在分布式+集群的环境下,变成了N对N的关系,在并发的环境下,如果使用UUID或者自增ID,就可能出现ID重复的问题,因此在集群下的环境下,对JVM进行加锁,这就是分布式锁。

Zookeeper实现的分布式锁

因为Zookeeper在创建节点的时候,需要保证节点的唯一性,也就是实现原理就是,每次一个线程获取到了锁,那就在Zookeeper上创建一个临时节点,但用完锁之后,在把这个节点删除掉,

创建锁

多个JVM服务器之间,同时在zookeeper上创建相同一个临时节点,因为临时节点路径是保证唯一,只要谁能创建节点成功,谁就能获取到锁。

没有创建成功节点,只能注册个监听器监听这个锁并进行等待,当释放锁的时候,采用事件通知给其它客户端重新获取锁的资源。

这时候客户端使用事件监听,如果该临时节点被删除的话,重新进入获取锁的步骤。

释放锁

Zookeeper使用直接关闭临时节点session会话连接,因为临时节点生命周期与session会话绑定在一块,如果session会话连接关闭,该临时节点也会被删除,这时候客户端使用事件监听,如果该临时节点被删除的话,重新进入到获取锁的步骤。

单机环境下的锁

对于单进程的并发场景,我们可以使用synchronized关键字和Reentrantlock等

当我们生成订单号时,假设建好了生成订单号的工具类及调用工具类的服务,当我们假设用户端循环50次生成订单,在获取线程的时候发现,会出现重复订单号,这时候就需要在service用锁来进行解决

    //lock 应该申明为类的成员变量
    private Lock lock = new ReentrantLock();
    public String getOrderNumber() {
        lock.lock();
        try {
            return orderNumberCreateUtil.getOrderNumber();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
       return null;
    }

分布式环境下的锁

对于 分布式场景,我们可以使用分布式锁。

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

意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构,即可重定义该算法的某些特定步骤:

主要解决:一些方法通用,却在每个子类都重新写了这一方法

何时使用:在一些通用的方法

如何解决:将这些通用算法抽象出来

关键代码:在抽象父类中实现通用方法,其它步骤下放到子类中实现

优缺点

  • 封装不变部分,扩展可变部分
  • 提取公共代码,便于维护
  • 行为由父类控制,子类实现
  • 缺点是:每一个不同的实现,都需要一个子类来实现,导致类的个数增加,使得系统变庞大

使用场景及实例

  • 有很多子类共有的方法,且逻辑相同
  • 重要的、复杂的方法,可以考虑模板方法
  • spring中对Hibernate的支持,将一些定好的方法封装起来,比如开启事务,获取Session,关闭Session,程序要不需要重复写那些已经规范好的代码,直接丢一个实体就可以保存。

用代码层次的方面来说

先定义一个锁接口,接口里面有上锁方法和未上锁方法

接着实现锁接口的模板,再定义两个抽象方法,一个是尝试锁,一个是等待锁,并且上锁方法会进行尝试锁,如果获取失败就转等待锁,接着重新调用上锁方法。

接着分布式锁继承上述模板,尝试锁是boolean的,用来判断节点释放存在,存在返回false,不存在返回true。等待锁需要加监控,用来检查lock节点释放

接着就可以通过ZkDistributedLock进行加锁,eg: lock(接口) lock1 = new ZkDistributedLock(继承模板的类)

lock1.lock(接口的上锁方法名)

然后在使用多个线程进行操作,而且是在线程里面实例化对象,来进行创建,最终保证每个对象再获取订单的时候,都是唯一的

mybatis_plus学习第一天

MyBatis-Plus (简称 MP )是一个 MyBatis 的增强工具 ,在 MyBatis 的基础上 只做增强不做改变 ,为
简化开发、提高效率而生

mybatis_plus特性

无侵入 :只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小 :启动即会自动注入基本 CURD ,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作 :内置通用 Mapper 、通用 Service ,仅仅通过少量配置即可实现单表大部分
CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持 Lambda 形式调用 :通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
支持主键自动生成 :支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence ),可自由
配置,完美解决主键问题
支持 ActiveRecord 模式 :支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强
大的 CRUD 操作
支持自定义全局通用操作 :支持全局通用方法注入( Write once, use anywhere
内置代码生成器 :采用代码或者 Maven 插件可快速生成 Mapper Model Service
Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
内置分页插件 :基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等
同于普通 List 查询
分页插件支持多种数据库 :支持 MySQL MariaDB Oracle DB2 H2 HSQL SQLite
Postgre SQLServer 等多种数据库
内置性能分析插件 :可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出
慢查询
内置全局拦截插件 :提供全表 delete update 操作智能分析阻断,也可自定义拦截规则,预防
误操作
任何能使用 MyBatis 进行 CRUD, 并且支持标准 SQL 的数据库,具体支持情况如下
MySQL Oracle DB2 H2 HSQL SQLite PostgreSQL SQLServer Phoenix Gauss
ClickHouse Sybase OceanBase Firebird Cubrid Goldilocks csiidb
达梦数据库,虚谷数据库,人大金仓数据库,南大通用 ( 华库 ) 数据库,南大通用数据库,神通数据
库,瀚高数据库
scan entity 扫描实体类,然后通过反射抽取当前表和实体类的关系,属性和字段,再提供通用方法生成对应的sql语句,再注入 mybatis容器
代码及文档地址
官方地址 : http://mp.baomidou.com
代码发布地址 :
Github: https://github.com/baomidou/mybatis-plus
Gitee: https://gitee.com/baomidou/mybatis-plus
文档发布地址 : https://baomidou.com/pages/24112f
创建数据库
CREATE DATABASE `mybatis_plus` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
use `mybatis_plus`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

简单使用

pom

    <dependencies>
        <!--   mybatis-plus启动器     -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <!-- lombok简化实体类开发       -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--    mysql驱动    -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
            <version>5.1.47</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

yml

MySQL5.7版本的url:
jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
MySQL8.0版本的url:
jdbc:mysql://localhost:3306/mybatis_plus?
serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false

spring:
# 配置数据源信息
  datasource:
# 配置数据源类型
    type: com.zaxxer.hikari.HikariDataSource
# 配置连接数据库信息
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

实体类

@Data
//@NoArgsConstructor
//@AllArgsConstructor
//@Getter
//@Setter
//@EqualsAndHashCode 一个data注解包括这五个

主启动

@MapperScan("com.atguigu.mybatisplus.mapper")
//或者自己创建一个config类 让springboot去自己扫描
//@Configuration
//@MapperScan({"com.atguigu.mybatisplus.alibaba.mapper"})

dao或者mapper

@Repository
//将一个类或接口标记成持久层组件-仓库
public interface UserMapper extends BaseMapper<User>  
//继承mybatisplus提供的sql映射  BaseMapper<实体类>

test

 @Autowired
    private UserMapper userMapper;


    @Test
    public void testSelectList(){
        //通过条件构造器查询一个list集合,若没有条件,则可以设置null为参数
        List<User> list = userMapper.selectList(null);
        list.forEach(System.out::println);
    }
//经测试发现 即使mapper或者dao文件中 仅仅是继承bashmapper就可以查询数据库语句 即使usermapper爆红也能查询 

日志功能

yml加入

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

basemapper

public interface BaseMapper<T> extends Mapper<T> {
/**
* 插入一条记录
* @param entity 实体对象
*/
int insert(T entity);
/**
* 根据 ID 删除
* @param id 主键ID
*/
int deleteById(Serializable id);
/**
* 根据实体(ID)删除
* @param entity 实体对象
* @since 3.4.4
*/
int deleteById(T entity);
/**
* 根据 columnMap 条件,删除记录
* @param columnMap 表字段 map 对象
*/
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* 根据 entity 条件,删除记录
* @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where
语句)
*/
int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 删除(根据ID 批量删除)
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends
Serializable> idList);
/**
* 根据 ID 修改
* @param entity 实体对象
*/
int updateById(@Param(Constants.ENTITY) T entity);
/**
* 根据 whereEntity 条件,更新记录
* @param entity 实体对象 (set 条件值,可以为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成
where 语句)
*/
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER)
Wrapper<T> updateWrapper);
/**
* 根据 ID 查询
* @param id 主键ID
*/
T selectById(Serializable id);
/**
* 查询(根据ID 批量查询)
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends
Serializable> idList);
/**
* 查询(根据 columnMap 条件)
* @param columnMap 表字段 map 对象
*/
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object>
columnMap);
/**
* 根据 entity 条件,查询一条记录
* <p>查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常
</p>
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {
List<T> ts = this.selectList(queryWrapper);
if (CollectionUtils.isNotEmpty(ts)) {
if (ts.size() != 1) {
throw ExceptionUtils.mpe("One record is expected, but the query
result is multiple records");
}
return ts.get(0);
}
return null;
}
/**
* 根据 Wrapper 条件,查询总记录数
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T>
queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
* <p>注意: 只返回第一个字段的值</p>
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录(并翻页)
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
<P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER)
Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录(并翻页)
* @param page 分页查询条件
* @param queryWrapper 实体对象封装操作类
*/
<P extends IPage<Map<String, Object>>> P selectMapsPage(P page,
@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

test

 @Test
    public void testInsert(){
        //实现新增用户信息
        //INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
        User user = new User();
        //user.setId(100L);
        user.setName("张三");
        user.setAge(23);
        user.setEmail("zhangsan@atguigu.com");
        int result = userMapper.insert(user);
        System.out.println("result:"+result);
        System.out.println("id:"+user.getId());
    }

    @Test
    public void testDelete(){
        //通过id删除用户信息
        //DELETE FROM user WHERE id=?
        /*int result = userMapper.deleteById(1492767055210991617L);
        System.out.println("result:"+result);*/
        //根据map集合中所设置的条件删除用户信息
        //DELETE FROM user WHERE name = ? AND age = ?
        /*Map<String, Object> map = new HashMap<>();
        map.put("name", "张三");
        map.put("age", 23);
        int result = userMapper.deleteByMap(map);
        System.out.println("result:"+result);*/
        //通过多个id实现批量删除
        //DELETE FROM user WHERE id IN ( ? , ? , ? )
        List<Long> list = Arrays.asList(1L, 2L, 3L);
        int result = userMapper.deleteBatchIds(list);
        System.out.println("result:"+result);
    }

    @Test
    public void testUpdate(){
        //修改用户信息
        //UPDATE user SET name=?, email=? WHERE id=?
        User user = new User();
        user.setId(4L);
        user.setName("李四");
        user.setEmail("lisi@atguigu.com");
        int result = userMapper.updateById(user);
        System.out.println("result:"+result);
    }

    @Test
    public void testSelect(){
        //通过id查询用户信息
        //SELECT id,name,age,email FROM user WHERE id=?
        /*User user = userMapper.selectById(1L);
        System.out.println(user);*/
        //根据多个id查询多个用户信息
        //SELECT id,name,age,email FROM user WHERE id IN ( ? , ? , ? )
        /*List<Long> list = Arrays.asList(1L, 2L, 3L);
        List<User> users = userMapper.selectBatchIds(list);
        users.forEach(System.out::println);*/
        //根据map集合中的条件查询用户信息
        //SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
        /*Map<String, Object> map = new HashMap<>();
        map.put("name", "Jack");
        map.put("age", 20);
        List<User> users = userMapper.selectByMap(map);
        users.forEach(System.out::println);*/
        //查询所有数据
        //SELECT id,name,age,email FROM user
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
        /*Map<String, Object> map = userMapper.selectMapById(1L);
        System.out.println(map);*/
    }

自定义sql

resources下创建mapper与java里面的dao或者mapper对应名字 自己写自己的

eg:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.mybatisplus.mapper.UserMapper">

    <!--Map<String, Object> selectMapById(Long id);-->
    <select id="selectMapById" resultType="map">
        select id,name,age,email from user where id = #{id}
    </select>

    <!--Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);-->
    <select id="selectPageVo" resultType="User">
        select uid,user_name,age,email from t_user where age > #{age}
    </select>

</mapper>

另外 yml文件里面默认会去找resources下的mapper

想自己改就自己添加下面的属性修改

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: xxx

通用service

说明 :
通用 Service CRUD 封装 IService 接口,进一步封装 CRUD 采用 get 查询单行 remove
list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,
泛型 T 为任意实体对象
建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承
Mybatis - Plus 提供的基类
官网地址: https://baomidou.com/pages/49cc81/#service-crud-%E6%8E%A5%E5%8F%
A3
Service
MyBatis-Plus 中有一个接口 IService 和其实现类 ServiceImpl ,封装了常见的业务层逻辑
详情查看源码 IService ServiceImpl
service
public interface UserService extends IService<User> {
}

service/impl
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

test 老师只演示两个 剩下的自己翻源码

@Autowired
    private UserService userService;

    @Test
    public void testGetCount(){
        //查询总记录数
        //SELECT COUNT( * ) FROM user
        long count = userService.count();
        System.out.println("总记录数:"+count);
    }

    @Test
    public void testInsertMore(){
        //批量添加
        //INSERT INTO user ( id, name, age ) VALUES ( ?, ?, ? )
        List<User> list = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            User user = new User();
            user.setName("ybc"+i);
            user.setAge(20+i);
            list.add(user);
        }
        boolean b = userService.saveBatch(list);
        System.out.println(b);
    }

常用注解

解决数据库表与实体类表名不一致@TableName

在实体类前面添加注解
//设置实体类所对应的表名
@TableName("t_user")
或者 在yml全局配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
 #设置myBatis-plus全局配置
  global-config:
    db-config:
      #设置实体类表的统一前缀
      table-prefix: t_ 
解决实体类和表中表示主键的不是 id,而是其他字段 @TableId
在实体类中 uid 属性上通过 @TableId 将其标识为主键,即可成功执行 SQL 语句
/将属性所对应的字段指定为主键
//@TableId注解的value属性用于指定主键的字段
//@TableId注解的type属性设置主键生成策略
//mybatis-plus默认雪花算法 如果我想用mysql自动递增呢
//@TableId(value = "uid", type = IdType.AUTO)
@TableId("uid")
private Long id;

如果不想使用默认的雪花算法 不仅上面改 在mysql也要改自增

IdType.ASSIGN_ID (默认)
基于雪花算法的策略生成数据 id ,与数据库 id 是否设置自增无关
IdType.AUTO
使用数据库的自增策略,注意,该类型请确保数据库设置了 id 自增,
否则无效
如果手动设置id了 是不会进行任何策略的 
yml设置全局配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  #设置myBatis-plus全局配置
  global-config:
    db-config:
      #设置实体类表的统一前缀
      table-prefix: t_
      #配置MyBatis-Plus的主键策略
      id-type: auto

 

雪花算法

背景
需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量。
数据库的扩展方式主要包括:业务分库、主从复制,数据库分表。
数据库分表
将不同业务数据分散存储到不同的数据库服务器,能够支撑百万甚至千万用户规模的业务,但如果业务
继续发展,同一业务的单表数据也会达到单台数据库服务器的处理瓶颈。例如,淘宝的几亿用户数据,
如果全部存放在一台数据库服务器的一张表中,肯定是无法满足性能要求的,此时就需要对单表数据进
行拆分。
单表数据拆分有两种方式:垂直分表和水平分表

 

垂直分表
垂直分表适合将表中某些不常用且占了大量空间的列拆分出去。
例如,前面示意图中的 nickname description 字段,假设我们是一个婚恋网站,用户在筛选其他用
户的时候,主要是用 age sex 两个字段进行查询,而 nickname description 两个字段主要用于展
示,一般不会在业务查询中用到。 description 本身又比较长,因此我们可以将这两个字段独立到另外
一张表中,这样在查询 age sex 时,就能带来一定的性能提升。
水平分表
水平分表适合表行数特别大的表,有的公司要求单表行数超过 5000 万就必须进行分表,这个数字可以
作为参考,但并不是绝对标准,关键还是要看表的访问性能。对于一些比较复杂的表,可能超过 1000
万就要分表了;而对于一些简单的表,即使存储数据超过 1 亿行,也可以不分表。
但不管怎样,当看到表的数据量达到千万级别时,作为架构师就要警觉起来,因为这很可能是架构的性
能瓶颈或者隐患。
水平分表相比垂直分表,会引入更多的复杂性,例如要求全局唯一的数据 id 该如何处理
主键自增
①以最常见的用户 ID 为例,可以按照 1000000 的范围大小进行分段, 1 ~ 999999 放到表 1 中,
1000000 ~ 1999999 放到表 2 中,以此类推。
②复杂点:分段大小的选取。分段太小会导致切分后子表数量过多,增加维护复杂度;分段太大可能会
导致单表依然存在性能问题,一般建议分段大小在 100 万至 2000 万之间,具体需要根据业务选取合适
的分段大小。
③优点:可以随着数据的增加平滑地扩充新的表。例如,现在的用户是 100 万,如果增加到 1000 万,
只需要增加新的表就可以了,原有的数据不需要动。
④缺点:分布不均匀。假如按照 1000 万来进行分表,有可能某个分段实际存储的数据量只有 1 条,而
另外一个分段实际存储的数据量有 1000 万条。
取模
①同样以用户 ID 为例,假如我们一开始就规划了 10 个数据库表,可以简单地用 user_id % 10 的值来
表示数据所属的数据库表编号, ID 985 的用户放到编号为 5 的子表中, ID 10086 的用户放到编号
6 的子表中。
②复杂点:初始表数量的确定。表数量太多维护比较麻烦,表数量太少又可能导致单表性能存在问题。
③优点:表分布比较均匀。
④缺点:扩充新的表很麻烦,所有数据都要重分布。
雪花算法
雪花算法是由 Twitter 公布的分布式主键生成算法,它能够保证不同表的主键的不重复性,以及相同表的
主键的有序性。
①核心思想:
长度共 64bit (一个 long 型)。
首先是一个符号位, 1bit 标识,由于 long 基本类型在 Java 中是带符号的,最高位是符号位,正数是 0 ,负
数是 1 ,所以 id 一般是正数,最高位是 0
41bit 时间截 ( 毫秒级 ) ,存储的是时间截的差值(当前时间截 - 开始时间截 ) ,结果约等于 69.73 年。
10bit 作为机器的 ID
5 bit 是数据中心, 5 bit 的机器 ID ,可以部署在 1024 个节点)。
12bit 作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 ID

 ②优点:整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞,并且效率较高。

如果实体类中的属性名和字段名不一致的情况 @TableField
若实体类中的属性使用的是驼峰命名风格,而表中的字段使用的是下划线命名风格
例如实体类属性userName,表中字段user_name
此时MyBatis-Plus会自动将下划线命名风格转化为驼峰命名风格
相当于在MyBatis中配置

若实体类中的属性和表中的字段不满足情况1
例如实体类属性name,表中字段username
此时需要在实体类属性上使用@TableField("username")设置属性所对应的字段名
//指定属性所对应的字段名
@TableField("user_name")

@TableLogic
物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库
中仍旧能看到此条数据记录
使用场景:可以进行数据恢复

 

step3:测试
测试删除功能,真正执行的是修改
UPDATE t_user SET is_deleted=1 WHERE id=? AND is_deleted=0
测试查询功能,被逻辑删除的数据默认不会被查询
SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0

总结

一天学一半确实不算多,毕竟全套课也才6小时,有一说一,确实mybatis都忘得差不多了,学者mybatisplus就想着如果要操作复杂的sql是不是得用mybatis,现在就想着重写回顾一下mybatis了,真的是不用就忘,明天再看看,反正也没上班,想学啥学啥,大不了花几天再回顾一波mybatis。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值