At 3MB average, FILESTREAM is the right side of the debate: the research behind the feature puts the crossover near 1MB, below which in table varbinary wins on access cost and above which NTFS streaming wins, with your documents comfortably in streaming territory. Whether it delivers depends on configuration people skip:
The volume: give the FILESTREAM filegroup its own volume away from data and log files, formatted with a 64KB NTFS cluster size for large file efficiency, with 8.3 short name generation disabled via fsutil since millions of files in FILESTREAM's directory structure pay a real tax generating legacy names. Exclude the container directory from antivirus on access scanning, the classic silent performance killer for FILESTREAM, letting scheduled scans cover it instead if policy insists.
The access path: the performance promised by FILESTREAM arrives through the streaming API, SqlFileStream in application code, which hands the app a Win32 handle to stream the document directly. Reading the same column through ordinary T-SQL SELECT pulls the bytes through SQL Server's memory anyway, keeping the storage benefits while surrendering much of the speed. Two million documents served to an application justify the SqlFileStream plumbing, worth stating in the design so nobody quietly ships the T-SQL path.
The operational fine print: backups include the FILESTREAM data and grow accordingly, plan the windows for it. Transactions covering FILESTREAM writes are heavier than row writes, batch document loads accordingly. And log shipping and mirroring carry FILESTREAM while Availability Groups do too on modern versions, but verify against your HA choice before committing, the feature's HA story has version teeth.