MySQL中BLOB列LIKE操作原理及多编码值匹配实现方法
好的,咱们一步步来解决你的问题——先搞清楚BLOB列上LIKE操作的基本逻辑,再适配你的拼接存储场景,最后聊聊背后的底层原理。
首先得明确:BLOB是二进制大对象,它和VARCHAR这类字符串列的本质区别是——BLOB存储的是原始字节序列,没有绑定字符集和排序规则。所以BLOB上的LIKE操作,完全是基于字节级的精确匹配,而不是字符级的匹配。
举个例子:如果你的BLOB里存的是UTF-8编码的"abc"(对应字节0x616263),当你执行WHERE blob_col LIKE '%abc%'时,MySQL会把字符串"abc"转换成当前连接的字符集对应的字节序列(比如UTF-8下就是0x616263),然后逐字节去和BLOB里的内容比对,找到连续匹配的片段。
和字符串列的LIKE相比,BLOB的LIKE有几个关键差异:
- 不支持大小写不敏感:因为没有字符集的概念,字节就是字节,
0x41(大写A)和0x61(小写a)会被当成完全不同的字节。 - 不会做任何字符转换:比如你存的是GBK编码的字节,查询时用UTF-8的字节去匹配,肯定找不到结果。
- 前缀匹配(
LIKE 'X%')理论上可以用索引,但任意子串匹配(LIKE '%X%')只能全表扫描,因为索引没法快速定位中间的字节片段。
回到你的场景:你把多个值分别编码成字节数组,然后直接拼接成encode(val1)+encode(val2)+encode(val3)的形式存在BLOB里,现在要找包含encode(val1)的行。
实现方法很直接,核心是保证查询时用的字节序列和存储时的encode(val1)完全一致,具体操作分两种情况:
情况1:你知道encode(val1)的十六进制字节值
比如你编码val1后得到的字节序列是0x12345678,那直接用这个十六进制字面量构造LIKE条件:
SELECT * FROM your_table WHERE blob_col LIKE CONCAT('%', 0x12345678, '%');
情况2:你需要从原始val1生成对应的字节序列
如果val1是字符串,且你存储时用的是特定编码(比如UTF-8),那可以用CAST()函数把字符串转成二进制字节序列:
-- 假设存储时用UTF-8编码val1,查询时也用同样编码转换 SELECT * FROM your_table WHERE blob_col LIKE CONCAT('%', CAST('your_val1_content' AS BINARY), '%');
关键注意点
一定要确保查询时的编码逻辑和存储时完全一致!比如存储时你把val1用GBK编码成字节,那查询时也必须把val1转成GBK的二进制,不然字节序列不匹配,就查不到结果。
咱们分两部分来理解背后的逻辑:
1. BLOB列LIKE操作的底层逻辑
MySQL处理BLOB的LIKE时,会跳过所有字符集相关的处理步骤,直接对存储的原始字节数组做匹配:
- 首先把LIKE模式里的内容转换成二进制序列(如果是字符串的话,会用当前连接的字符集转成字节)。
- 然后遍历目标BLOB的每个字节位置,从该位置开始逐字节对比后续内容是否和模式的字节序列完全一致。
- 只要找到连续的匹配字节段,就判定该行符合条件。
2. 拼接场景下的匹配原理
因为你存储的是三个编码后字节数组的直接拼接,整个BLOB的字节序列就是[val1字节][val2字节][val3字节](没有分隔符)。这时候LIKE '%encode(val1)%'会匹配任何包含encode(val1)字节序列的位置:
- 如果val1的字节序列刚好在BLOB的开头,会被匹配;
- 如果val1的字节序列出现在val2和val3的拼接处(刚好巧合和val1的字节一致),也会被匹配(这是你需要注意的误匹配风险,如果要避免的话,最好在编码时给每个值加唯一的分隔字节);
- 因为是字节级匹配,所以只要连续字节完全一致,不管位置在哪里都会被命中。
另外要提一句:这种%X%的任意子串匹配,在BLOB列上是无法利用索引的,MySQL只能做全表扫描,所以如果表数据量很大,查询性能可能会比较差——如果频繁做这类查询,建议考虑拆分存储或者用全文索引(不过MySQL的全文索引对BLOB支持有限,可能需要额外处理)。
内容的提问来源于stack exchange,提问作者user7665040

