mysql字符默认不区分大小写?

最近发现短链接服务在生成短链接时经常会发生碰撞,刚开始单纯地以为是生成算法有问题,毕竟就算是4位的短链接也有62^4个链接可能(26个字母的大小写+10个数字),如果生成算法没问题的话发生碰撞的概率应该是较小的。

检查了一遍代码,生成算法也确实有一些问题,但都不是主要问题。今天随手在mysql里select了一下,发现查询的时候居然不区分大小写!那么其实4位的短链接就只有36^4个链接可能(160多万而已)!

查阅了mysql相关文档,大致了解了mysql字符集及校对规则的选取方式:

  1. 如果指定了字符集和校对规则,那么采用指定的字符集和校对规则。
  2. 如果指定了字符集但没有指定校对规则,那么采用指定的字符集和该字符集的默认校对规则。
  3. 否则,采用服务器字符集和服务器校对规则。

可以通过show collation命令查询支持的校对规则及字符集默认的校对规则。
mysql数据库也差不多支撑不住目前的需求了,反正要迁移到分布式数据库了就暂时不理这个问题了。但越来越觉得是应该好好完整地将mysql手册看一遍了。

数据库表的轮转(续)

上次提到了数据库表的轮转,大致阐述了问题及初步的解决方法。然而实现的时候还是会遇到些问题。

轮转的时候,需要将旧表A中未完成的任务都迁移到新建的表B中(当然也可以不迁移,而是直接将旧表中的任务处理完为止,但这种方式更为复杂曲折,不予考虑)。迁移的时候有可能会往A中继续插入新的任务,迁移可能会遗漏一些记录;或者从A中取出任务执行完后需要更新任务状态时却发现A中该记录已被迁移到B中了,造成任务状态的不确定性;又或者在A中存在某用户U的开通记录但处理时失败了需要继续重试,但迁移过程中继续插入了一条取消记录,那么就有可能取消成功了但是又由于之前有个失败的开通记录,重试后就导致又被开通了。可能存在的问题还有许多,比如有可能新建表或迁移的时候数据库处理失败了。有没办法将创建新表、迁移记录处理成一个原子操作呢?

首先想到的是通过加锁。具体步骤如下:

  1. 创建新表B
  2. 对旧表A和新表B及轮转记录表C都加写锁(lock tables a write, b write, c write;)
  3. 关闭自动提交(set autocommit=0)
  4. 更新轮转记录C
  5. 将旧表A中需要迁移的记录都select出来,然后批量插入到B中
  6. 将sql语句中记录的tableName变量更改成新表B的表名
  7. 更新轮转记录C,标记本次迁移完成
  8. 释放表锁(unlock tables)及其他一些后续处理

但由于对数据库锁天然的畏惧,最终并没有使用该方式,而是通过自己程序内部控制以模拟达到锁的效果。具体步骤如下:

  1. 创建新表B
  2. 设置onCopy=true(onCopy=true时,其他希望更新任务记录的数据库操作都会阻塞在copyLock.wait()上)
    3~7与上述方式一致
  3. 设置onCopy=false,并调用copyLock.notifyAll()以唤醒所有阻塞的数据库操作

这次重构,由于对mysql的了解还不够深入,花费了许多时间,期间详细查看了mysql官方文档关于事务及锁的描述,获益匪浅

数据库表的轮转

负责的一个项目,在开通时会赠送一批服务(粗略算了下大概有10来个),而开通这些服务有些可能比较耗时,就将这些服务都以后台任务的形式处理了。

最开始开通量少的时候,这些任务都是在一张表里以不同任务名记录的,处理完就修改下任务状态。后来随着开通量的增加,集中在一张表里已经开始会影响性能了,那么很自然地就想到分表,将不同的任务划分到不同的表里,数据量就少了一个数量级。

但是开通量增长得比较快,就算分表发现性能也并不能好多少。最开始想偷懒就打算隔一段时间清理一下已经处理完的任务(反正这些任务处理完后基本都不需要再保留的了),但是mysql如果不optimize表的话,删除的空间是不会被回收利用的,索引也是,所以就得经常停机optimize表。

经常这么折腾,谁都受不了,而且服务也得停会影响线上用户的正常使用也不好。最近就将这些任务表都做成按天轮转,每天轮换一个表,这样每个表的数据就基本在10多万行里,而且空间不够的时候可以直接drop掉一些过期的表(写个脚本定期drop)也不会影响线上服务,维护工作量明显少了很多。