zero's Blog

持续迭代

什么是面向接口编程
“基于接口而非实现编程”这条原则的英文描述是:“Program to an interface, not an implementation”。

我们理解这条原则的时候,千万不要一开始就与具体的编程语言挂钩,局限在编程语言的“接口”语法中(比如Java中的 interface)。这条原则是一条比较抽象、泛化的设计思想。

实际上,理解这条原则的关键,就是理解其中的“接口”两个字,这里的“接口”是泛指,可以理解为“抽象”。“基于接口而非实现编程”这条原则的另一个表述方式是“基于抽象而非实现编程”,后者的表述方式其实更能体现这条原则的设计初衷。落实到具体的编码,“接口”可以理解为编程语言中的接口(interface)或者抽象类(abstract class)。

Read more »

表要求:有PrimaryKey,或者unique索引
结果:表id都会自增

测试代码

创建表

1
2
3
4
5
CREATE TABLE names(
id INT(10) PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) UNIQUE,
age INT(10)
)

插入数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> insert into names(name, age) values("小明", 24);
mysql> insert into names(name, age) values("大红", 24);
mysql> insert into names(name, age) values("大壮", 24);
mysql> insert into names(name, age) values("秀英", 24);

mysql> select * from names;
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | 小明 | 24 |
| 2 | 大红 | 24 |
| 3 | 大壮 | 24 |
| 4 | 秀英 | 24 |
+----+--------+------+
Read more »

现象
日志里提示了如下一个错误:
Transaction rolled back because it has been marked as rollback
意思是,准备提交事务,将落地的数据库数据生效时,事务已经被设置为rollback-only了,无法提交事务。后面仔细看了一下代码,原来是开了事务的方法里,抛了一个异常,但是程序把这个异常给catch住了,没往上抛。

原因
字面上的意思就是:事务已回滚,因为它已被标记为仅回滚,那为什么会标记为仅回滚呢?

其实原因就是嵌套事务导致的,因为spring事务有传递性,spring默认的事务传播级别是PROPAGATION_REQUIRED,即当前上下文存在事务则用此事务,如果不存在事务则新建一个事务执行;

那么现在有A和B两个方法,这两个方法都开启了事务,A方法中调用B方法(因为都使用事务,默认的事务传播级别是PROPAGATION_REQUIRED,所以这过程中会使用同一个事务);

当执行B方法的时候,B方法抛出异常,这个时候事务就会被标记为仅回滚(因为在B方法中抛出异常,B方法这事务本该是要回滚,所以会将B方法的事务标记为rollback-only);

但是在A方法又catch到B方法抛的异常,但是A方法catch到异常后没有继续往上抛出,而是继续执行后面的代码,最后正常提交事务,那么就会抛出 Transaction rolled back because it has been marked as rollback-only这异常!(因为AB是用同一个事务,在B方法执行的时候这个事务就标记为rollback-only,然后A方法继续使用该事务,然后又执行事务提交的操作,所以最后会抛异常)

1
2
3
4
5
6
7
8
9
@Transactional
@Override
public boolean A(User user) {
try {
userMapper.B(user);
} case {
//这里做了保存,没有继续向上抛错
}
}
1
2
3
4
5
6
7
@Transactional
@Override
public boolean B(User user) {
//.....
int i = 1/0; //这里报错了
return false;
}

解决办法
1、在B方法中进行try case case中不进行抛错处理;
2、在A方法case中继续抛错处理;
3、在B方法上的Transaction 添加事务传播规则为 propagation = Propagation.REQUIRES_NEW 新建事务进行处理

在业务中查询某张表时需要设置多个查询条件,并且还要根据id列表进行权限过滤,这时推荐采用Map<String, Object>作为参数进行查询,因为:Object可以设置成不同的类 型,比如:getByParam(List<Integer> ids, Map<String, Object>)

我们看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<select id="getByParam" resultMap="CrowdManageResult" parameterType="java.util.Map">
select <include refid="Base_Column_List"/>
from t_crowd_manage where 1=1
<if test="crowd.name != null">
and name like concat('%',#{crowd.name, jdbcType=VARCHAR},'%')
</if>
<if test="crowd.type != null">
and type = #{crowd.type, jdbcType=INTEGER}
</if>
<if test="crowd.status != null">
and status = #{crowd.status, jdbcType=INTEGER}
</if>
<if test="crowd.noStatus != null">
and status != #{crowd.noStatus, jdbcType=INTEGER}
</if>
<if test="ids != null and ids.size() > 0">
and id in
<foreach item="id" collection="ids" open="(" separator="," close=")">
#{id, jdbcType=BIGINT}
</foreach>
</if>
order by update_time desc
</select>

service层查询代码:

1
2
3
4
5
6
7
8
9
10
Map<String, Object> param = new HashMap<>(2);
Map<String, Object> crowdEntity = new HashMap<>(); // xml if 判断不会报错
crowdEntity.put("name", "test");
crowdEntity.put("type", 1);
crowdEntity.put("noStatus", -1);

Set<Integer> idList = //....

param.put("crowd", crowdEntity);
param.put("ids", idList);

注意:这个sql条件查询条件主要设计两个部分:

  1. 一般的查询条件,这里可以使用Bean对象,也可以使用map。

    推荐使用后者,因为在sql的条件中有一个noStatus字段判断,通常Bean定义的时候只有status字段(没有noStatus),如果这里使用定义Bean来当查询参数,在xml中<if test="crowd.noStatus != null"> 这句会报错。原因是mybatis的xml采用ONGI查询来解析,所以如果Bean中没有定义这个字段,就会报错。

  2. in条件查询,这里使用的是foreach

在开发过程中,操作数据库总会涉及到批量操作。有些批量操作可以利用multiquery更新数据库,但有些不可,例如对于同一张表不同字段的多行更新。我们经常会把这种操作放到一个事务里面,由于都是按照主键更新,所以性能上不会有大问题,例如这样:

1
2
3
4
5
6
7
8
@Transactional
public void update(List<DAO> daoList)
{
for (DAO dao : daoList) {
daoMapper.updateByPrimaryKeySelective(dao);
}

}
Read more »

1.用法

用法: select … for update;
例如:select * from goods where id = 1 for update;
排他锁的申请前提:没有线程对该结果集中的任何行数据使用排他锁或共享锁,否则申请会阻塞。

**for update仅适用于InnoDB,且必须在事务块(BEGIN/COMMIT)**中才能生效。在进行事务操作时,通过“for update”语句,MySQL会对查询结果集中每行数据都添加排他锁,其他线程对该记录的更新与删除操作都会阻塞。排他锁包含行锁、表锁。

Read more »

[toc]

1. 概述

Redis Cluster,采用了去中心化的多主多从架构,以提高数据的可用性和伸缩性。Redis集群的目的是实现数据的横向伸缩,把一块数据分片保存到多个机器,可以横向扩展数据库大小,扩展带宽,计算能力等。

以下是Redis Cluster的几个关键特点和优势的详细阐述:

  • 去中心化的多主多从架构

    • 每个从节点都复制主节点的数据,但不直接参与读写操作,主要用于数据备份和故障恢复。
    • 这种架构使得每个节点都可以在需要时承担主节点的角色,从而提高了整体系统的可靠性和容错能力。
  • 数据处理与性能

    • Redis Cluster在处理涉及多个键的操作时可能面临性能挑战,尤其是在数据量大和高并发的场景下。这是因为多key操作可能需要跨多个节点进行,从而增加了操作的复杂性。
    • 然而,对于单key操作,Redis Cluster能够保持其一贯的高性能,特别是在读操作上。
  • 动态扩容和收缩能力

    • Redis Cluster支持动态地添加或移除节点,这意味着可以根据实际需求调整集群的规模,无需停机或中断服务。
    • 这一特性对于处理不断变化的负载和数据量非常重要,使得Redis Cluster在大型应用中更具弹性。
  • 节点间的通信与故障转移

    • 在Redis Cluster中,主节点之间会进行定期的健康检查和状态同步,确保数据的一致性。
    • 当主节点出现故障时,其他主节点可以通过选举机制快速选出新的主节点,实现故障的自动转移,从而确保服务的连续性。
Read more »

Redis 的几种常见使用方式包括:

  • 单机模式
  • 主从模式
  • 哨兵模式(sentinel)
  • 集群模式(cluster)
  • 第三方模式

  

单机模式

Redis 单副本,采用单个 Redis 节点部署架构,没有备用节点实时同步数据,不提供数据持久化和备份策略,适用于数据可靠性要求不高的纯缓存业务场景。

优点:

  1. 架构简单,部署方便。
  2. 高性价比:缓存使用时无需备用节点(单实例可用性可以用 supervisor 或 crontab 保证),当然为了满足业务的高可用性,也可以牺牲一个备用节点,但同时刻只有一个实例对外提供服务。
  3. 高性能。

缺点:

  1. 不保证数据的可靠性。
  2. 在缓存使用,进程重启后,数据丢失,即使有备用的节点解决高可用性,但是仍然不能解决缓存预热问题,因此不适用于数据可靠性要求高的业务。
  3. 高性能受限于单核 CPU 的处理能力(Redis 是单线程机制),CPU 为主要瓶颈,所以适合操作命令简单,排序、计算较少的场景。
Read more »

Redis 的线程模型其实是分两块的:

  • Redis 6.0 之前的单线程模型。其实从 4.0 开始,Redis 并不是严格意义上的单线程模型,因为 Redis 除了主线程外,也有一些后台的线程或者子进程在处理任务(例如清理脏数据、生成快照、AOF 重写),这个时候大家所说的单线程应该是 Redis 的主线程模型。

  • Redis 6.0 之后的多线程模型。Redis 在 6.0 之后引入了一种多线程模型,用于处理网络 I/O 的任务。

所以,你的回答要涉及这两个方面。

  • Redis 的单线程是指Redis 在执行一次命令时是单线程的。其过程包括「接收客户端请求 -> 解析请求 ->数据读写等操作->返回结果给客户端」,这个过程是由一个主线程来完成的,这也是我们常说 Redis 是单线程的原因。Redis 的模型是基于单线程事件驱动模型,内部使用文件事件处理器,而这个文件事件处理是单线程的,也就决定了 Redis 是单线程的。其核心原理是:采用 IO多路复用机制同时监听多个 socket,将产生事件的 socket 压入内存队列中,事件分派器根据 socket 上的事件类型来选择对应的事件处理器进行处理。
  • 随着底层网络硬件越来越好,Redis 的性能瓶颈逐渐体现在网络 I/O 的读写上,单个线程处理网络 I/O 读写的速度跟不上底层网络硬件执行的速度。所以为了提高 Redis 的性能,在 Redis 6.0 引入多线程模型,该多线程模型只用来处理网络数据的读写和协议解析,执行读写命令的仍然是单线程。
Read more »
0%