ShardingSphere 数据分片之 Sharding-JDBC 深入理解
更多内容,前往 IT-BLOG
MySQL 的存储单位是 page[16kb],索引使用 B+Tree,深度为3(3次 IO便能查出数据)。为了提高查询速度,存储单元中都存储的是索引的指针。MySQL 内部索引指针大小在 InnoDB源码中设置为 6个字节+例如 ID类型 bigint(long) 占8个字节 = 14字节,那么一页存储 16*1024/14 ≈ 1170行数据。因为深度为3,表示此时一张表最多存储(这里假设叶子节点一行记录的数据大小为1k,实际上现在很多互联网业务数据记录大小通常就是1K左右)16[叶子节点只能存16行数据] * 1170 * 1170 = 21902400 行数据。如果再添加数据就会导致深度增加,深度增加就会导致查询效率下降,所以单表只能放这么多数据。通常,单个数据库中的数据在 1TB以内是一个相对合理的范围。在传统的关系数据库无法满足 Internet需求的情况下,越来越多的尝试将数据存储在本机分布式 NoSQL中。但是它与 SQL的不兼容性以及生态系统的不完善性使其无法在竞争中击败关系数据库,因此关系数据库仍然处于不可动摇的地位。同时在互联网应用系统下,单库会存在性能瓶颈(高性能,高可能,高并发),就出现了分库分表。MyCat 并发量大的时候会出现性能瓶颈,所以在实际生产中推荐使用 ShardingSphere。
一、概述
ShardingSphere是一款开源的分布式数据库中间件组成的生态圈。自从2016年开源以来,不断升级开发新功能、重构内核,并于2018年11月进入 Apache基金会孵化器。它由京东集团主导,并由多家公司以及整个 ShardingSphere社区共同运营参与贡献。其主要的功能模块为:数据分片(分库分表)、分布式事务、数据库治理三大块内容。
Sharding-JDBC 是 ShardingSphere的第一个产品,也是 ShardingSphere的前身。 它定位为轻量级 Java框架,在 Java的 JDBC层提供的额外服务。它使用客户端直连数据库,以 jar包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC驱动,完全兼容 JDBC和各种 ORM框架。Sharding-JDBC 采用无中心化架构,适用于Java开发的高性能的轻量级 OLTP应用;
【1】适用于任何基于 JDBC的 ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
【2】支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
【3】支持任意实现 JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标准的数据库。
ShardingSphere 主要的三种类型的比较 Sharding-JDBC(代码层面的处理)、Sharding-Proxy(专门的分库分表中间件,目前在docker 中跑不起来) 和 Sharding-Sidecar(大规模集群上,例如k8s。计划中,还未开发完成):
二、数据分片
分片是指将数据按照一定的标准将其存储在多个表和数据库中,从而提高性能和可用性。两种方法都可以有效避免由于数据超出可承受阈值而导致的查询限制。而且,数据库分片还可以有效地分散TPS。表分片虽然不能减轻数据库的压力,但可以提供将分布式事务转移到本地事务的可能性,因为曾经涉及跨数据库升级,所以分布式事务有时会变得非常棘手。使用多个主从分片方法可以有效避免数据集中在一个节点上,提高架构可用性。通过数据库分片和表分片拆分数据是处理高TPS和海量数据系统的有效方法,因为它可以使数据量保持低于阈值并疏散流量。分片方法可以分为垂直分片和水平分片。
数据分片在 ShardingSphere中主要被划分为读写分离、数据拆分。读写分离主要是指:为数据库搭建灾备副本,并在访问时将这些生产及灾备库分为主库、从库两种角色。其中主库处理所有的修改、变更操作以及少部分读操作;从库分担主库大部分的读请求。数据拆分在这里主要指数据水平分片,即真正意义上将一个数据库拆分成多个分库,分别存储及访问。具体架构如下图所示:
在此基础上,很多业务系统出于性能和安全考虑,会选择这两种方式的混合部署架构,即同时使用读写分离和水平分片策略,如下图所示:
在这种情况下,底层数据的架构网络就会显得异常复杂和繁琐。因为在整个分布式的数据库系统当中会存在:分库1、分库2……,还有对应的从库1、从库2……对业务开发的同学来讲,自身的精力和注意力不仅要放到跟 KPI挂钩的业务代码开发上,还需要考虑如何实现和维护这样一套分布式数据库系统。所以 ShardingSphere便提供了 Sharding-Proxy充当一款分布式数据库中间件,它将为大家解决这些场景下的数据库管理维护的工作(如果生产上不适用 docker 部署就可以使用此模式,但我们生产目前使用 docker部署,而 Sharding-Proxy 在docker 上跑不起来,镜像有问题,后期官方会进行修复) 。
数据分片的重中之重是:如何去拆分数据库表:这将关系到今后整个数据库系统的性能、与业务系统的匹配默契程度。ShardingSphere提供了如哈希取模、范围划分、标签分类、时间范围以及复合分片等多种切分策略。
举例:业务方有可能会按照订单号后几十位做哈希取模来切分库表;也有可能将日志文件信息按照日、月、年的维度进行切分数据,并存储到数据库;还可能按照业务类型进行分库分表等。针对各式各样的业务场景,ShardingSphere提供了以下多种分片策略。虽然这些分配策略基本可以满足80%以上业务方需求,但还是会存在一些变态的业务场景。为此,我们开放了数据分片策略的接口,业务方可以选择按照自己的需求实现这些数据分片接口,ShardingSphere就会通过 SPI的方式将其加载使用。
在确定好数据分片策略后,ShardingSphere将使用该分片策略进行以下操作来完成对某条 SQL的 DDL&DML&DAL&DQL&TCL等操作。但是这个过程对用户来说是透明的,即在用户无感知的情况下,ShardingSphere将用户输入的 SQL进行解析,然后依据用户指定的分片策略对这条不含分片信息的 SQL进行改写,将其改写成为真正在某个或多个数据表上执行的某条或多条真实的SQL。此外,还需要找到每一条真实的SQL究竟需要在哪个库的哪张分表上执行,最终把改写后的真实SQL下发到对应的分表上进行多线程的执行。而用户会将拿到最终汇总后的数据结果集。
挑战性:尽管分片解决了性能,可用性以及单节点备份和恢复等问题,但其分布式体系结构还带来了一些新问题。一个问题是,面对如此分散的数据库和表时,应用程序开发工程师和数据库管理员的操作变得格外费力。他们应该确切地知道从哪个数据库表中获取数据。另一个挑战是,在单节点数据库中正确运行的 SQL在分片数据库中可能不合适。分片后表名称的更改,或由分页,排序依据或聚合分组依据等操作引起的不当行为就是这种情况。
跨数据库事务处理也是分布式数据库需要处理的棘手事情。当单表数据量减少时,合理使用分片表还可以充分利用本地事务。通过明智地使用同一数据库中的不同表,可以避免分布式事务带来的麻烦。当无法避免跨数据库事务时,某些企业仍然需要保持事务一致。互联网巨头尚未大规模采用基于XA的分布式事务,因为它们无法确保其在高并发情况下的性能。它们通常用最终一致的软状态代替强一致的事务。
目标:ShardingSphere数据分片模块的主要设计目标是试图减少分片的影响,以使用户像一个数据库一样使用水平分片数据库组。
三、快速入门
【1】将${latest.release.version}
更改为实际的版本号,我使用的是 4.0.1 版本。
1 <dependency>
2 <groupId>org.apache.shardingsphere</groupId>
3 <artifactId>sharding-jdbc-core</artifactId>
4 <version>${latest.release.version}</version>
5 </dependency>
【2】数据分片+读写拆分规则配置:Sharding-JDBC 可以通过 Java,YAML,Spring命名空间和 Spring Boot Starter四种方式配置,这里使用 SpringBoot进行配置。
1 spring:
2 shardingsphere:
3 #展示实际操作数据库的语句
4 props:
5 sql.show: true
6 #数据库服务器地址
7 datasource:
8 names: ds0,ds0_slave0,ds0_slave1,ds1,ds1_slave0,ds1_slave1
9 ds0:
10 #hikari的高性能得来益于最大源限度的避免锁竞争。
11 type: com.zaxxer.hikari.HikariDataSource
12 hikari:
13 connection-timeout: 5000
14 idle-timeout: 12000
15 max-lifetime: 900000
16 minimum-idle: 10
17 maximum-pool-size: 100
18 connection-test-query: SELECT 1
19 driverClassName: com.mysql.cj.jdbc.Driver
20 #ds0 数据源,这里的从库跟主服务器在同一台机器上,生产上是分开的。
21 jdbcUrl: jdbc:mysql://127.0.0.1:3306/ds0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
22 username: root
23 password: xxxx
24 ds0_slave0:
25 type: com.zaxxer.hikari.HikariDataSource
26 hikari:
27 connection-timeout: 5000
28 idle-timeout: 12000
29 max-lifetime: 900000
30 minimum-idle: 10
31 maximum-pool-size: 100
32 connection-test-query: SELECT 1
33 driverClassName: com.mysql.cj.jdbc.Driver
34 jdbcUrl: jdbc:mysql://127.0.0.1:3306/ds0_slave0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
35 username: root
36 password: xxxx
37 ds0_slave1:
38 type: com.zaxxer.hikari.HikariDataSource
39 hikari:
40 connection-timeout: 5000
41 idle-timeout: 12000
42 max-lifetime: 900000
43 minimum-idle: 10
44 maximum-pool-size: 100
45 connection-test-query: SELECT 1
46 driverClassName: com.mysql.cj.jdbc.Driver
47 jdbcUrl: jdbc:mysql://127.0.0.1:3306/ds0_slave1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
48 username: root
49 password: xxxx
50 ds1:
51 type: com.zaxxer.hikari.HikariDataSource
52 hikari:
53 connection-timeout: 5000
54 idle-timeout: 12000
55 max-lifetime: 900000
56 minimum-idle: 10
57 maximum-pool-size: 100
58 connection-test-query: SELECT 1
59 driverClassName: com.mysql.cj.jdbc.Driver
60 jdbcUrl: jdbc:mysql://127.0.0.1:3306/ds1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
61 username: root
62 password: xxx
63 ds1_slave0:
64 type: com.zaxxer.hikari.HikariDataSource
65 hikari:
66 connection-timeout: 5000
67 idle-timeout: 12000
68 max-lifetime: 900000
69 minimum-idle: 10
70 maximum-pool-size: 100
71 connection-test-query: SELECT 1
72 driverClassName: com.mysql.cj.jdbc.Driver
73 jdbcUrl: jdbc:mysql://127.0.0.1:3306/ds1_slave0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
74 username: root
75 password: xxx
76 ds1_slave1:
77 type: com.zaxxer.hikari.HikariDataSource
78 hikari:
79 connection-timeout: 5000
80 idle-timeout: 12000
81 max-lifetime: 900000
82 minimum-idle: 10
83 maximum-pool-size: 100
84 connection-test-query: SELECT 1
85 driverClassName: com.mysql.cj.jdbc.Driver
86 jdbcUrl: jdbc:mysql://127.0.0.1:3306/ds1_slave1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
87 username: root
88 password: xxx
89
90 #读写拆分
91 masterSlaveRules:
92 ms_ds0:
93 masterDataSourceName: ds0
94 slaveDataSourceNames:
95 - ds0_slave0
96 - ds0_slave1
97 loadBalanceAlgorithmType: ROUND_ROBIN
98 ms_ds1:
99 masterDataSourceName: ds1
100 slaveDataSourceNames:
101 - ds1_slave0
102 - ds1_slave1
103 loadBalanceAlgorithmType: ROUND_ROBIN
104
105 #数据分片
106 sharding:
107 tables:
108 # role 是表的名称,不同的表都需要配置分片规则,这也是 ShandingSphere 的缺点。
109 role:
110 actualDataNodes: ms_ds${0..1}.role${0..1}
111 databaseStrategy:
112 inline:
113 #id 是role表的字段
114 shardingColumn: id
115 algorithmExpression: ds${id % 2}
116 tableStrategy:
117 inline:
118 #f_id 是role表的字段
119 shardingColumn: f_id
120 algorithmExpression: role${id % 2}
121 keyGenerator:
122 #主见通过雪花算法生成
123 type: SNOWFLAKE
124 column: id
【3】创建数据源:使用ShardingDataSourceFactory 和规则配置对象来创建ShardingDataSource,它是通过标准JDBC接口DataSource实现的。然后,用户可以使用本机 JDBC或 JPA,MyBatis(我们使用)和其他 ORM框架进行开发。
DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, props);
四、读写拆分
随着系统TPS的增加,数据库容量面临巨大的瓶颈效应。对于具有大量并发读取操作但同时写入操作较少的应用程序系统,我们可以将数据库分为主数据库和从数据库。主数据库负责事务的添加,删除和修改,而从数据库负责查询。通过有效地避免由于数据更新而引起的行锁定,可以显着提高整个系统的查询性能。一个具有多个从属数据库的主数据库可以通过将查询平均分配到多个数据副本中来进一步增强系统处理能力。具有多个从数据库的多个主数据库不仅可以提高系统吞吐量,而且可以提高系统可用性。因此,即使任何数据库已关闭或物理磁盘已损坏,系统仍可以正常运行。与根据分片键将数据分离到所有节点的水平分片不同,读写拆分路由根据SQL含义分析将读操作和写操作分别写入主数据库和从属数据库。读写拆分节点中的数据是一致的,而水平分片中的数据则不一致。水平分片和读写拆分的组合使用将有效地提高系统性能。
【挑战性】:尽管读写拆分可以提高系统吞吐量和可用性,但它还会带来不一致的数据,包括多个主数据库之间以及主数据库和从数据库之间的数据。此外,它还带来与数据分片相同的问题,使应用程序开发人员和操作员的维护与操作复杂化。下图显示了将分片表和数据库与读写拆分一起使用时,应用程序和数据库组之间的复杂拓扑关系。
【主从复制】:它是指将数据从主数据库异步复制到从数据库的操作。由于主从异步,它们之间可能存在短期数据不一致。
【负载均衡策略】:通过这种策略,查询被分离到不同的从数据库。
五、问题及解决方案
【1】SQL兼容程度:通过上面的讲解,大家可以看到使用上任何一款分布式数据库中间件都会面临一个问题:SQL是否全支持?因为一条不含分片信息的SQL是需要经过解析、改写、路由、执行、归并这些步骤的,所以对SQL的加工处理,有可能会致使中间件对于部分SQL是不支持的。在真正落地XX业务时,也出现了这个问题。XX业务的业务逻辑非常复杂且庞大,同时多样化场景的需求对SQL的兼容程度有较高要求。ShardingSphere为了能全面支撑XX业务,进行了两方面的优化重构:
▄ 一方面是重构了SQL解析模块;
▄ 另一方面是在除了解析模块之外的模块对更多的SQL进行兼容支持,例如COUNT(DISTINCT *) 等SQL。
SQL解析模块是中间件的基石,如果基石不牢靠,上层建筑将岌岌可危。从第一代的解析引擎使用 Druid的内置解析引擎到第二代自研了 SQL解析引擎,再到现在使用 Antlr解析器作为 SQL解析器,经历了2年之久。耗时费力如此之多,只为了真正搭建好基石,做到解析引擎自主可控、对SQL高效全面支持。当前,SQL支持情况为:
➊ 路由至单节点,SQL100%支持;
➋ 路由至多节点,全面支持DQL、DML、DDL、DCL、TCL和MySQL的部分DAL。支持分页、去重、排序、分组、聚合、关联查询(不支持跨库关联);
【2】分布式主键:传统数据库软件开发中,主键自动生成技术是基本需求。而各个数据库对于该需求也提供了相应的支持,比如MySQL的自增键、Oracle的自增序列等。数据分片后,不同数据节点生成全局唯一主键是非常棘手的问题。同一个逻辑表内的不同实际表之间的自增键由于无法互相感知而产生重复主键。虽然可通过约束自增主键初始值和步长的方式避免碰撞,但需引入额外的运维规则,使解决方案缺乏完整性和可扩展性。目前有许多第三方解决方案可以完美解决这个问题,如 UUID等依靠特定算法自生成不重复键,或者通过引入主键生成服务等。为了方便用户使用、满足不同用户不同使用场景的需求,ShardingSphere提供了内置的分布式主键生成器,例如UUID、SNOWFLAKE等分布式主键生成器,用户仅需简单配置即可使用,生成全局性的唯一自增ID。此外,我们还抽离出分布式主键生成器的接口,方便用户自行实现自定义的自增主键生成算法,以满足用户特殊场景的需求。
【3】业务分片键值注入:通过解析SQL语句提取分片键列与值并进行分片,是ShardingSphere对SQL零侵入的实现方式。若SQL语句中没有分片条件,则无法进行分片,需要全路由。在一些应用场景中,分片条件并不存在于SQL,而存在于外部业务逻辑。因此需要提供一种通过外部指定分片结果的方式,在 ShardingSphere中叫做Hint。ShardingSphere使用 ThreadLocal管理分片键值。可以通过编程的方式向 HintManager中添加分片条件,该分片条件仅在当前线程内生效。除了通过编程的方式使用强制分片路由,ShardingSphere还计划通过SQL中的特殊注释的方式引用Hint,使开发者可以采用更加透明的方式使用该功能。指定了强制分片路由的SQL将会无视原有的分片逻辑,直接路由至指定的真实数据节点。下面的图片将给出这一场景的具体实施案例:
通过向 HintManager注入 status和具体路由表的关系,ShardingSphere将按照用户指定规则,强制到 db_0.t_order_1执行SQL,并将结果返回给用户。
【4】性能优化:性能问题是任何一个上线系统在面临业务高峰时都必须要考虑的问题。面对XX白条这个量级的应用,ShardingSphere为了满足白条业务对TPS/QPS的强制要求,做了多方面优化,主要为:
★ SQL解析结果缓存;
★ JDBC元数据信息缓存;
★ Bind表&广播表的使用;
★ 自动化执行引擎&流式归并;
受篇幅所限,这里主要为大家介绍 Bind表和广播表使用。这两种表的配置使用,主要是为了优化表关联问题中,切分表与切分表之间笛卡尔积表关联的情况,以及解决跨库表关联不支持的情况。
绑定表是指分片规则一致的主表和子表。例如:t_order表和t_order_item表,均按照order_id分片,则此两张表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,从而关联查询效率将大大提升。因为主表和子表使用相同的分片策略,数据在主表和子表的分布情况将一模一样,所以表关联查询的时候就能避免笛卡尔积。举例说明,如果SQL为:
1 SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
在不配置绑定表关系时,假设分片键order_id将数值10路由至第0片,将数值11路由至第1片,那么路由后的SQL应该为4条,它们呈现为笛卡尔积:
1 SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
2 SELECT i.* FROM t_order_0 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
3 SELECT i.* FROM t_order_1 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
4 SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
在配置绑定表关系后,路由的SQL应该为2条:
1 SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
2 SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
其中 t_order在 FROM的最左侧,ShardingSphere将会以它作为整个绑定表的主表。所有路由计算将会只使用主表的策略,那么t_order_item表的分片计算将会使用 t_order的条件。故绑定表之间的分区键要完全相同。广播表是指所有底层分片数据源中都存在的表,表结构和表中的数据在每个分库中完全一致。这样在进行关联查询的时候,由于广播表在所有分库均存在,就避免了笛卡尔积关联查询以及跨库关联的情况。比较适用于数据量不大且需要与海量数据的表进行关联查询的场景,例如:字典表。
ShardingSphere 数据分片之 Sharding-JDBC 深入理解的更多相关文章
- ShardingSphere数据分片
码农在囧途 坚持是一件比较难的事,坚持并不是自欺欺人的一种自我麻痹和安慰,也不是做给被人的,我觉得,坚持的本质并没有带着过多的功利主义,如果满是功利主义,那么这个坚持并不会长久,也不会有好的收获,坚持 ...
- spring boot:配置shardingsphere(sharding jdbc)使用druid数据源(druid 1.1.23 / sharding-jdbc 4.1.1 / mybatis / spring boot 2.3.3)
一,为什么要使用druid数据源? 1,druid的优点 Druid是阿里巴巴开发的号称为监控而生的数据库连接池 它的优点包括: 可以监控数据库访问性能 SQL执行日志 SQL防火墙 但spring ...
- 【ShardingSphere】ShardingSphere学习(三)-数据分片-分片
分片键 分片算法 分片策略 SQL Hint 分片键 用于分片的数据库字段,是将数据库(表)水平拆分的关键字段.例:将订单表中的订单主键的尾数取模分片,则订单主键为分片字段. SQL中如果无分片字段, ...
- Sharding jdbc 强制路由策略(HintShardingStrategy)使用记录
背景 随着项目运行时间逐渐增加,数据库中的数据也越来越多,虽然加索引,优化查询,但是数据量太大,还是会影响查询效率,也给数据库增加了负载. 再加上冷数据基本不使用的场景,决定采用分表来处理数据,从而来 ...
- sharding-jdbc数据分片配置
数据分片 不使用Spring 引入Maven依赖 <dependency> <groupId>org.apache.shardingsphere</groupId> ...
- Sharding JDBC案例实战
基础分库 以下实例基于shardingsphere 4.1.0 + SpringBoot 2.2.5.RELEASE版本 依赖导入: <properties> <project.bu ...
- MyCat 学习笔记 第十二篇.数据分片 之 分片事务处理
1 环境说明 VM 模拟3台MYSQL 5.6 服务器 VM1 192.168.31.187:3307 VM2 192.168.31.212:3307 VM3 192.168.31.150: 330 ...
- MongoDB分片(Sharding)技术
分片(sharding)是MongoDB用来将大型集合分割到不同服务器(或者说一个集群)上所采用的方法.尽管分片起源于关系型数据库分区,但MongoDB分片完全又是另一回事. 和MySQL分区方案相比 ...
- 高级开发不得不懂的Redis Cluster数据分片机制
Redis 集群简介 Redis Cluster 是 Redis 的分布式解决方案,在 3.0 版本正式推出,有效地解决了 Redis 分布式方面的需求. Redis Cluster 一般由多个节点组 ...
- Redis Cluster 的数据分片机制
上一篇<分布式数据缓存中的一致性哈希算法> 文章中讲述了一致性哈希算法的基本原理和实现,今天就以 Redis Cluster 为例,详细讲解一下分布式数据缓存中的数据分片,上线下线时数据迁 ...
随机推荐
- idea常用快捷键记录
实用编写代码辅助快捷键 Ctrl+Alt+V 提出选中内容为局部变量 Ctrl+Backspace 按单词删除 Ctrl+D 复制行 Ctrl+Y 删除当前行 Ctr+Shift+U 大小写转化 Sh ...
- vue+antd实现PDF预览(后端返回的是文件流)
操作步骤: 第一步:下载包 npm install --save vue-pdf 第二步:导入组件 第三步:使用pdf标签进行展示,showUrl指的是访问路径 第四步:定义要用到的变量 第五步: ...
- vue后台管理系统——登录/退出功能
电商后台管理系统的功能--登录/退出功能 1. 登录业务流程 ① 在登录页面输入用户名和密码 ② 调用后台接口进行验证 ③ 通过验证之后,根据后台的响应状态跳转到项目主页 2. 登录业务的相关技术点 ...
- Java中File类
File类是java.io包中唯一代表磁盘文件本身的对象.File类的对象主要用来获取文件本身的一些信息,如文件所在目录.文件长度.读写权限等. 一. 文件的创建与删除 通常使用以下三种方法来创建一个 ...
- SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated
错误提示: SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #1 of SELECT list is not in ...
- UE4 联网RPC部分
有些人生来就是为了奔跑,而有些人,则拥有更高的目标> 笔者近期在进行UE C++网络的一些开发,发现RPC这个部分它看起来很简单,理解起来也算不上难.但真正应用起来,对netcode经验不多的人 ...
- python3中的负数整除、求余问题
注:小白问题,大神们请忽略先看示例,非整除: >>> -10/3-3.3333333333333335>>> 10/-3-3.3333333333333335> ...
- gpio 理解
NVIC :NVIC_Init(&NVIC_Initsture); 1.NVIC只是设置某一种中断的优先级,而不是打开某种中断. 2.ppp_ITConfig():才是开/关具体某种中断使能位 ...
- 学习JavaScript 第二周
分支结构中的switch switch(值&条件表达式){ case 值: 操作: break; case 值: 操作: break; ... default: 默认操作 } switch根据 ...
- pg_dump导出表时正则匹配多个表,pg_dump导出表
报错信息:pg_dump: 错误: 没有找到符合的表 报错语句:C:\Users\Admin>pg_dump -h172.16.3.159 -p5432 -dchisapp -nmchs -Um ...