sql线程池
‘壹’ 如何设置sql server的最大工作线程
使用 max worker threads 选项可以配置可用于 Microsoft SQL Server 进程的工作线程数。SQL Server 使用 Microsoft Windows 2000 和 Windows Server 2003 操作系统的本机线程服务,以便使一个或多个线程支持 SQL Server 同时支持的每一个网络,另一个线程处理数据库检查点,而线程池则处理所有用户。
https://msdn.microsoft.com/zh-cn/library/ms190219(SQL.90).aspx
‘贰’ 想问下Mysql和MSSQL在多线程下写入或读取数据是否起冲突
楼上的解释不太对。多线程虽然是最终单cpu执行,但是cpu执行并不是1 次处理完1个线程才到第2个的,多线程处理时候,cpu把各线程再分成片段,处理完1线程第一片段就到线程2第1片段,如此循环。
至于楼主问题,冲突可能性是存在的,数据库一般是并发只读,如果有写入,默认锁表或者锁行。此时排他写操作。完毕以后解锁。如果写进程不幸锁死或者过长,会导致其他线程无法写入。因此,冲突发生的概率其实和写过程复杂度相关,和并发写请求多少相关,如果后台100个线程写入的话,冲突几率还是有的,如果调试能监控到拒绝写入错误,建议单进程负责写入,其他工作线程提交请求至写入线程轮候输入。解决写入冲突问题。
‘叁’ mysql数据库每次查询是一条线程吗
MySQL的查询使用的是线程池。当有大量请求并发访问时,一定伴随着资源的不断创建和释放,导致资源利用率低,降低了服务质量。线程池技术,预先会创建一定数量的线程,当有请求达到时,线程池分配一个线程提供服务,请求结束后,该线程又去服务其他请求。 通过这种方式,避免了线程和内存对象的频繁创建和释放,降低了服务端的并发度,减少了上下文切换和资源的竞争,提高资源利用效率。在MySQL早期的版本中,处理连接的方式是One-Connection-Per-Thread,即对于每一个数据库连接,MySQL-Server都会创建一个独立的线程服务,请求结束后,销毁线程。再来一个连接请求,则再创建一个连接,结束后再进行销毁。但是,这种方式在高并发情况下,会导致线程的频繁创建和释放。当然,通过thread-cache,我们可以将线程缓存起来,以供下次使用,避免频繁创建和释放的问题,但是无法解决高连接数的问题。One-Connection-Per-Thread方式随着连接数暴增,导致需要创建同样多的服务线程,高并发线程意味着高的内存消耗,更多的上下文切换(cpu cache命中率降低)以及更多的资源竞争,导致服务出现抖动。相对于One-Thread-Per-Connection方式,一个线程对应一个连接,Thread-Pool实现方式中,线程处理的最小单位是statement(语句),一个线程可以处理多个连接的请求。这样,在保证充分利用硬件资源情况下(合理设置线程池大小),可以避免瞬间连接数暴增导致的服务器抖动。
‘肆’ Oracle数据库和Sql server数据库各有什么优缺点
1.Oracle跨平台,SQL
Server只能运行在Windows上,而Windows能够安装的硬件是有限的,如Sun的Sparc服务器不能安装Windows,一些大型机、小型机也只能装UNIX,在这些高端机器上就只能跑Oracle了,这注定了Oracle就是高端数据库,而SQL
Server呢,中低端。
2.Oracle真正实现了行级锁,SQL
Server也宣称实现了行级锁,但你实际去试,如果不加索引,其实是不行的。
3.Oracle因为有多版本数据的技术,读写操作不会相互等待,虽然SQL
Server
2005学习Oracle增加了snapshot机制,从而也引进了多版本数据(MySQL也有多版本数据机制,不能说一定是学习Oracle),但是实际效果感觉就是2个版本的数据,隔离级别为read
committed时候,读写不再相互等待,但是把隔离设置为Serializable还是会产生读写相互等待。
4.Oracle的事务日志归档相当方便,而SQL
Server要用事务日志备份来实现,而且还要配置自动作业,启动agent服务。
5.Oracle的数据字典丰富,使得DBA容易判断数据库的各种情况,虽然SQL
Server
2005学习了Oracle的数据字典的特点,但从数量及方便程度上还是相差太多。个人感觉这是Oracle最人性化的地方。
6.Oracle的PL/SQL比SQL
Server的T-SQL功能强大很多。
7.Oracle的触发器比SQL
Server的种类多几种。
8.oracle的备份恢复原理相当简单明了,备份就在操作系统上拷贝数据文件好了,恢复呢,再拷贝回来,数据是旧的,不怕,应用重做日志好了。SQLServer呢,虽然原理在本质上还是这些,但操作起来麻烦多了,麻烦到让你体会不到其本质。
9.Oracle数据库启动可以有多个阶段,使得DBA可以在不同的情况下,通过启动到特定的阶段解决一些特殊问题,而SQLServer只要服务一启动,所有数据库就都打开了。
10.SQLServer给人的感觉是简单易用,但是我要说,如果你继续向前走,就会发现SQLServer的体系结构相当复杂(注意我这里是说的复杂),大体还是沿袭的Sybase的体系结构,这种复杂结构,估计很难有根本性的改变,而Oracle呢,时间越长你越会觉得其体系结构严谨,虽然开始会感觉很难。我的一个比喻,SQLServer是傻瓜相机(就是那些一两千的小数码),Oracle是单反相机(40D,5D,D300),如果你是入门者,那用傻瓜相机好了,在各种环境下拍摄,基本都过得去,用单反,光圈、快门都要自己设定,反倒不如傻瓜相机的效果,如果你是高手了,那傻瓜相机就很难得心应手了。
11.Oracle的书籍一般都比较深,随便一说就是一大批,EpertOracle、PracticalOracle8i、Cost-basedOracle,SQLServer呢,恐怕只有那套InsideSQLServer了,虽然SQLServer的书籍数量比Oracle的多的多(特别是在国内),但多数都是stepbystep的入门书。
12.对比SQL*Plus与sqlcmd(或2000的osql,6.5的isql),sqlcmd的功能是太简陋,差得太多了。
13.SQLServer的最大优点就是和Windows结合紧密,易用,但是要注意事情都是两面的,这些优点可能导致其致命的缺点,例如易用,使得搞SQLServer的人可以不求甚解,有时候不求甚解是没问题的,但是有时候不求甚解可能会造成灾难,特别是对搞数据库的人来说。不好意思,本来要说SQLServer的优点呢,最后也成了缺点了。
‘伍’ 啥时候会使用线程池
编者注:java中的线程池是运用场景最多的并发组件,几乎所有需要异步或并发执行任务的程序都可以使用线程池。
在开发过程中,合理地使用线程池能够带来至少以下几个好处。
降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。
提高线程的可管理性:线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用线程池,必须了解其实现原理。
代码解耦:比如生产者消费者模式。
线程池实现原理
当提交一个新任务到线程池时,线程池的处理流程如下:
如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。
如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。
如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤也需要获取全局锁)。
如果创建新线程将使当前运行的线程数超出maximumPoolSize,该任务将被拒绝,并调用相应的拒绝策略来处理(RejectedExecutionHandler.rejectedExecution()方法,线程池默认的饱和策略是AbortPolicy,也就是抛异常)。
ThreadPoolExecutor采取上述步骤的总体设计思路,是为了在执行execute()方法时,尽可能地避免获取全局锁(那将会是一个严重的可伸缩瓶颈)。在ThreadPoolExecutor完成预热之后(当前运行的线程数大于等于corePoolSize),几乎所有的execute()方法调用都是执行步骤2,而步骤2不需要获取全局锁。
线程池任务 拒绝策略包括 抛异常、直接丢弃、丢弃队列中最老的任务、将任务分发给调用线程处理。
线程池的创建:通过ThreadPoolExecutor来创建一个线程池。
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, timeUnit, runnableTaskQueue, handler);
创建一个线程池时需要输入以下几个参数:
corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到线程池的线程数等于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。
maximumPoolSize(线程池最大数量):线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是,如果使用了无界的任务队列这个参数就没什么效果。
keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以,如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率。
TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS)、小时(HOURS)、分钟(MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一毫秒)和纳秒(NANOSECONDS,千分之一微秒)。
runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。可以选择以下几个阻塞队列。
- ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序。
LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
PriorityBlockingQueue:一个具有优先级的无界阻塞队列。
线程的状态
在HotSpot VM线程模型中,Java线程被一对一映射到本地系统线程,Java线程启动时会创建一个本地系统线程;当Java线程终止时,这个本地系统线程也会被回收。操作系统调度所有线程并把它们分配给可用的CPU。
thread运行周期中,有以下6种状态,在 java.lang.Thread.State 中有详细定义和说明:
// Thread类
public enum State {
/**
* 刚创建尚未运行
*/
NEW,
/**
* 可运行状态,该状态表示正在JVM中处于运行状态,不过有可能是在等待其他资源,比如CPU时间片,IO等待
*/
RUNNABLE,
/**
* 阻塞状态表示等待monitor锁(阻塞在等待monitor锁或者在调用Object.wait方法后重新进入synchronized块时阻塞)
*/
BLOCKED,
/**
* 等待状态,发生在调用Object.wait、Thread.join (with no timeout)、LockSupport.park
* 表示当前线程在等待另一个线程执行某种动作,比如Object.notify()、Object.notifyAll(),Thread.join表示等待线程执行完成
*/
WAITING,
/**
* 超时等待,发生在调用Thread.sleep、Object.wait、Thread.join (in timeout)、LockSupport.parkNanos、LockSupport.parkUntil
*/
TIMED_WAITING,
/**
*线程已执行完成,终止状态
*/
TERMINATED;
}
线程池操作
向线程池提交任务,可以使用两个方法向线程池提交任务,分别为execute()和submit()方法。execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功。通过以下代码可知execute()方法输入的任务是一个Runnable类的实例。
threadsPool.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
});
submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,通过future的get()方法来获取返回值,future的get()方法会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务还没有执行完。
Future<Object> future = executor.submit(harReturnValuetask);
try {
Object s = future.get();
} catch (InterruptedException e) {
// 处理中断异常
} catch (ExecutionException e) {
// 处理无法执行任务异常
} finally {
// 关闭线程池
executor.shutdown();
}
合理配置线程池
要想合理配置线程池,必须先分析任务的特点,可以从以下几个角度分析:
任务的性质:CPU密集型任务、IO密集型任务和混合型任务。
任务的优先级:高、中和低。
任务的执行时间:长、中和短。
任务的依赖性:是否依赖其他系统资源,如数据库连接。
性质不同的任务可以用不同规模的线程池分开处理。CPU密集型任务应配置尽可能少的线程,如配置Ncpu+1个线程的线程池。由于IO密集型任务线程并不是一直在执行任务,则应配置多一点线程,如2*Ncpu。混合型的任务,如果可以拆分,将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐量将高于串行执行的吞吐量。如果这两个任务执行时间相差太大,则没必要进行分解。可以通过Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。
优先级不同的任务可以使用优先级队列PriorityBlockingQueue来处理。它可以让优先级高的任务先执行。执行时间不同的任务可以交给不同规模的线程池来处理,或者可以使用优先级队列,让执行时间短的任务先执行。依赖数据库连接池的任务,因为线程提交SQL后需要等待数据库返回结果,等待的时间越长,则CPU空闲时间就越长,那么线程数应该设置得越大,这样才能更好地利用CPU。
线程池中线程数量未达到coreSize时,这些线程处于什么状态?
这些线程处于RUNNING或者WAITING,RUNNING表示线程处于运行当中,WAITING表示线程阻塞等待在阻塞队列上。当一个task submit给线程池时,如果当前线程池线程数量还未达到coreSize时,会创建线程执行task,否则将任务提交给阻塞队列,然后触发线程执行。(从submit内部调用的代码也可以看出来)
ScheledThreadPoolExecutor
ScheledThreadPoolExecutor继承自ThreadPoolExecutor。它主要用来在给定的延迟之后运行任务,或者定期执行任务。
ScheledThreadPoolExecutor的功能与Timer类似,但
ScheledThreadPoolExecutor功能更强大、更灵活。Timer对应的是单个后台线程,而
ScheledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数。
ScheledThreadPoolExecutor继承自ThreadPoolExecutor,
ScheledThreadPoolExecutor和ThreadPoolExecutor的区别是,ThreadPoolExecutor获取任务时是从BlockingQueue中获取的,而
ScheledThreadPoolExecutor是从DelayedWorkQueue中获取的(注意,DelayedWorkQueue是BlockingQueue的实现类)。
ScheledThreadPoolExecutor把待调度的任务(ScheledFutureTask)放到一个DelayQueue中,其中ScheledFutureTask主要包含3个成员变量:
sequenceNumber:任务被添加到ScheledThreadPoolExecutor中的序号;
time:任务将要被执行的具体时间;
period:任务执行的间隔周期。
ScheledThreadPoolExecutor会把待执行的任务放到工作队列DelayQueue中,DelayQueue封装了一个PriorityQueue,PriorityQueue会对队列中的ScheledFutureTask进行排序,具体的排序比较算法实现如下:
ScheledFutureTask在DelayQueue中被保存在一个PriorityQueue(基于数组实现的优先队列,类似于堆排序中的优先队列)中,在往数组中添加/移除元素时,会调用siftDown/siftUp来进行元素的重排序,保证元素的优先级顺序。
static class DelayedWorkQueue extends AbstractQueue<Runnable>
implements BlockingQueue<Runnable> {
private static final int INITIAL_CAPACITY = 16;
private RunnableScheledFuture<?>[] queue =
new RunnableScheledFuture<?>[INITIAL_CAPACITY];
private final ReentrantLock lock = new ReentrantLock();
private int size = 0;
private Thread leader = null;
private final Condition available = lock.newCondition();
}
从DelayQueue获取任务的主要逻辑就在take()方法中,首先获取lock,然后获取queue[0],如果为null则await等待任务的来临,如果非null查看任务是否到期,是的话就执行该任务,否则再次await等待。这里有一个leader变量,用来表示当前进行awaitNanos等待的线程,如果leader非null,表示已经有其他线程在进行awaitNanos等待,自己await等待,否则自己进行awaitNanos等待。
// DelayedWorkQueue
public RunnableScheledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
RunnableScheledFuture<?> first = queue[0];
if (first == null)
available.await();
else {
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return finishPoll(first);
first = null; // don't retain ref while waiting
if (leader != null)
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}
获取到任务之后,就会执行task的run()方法了,即ScheledFutureTask.run():
public void run() {
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
ScheledFutureTask.super.run();
else if (ScheledFutureTask.super.runAndReset()) {
setNextRunTime();
reExecutePeriodic(outerTask);
}
}
推荐阅读:
JMM Java内存模型
happens-before那些事儿
为什么说LockSupport是Java并发的基石?
责任链的2种实现方式,你更pick哪一种
2阅读
‘陆’ SQL Server的线程池,内存池和连接池有什么区别和关系
网上找了写资料:�0�2数据库连接池: �0�2�0�2�0�2�0�2�0�2 �0�2�0�2�0�2�0�2 数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。 �0�2�0�2 �0�2 一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的 性能低下。 数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并讲这些连接组成一个连接池( 简单说:在一个“ 池”里放了好多半成品的数据库联接对象),由应用程序动态地对池中的连接进行申请、使用和释放。对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。 �0�2�0�2 �0�2连接池技术尽可能多地重用了消耗内存地资源,大大节省了内存,提高了服务器地服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。 �0�2�0�2�0�2 1) 最小连接数是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费; �0�2�0�2�0�2 2) 最大连接数是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影响之后的数据库操作。 �0�2线程池�0�2SQL Server 维护一个操作系统线程池,以便执行从客户端送达的成批 SQL 语句。在 Microsoft Windows NT�0�3 上,如果服务器的 lightweight pooling 配置选项设为 1,则 SQL Server 不再维护线程,转而维护一个纤程池;纤程使用比线程更少的资源。使用线程池或纤程池,可以使 SQL Server 在同时执行多个 SQL 语句时优化处理时间分配。该池中的线程或纤程共同作为工作线程。 �0�2工作线程数是由服务器配置选项 max worker threads 控制的。默认值为 255 而且几乎不需要更改。 当从客户端收到一批 Transact-SQL 语句时,如果现有的工作线程空闲,就会分配它来执行这个批处理。如果没有空闲的现有工作线程而且工作线程数少于 max worker threads,那么就会分配一个新的工作线程。如果没有空闲的工作线程而且已经达到 max worker threads,那么新的批处理就要一直等到现有工作线程完成其当前批处理任务而空闲为止。当工作线程数达到 max worker threads 时,SQL Server 显示如下消息:工作线程限制 255 已经达到。 �0�2对所有工作线程进行分配,并不意味着 SQL Server 的性能会降低。通常,新的批处理等待空闲线程只需要很短的时间。分配更多的线程可能会降低性能,因为增加的工作需要在线程之间协调资源。很多在生产状态运行的 SQL Server 系统都会达到这样的状态,而且以非常高的性能级别运行。 �0�2内存池内存池的思想通过这个池字表露无疑,应用程序可以通过系统的内存分配调用预先一次性申请适当大小的内存作为一个内存池,之后应用程序自己对内存的分配和释放则可以通过这个内存池来完成。
‘柒’ mysql数据库线程池问题,为什么总是报错Cannot create PoolableConnectionFactory
一般这种情况可能的原因有这几种:
1. 可能连接超过mysql设置的上限(你的应该没超)
2. 程序问题,建立了连接不关闭(这个有可能,看看你的session)
3. 在没有使用连接池的情况下,每次都建立一个新的连接到数据库(即使每次操作完毕都及时准确的close了),但是由于可能建立到数据库连接的频率很高(比如在for循环里),那么会迅速建立大量的tcp连接到mysql的指定端口,OS在关闭tcp连接是有一定的延迟的,也是有一定数量限制的,所以就会出现无法连接的情况(connection refused)。
-------------------------------------
我个人感觉设置最大连接数可能用处不大,因为默认的是100,你说刚10个就出问题~所以我觉得可能还是和系统有关系,因为你说刷新的不频繁就不会报错~
不过你到可以先试试看~万一能行更好不是~
还有啊,你可以找别的机器试验下,在别人的电脑上做下测试~