1 场景复现
线上服务做A/B测试,根据a数据切分流量,
这里直接采用hashCode,取最后两位0-99作为流量通道,
取0-10通道测试功能,在做流量切分时,发现流量并比例并不是1:9,
而是达到了惊人的1:1,于是开始排查a数据的分布是否均匀,发现,是均匀的,然后,对a数据进行独立数统计,
抽取数量较多的a数据进行hashCode测试,发现计算之后出现负数,这些负数都挤在了[0,9]这个通道,
因为流量切分直接使用traffic<=10,所以出现流量比例异常的问题。
2 方案
重新设计逻辑:取结果的绝对值:Math.abs(result)。
3 原理
3.1 数据轮转
数据在取值范围内轮转,当数据值超过取值范围时,
按照约定计算新值,本文以byte为例,探究数据是如何轮转的。
1byte占用的存储空间为8bit,即1byte=8bits,取值范围:[
−
2
7
-2^7
−27,
2
7
2^7
27-1],
总共可以存储256个数据,数据轮转示意图如下图所示,
数据轮转计算规则:
(1)绝对值按位取反;
(2)加1;
(3)变更符号。
3.2 Code
byte测试数据轮转样例如下,
数据转换过程在注释中给出。
package com.monkey.java_study.functiontest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
/**
* Byte测试.
*
* @author xindaqi
* @date 2021-12-01 17:48
*/
public class ByteTest {
private static final Logger logger = LogManager.getLogger(ByteTest.class);
@Test
public void byteRange() {
Byte byteMax = Byte.MAX_VALUE;
Byte byteMin = Byte.MIN_VALUE;
logger.info(">>>>>>>>Byte max value:{}, byte min value:{}", byteMax, byteMin);
// 在取值范围内,不进行操作
Byte var127 = (byte) 0b0111_1111;
// 128(1000,0000)->绝对值按位取反(0111,1111)->加1(0111,1111+1)=128->变更符号(-128)
Byte var128 = (byte) 0b1000_0000;
logger.info(">>>>>>>>Var127:{}, Var128:{}", var127, var128);
// 129(1000_0001)->绝对值按位取反(0111,1110)->加1(0111,1110+1)=127->变更符号(-127)
Byte var129 = (byte) 0b1000_0001;
// -129(-1000,0001):绝对值按位取反(0111,1110)->加1(0111,1111+1)=127->变更符号(127)
Byte varNegative129 = (byte) -0b1000_0001;
logger.info(">>>>>>>>Var129:{}, VarNegative129:{}", var129, varNegative129);
}
}
测试结果如下图所示,与计算逻辑一致。
4 小结
- 数据取值在对应的范围内,直接使用,超出范围,重新进行计算,并使用在数据范围内轮转;
- 数据轮转规则:
- 绝对值按位取反;
- 加1;
- 变更符号。