假设维护数据库是个市公民信息系统,若对身份证号做长度6前缀索引,区分度非常低 。
可能需创建长度12以上前缀索引,才能够满足区分度要求 。
但索引选取越长,占磁盘空间越大,相同数据页能放下索引值越少,查询效率就越低 。
- 若能确定业务需求只有按身份证进行等值查询的需求,还有没有别的处理方法,既可占用更小空间,也能达到相同查询效率?
Yes!
第一种方式使用3.1 倒序存储 如果存储身份证号时把它倒过来存,每次查询这么写:select field_list from t where id_card = reverse('input_id_card_string'); 由于身份证号最后6位没有地址码这样重复逻辑,所以最后6位可能提供足够的区分度 。
实践中也别忘记使用count(distinct)验证区分度哦!
第二种方式是使用3.2 hash字段 可在表再创建整数字段,保存身份证的校验码,同时在该字段创建索引 。alter table t add id_card_crc int unsigned, add index(id_card_crc); 每次插新记录时,同时用crc32()函数得到校验码填到该新字段 。
由于校验码可能存在冲突,即两不同身份证号crc32()所得结果可能相同(哈希冲突),所以查询语句where部分要判断id_card值是否精确相同 。select field_list from t where id_card_crc=crc32('input_id_card_string') and id_card='input_id_card_string' 这索引长度变4字节,比原来小很多 。3.3 倒序存储和使用hash字段异同点 相同点 都不支持范围查询 。 - 倒序存储的字段上创建的索引
按倒序字符串的方式排序,已无法利用索引查出身份证号码在[ID_X, ID_Y]的所有市民 - hash字段也只支持等值查询区别 占用的额外空间
- 倒序存储在主键索引上,不会消耗额外存储空间
当然,倒序存储使用4字节前缀长度应该不够,若再长点,这消耗和hash字段也差不多了 - hash字段需要加个字段CPU消耗
- 倒序方式每次读写时,都需额外调用次reverse函数
- hash字段需额外调用一次crc32()函数
若只从这俩函数计算复杂度看,reverse函数额外消耗CPU资源较少 。查询效率 - hash字段查询性能较稳定
因为crc32值虽然会冲突,但概率很小,可认为每次查询的平均扫描行数接近1 - 倒序存储
还是前缀索引,即还是会增加扫描行数总结 字符串字段创建索引的场景,可使用的方式如下:
- 直接创建完整索引,这样可能比较占用空间
- 创建前缀索引,节省空间,但增加查询扫描次数,且不能使用覆盖索引
- 倒序存储,再创建前缀索引,用于绕过字符串本身前缀的区分度不足缺陷
- 创建hash字段索引,查询性能稳定,有额外存储和计算消耗,跟第三种方式一样不支持范围扫描
实际应用中,根据业务字段的特点选择使用哪种方式 。思考题 维护学生信息数据库,学生登录名的统一格式是”学号@gmail.com”
学号的则是:十五位的数字,其中前三位是所在城市编号、第四到第六位是学校编号、第七位到第十位是入学年份、最后五位是顺序编号 。
系统登录时输入登录名和密码,验证正确后才能继续使用系统 。
只考虑登录验证,怎么设计这个登录名的索引呢?
上期我留给你的问题是,给一个学号字段创建索引,有哪些方法 。
由于学号规则,无论正向反向前缀索引,重复度都较高 。
因为维护的只是一个学校的,因此前面6位(其中,前三位是所在城市编号、第四到第六位是学校编号)固定,邮箱后缀都是@gamil.com,因此可只存入学年份加顺序编号,长度9位 。
在此基础,可用数字型存这9位数字 。比如201100001,只需占4字节 。其实这就是种hash,只是用最简单转换规则:字符串转数字,而刚好我们设定背景,可保证转换结果唯一 。
当然了,一个学校的总人数这种数据量,50年才100万学生,这表肯定是小表 。为了业务简单,直接存原来的字符串 。“优化成本和收益”的思想 。
- 参考
《MySQL 实战 45 讲》
推荐阅读
- Python通过MySQLdb访问操作MySQL数据库
- 喝茶可解油去腻,大麦茶去油解腻还具有养胃暖胃之功效
- 网页端收消息,究竟是推还是拉?
- linux安装mysql启动不起来总结
- 小芥子酒方的功效与作用
- 目前地球及人类面临的危机有哪些 我从资料上还了解地球还面临什么危机
- |在单位选择一个领导,还是多跟几个领导?相信厚黑学没有好下场!
- 莓茶是热性还是凉性,茅岩莓是寒性的吗?[养生茶]
- 太极拳的“气”你大概还不知道吧
- 月球上的三眼女尸真相,三眼女尸被发现时竟然还活着 月球上发现神秘三眼女尸是真的吗