死锁
压测sharding-jdbc 改造一个原先用类似mycat的一个程序放到压测平台后,发现运行一会程序就假死了。于是在本地压测。同样运行一会就假死,然后debug。 发现在获取分布式id的时候在锁的地方进行不下去。然后怀疑是哪里导致了死锁,于是运行jstack -l 如下
发现很多请求的线程都等待在了druid的takelast 方法。进去这个方法看,发现是 一个生产者消费者模式的 获取连接的数组。由于没有可以用的链接了(大于最大的链接数 默认是8个)。所以没有链接了就在这里一直阻塞。为什么没有链接了。及时是很多的请求,那么获取到链接请求的线程也应该往下走,而不是假死。于是继续往下看线程状态。发现开始的几个请求阻塞在了分布式id获取id的地方
进去nextRang查看 发现他会从新获取连接 而不是用当前线程的链接
mybatis-spring包提供的sqlsessionFactory 可以保证当前线程都是同一个session (同一个链接)但是这里 获取分布式id的时候是重新获取连接。没有用sqlsessionFactory 来管理。sqlsessionFactory 具体怎么分析https://blog.youkuaiyun.com/z530065424/article/details/82864328
由于我在设置druid数据源的时候没有传入最大的链接数,和最大等待时间。 因此默认链接最多只有8个。且获取连接时 没有等待期限。在这两个前提下。多个请求进来 将连接数用满。一个请求在去获取链接来查询分布式id 的时候就拿不到链接从而阻塞了。而他又 获取了锁。导致他拿着锁,阻塞在了这里。当其他请求进行到这里获取分布式id的时候就会阻塞在这个锁上。然后执行不下去。那么该请求不会释放自己占有的链接。当多个线程阻塞在这,那么连接就耗尽了,那么拿着分布式id 锁的 线程 得不到链接 就不放锁。从而导致一个典型的死锁。导致数据源链接泄露。程序假死。
开始出现这个问题的时候不知道druid不传 最大等待时间会一直等待。因此没有找出上面的死锁原因。于是乎拿mycat测试。看mycat会不会死锁。压测后发现不死锁。于是看他的连接信息发现,他最大链接是80个最大等待时间5秒。把最大等待时间去掉。最大链接数减小。在压测。发现程序也是一样的假死了。因此回到sharding-jdbc 把数据源的连接数调大,等待时间加上。程序不会死锁了。因为 拿不到链接的时间有限,超过时间就放弃了。打破了死锁。