代码开发套路
Optional.orElseGet
Optional<OrderByContext> result = // 省略代码...
return result.orElse(getDefaultOrderByContextWithoutOrderBy(groupByContext));
以上这种使用 orElse 的写法,即使 result 的结果不为空,orElse 里面的方法也会被调用,尤其是 orElse 里面的方法涉及修改操作时,可能会发生意料之外的事情。涉及方法调用的情况下应调整为下面的写法:
Optional<OrderByContext> result = // 省略代码...
return result.orElseGet(() -> getDefaultOrderByContextWithoutOrderBy(groupByContext));
使用 lambda 提供一个 Supplier 给 orElseGet,这样只有 result 为空的时候才会调用 orElseGet 里面的方法。
Java 8 ConcurrentHashMap 的 computeIfAbsent
在JAVA8的Map接口中,增加了一个方法computeIfAbsent,此方法签名如下:
public V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
如果mappingFunction(key)返回的值为null或抛出异常,则不会有记录存入map
1.ConcurrentHashMap 的 computeIfAbsent 在 key 存在的情况下,仍然会在 synchronized 代码块中获取 value,在对同一个 key 高频调用 computeIfAbsent 的情况下非常影响并发性能。
规避问题的解决
SQLExecutionUnitBuilder result;
if (null == (result = TYPE_TO_BUILDER_MAP.get(type))) {
result = TYPE_TO_BUILDER_MAP.computeIfAbsent(type, key -> TypedSPIRegistry.getRegisteredService(SQLExecutionUnitBuilder.class, key, new Properties()));
}
return result;
避免高频调用 java.util.Properties
java.util.Properties 是 ShardingSphere 在配置方面比较常用的一个类,Properties 继承了 java.util.Hashtable,因此要避免在并发情况下高频调用 Properties 的方法。
init 方法内完成,避免在分片算法计算逻辑的并发性能。
避免使用Collections.synchronizedMap
String.format性能有影响慎用
fore-each 代替高频 stream
我们发现将压测过程中发现的所有高频 stream 替换为 for-each 后,ShardingSphere-JDBC 的性能提升明显。
以上测试结果也可能和 aarch64 平台及 JDK 有关。不过 stream 本身存在一定开销,性能在不同场景下差异较大,对于高频调用且不确定 stream 能够优化性能的逻辑,我们考虑优先使用 for-each 循环。
hashCode计算
@RequiredArgsConstructor
@Getter
@ToString
public final class Column {
private final String name;
private final String tableName;
@Override
public boolean equals(final Object obj) {...}
@Override
public int hashCode() {
return Objects.hashCode(name.toUpperCase(), tableName.toUpperCase());
}
}
显而易见,上面这个类是不可变的,但是却在hashCode 方法的实现中每次都调用方法计算 hashCode。如果这个对象频繁在 Map 或者 Set 中存取,就会多出很多不必要的计算开销。
调整后:
@Getter
@ToString
public final class Column {
private final String name;
private final String tableName;
private final int hashCode;
public Column(final String name, final String tableName) {
this.name = name;
this.tableName = tableName;
hashCode = Objects.hash(name.toUpperCase(), tableName.toUpperCase());
}
@Override
public boolean equals(final Object obj) {...}
@Override
public int hashCode() {
return hashCode;
}
}
使用 lambda 代替反射调用方法
使用反射的方式记录方法调用及重放,反射调用方法本身存在一定的性能开销,且代码可读性欠佳:
@Override
public void begin() {
recordMethodInvocation(Connection.class, "setAutoCommit", new Class[]{boolean.class}, new Object[]{false});
}
避免反射
@Override
public void begin() {
connection.getConnectionPostProcessors().add(target -> {
try {
target.setAutoCommit(false);
} catch (final SQLException ex) {
throw new RuntimeException(ex);
}
});
}
UUID.random().toString存在性能差
UUID.randomUUID().toString();修改为
Random random = ThreadLocalRandom.current();
new UUID(random.nextLong(), random.nextLong()).toString();